package scriptbuilder.structures;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import scriptbuilder.gui.ScriptBuilderGuiConstants;
import scriptbuilder.structures.events.*;
import scriptbuilder.structures.events.I_ScriptEvent;

/**
 * An instance in time in the simulation. Each timeslice has a start time, an X
 * position on the GUI timeline, and a list of ScriptEvents which occur at that
 * time.
 *
 * @author Greg Eddington <geddingt@calpoly.edu>
 * @author Bryan McGuffin
 * @version 2017/06/30
 */
public class TimeSlice implements Comparable, I_XML_Writable
{

    CadData cadData = null;

    /**
     * Reference to the incident which contains this timeslice.
     */
    private ScriptIncident thisIncident;

    /**
     * List of Script Events which begin at this instance.
     */
    public List<I_ScriptEvent> events;
    //Absolute start time of this time slice
    private int seconds;

    /**
     * Constructor.
     *
     * @param seconds Number of seconds from the beginning of the simulation
     * that this timeslice occurs
     * @param inc the incident to which this timeslice belongs.
     */
    public TimeSlice(int seconds, ScriptIncident inc)
    {
        this.seconds = seconds;
        events = new ArrayList<I_ScriptEvent>();
        thisIncident = inc;
    }

    /**
     * Add a new script event to this time slice. Sort events by event type.
     *
     * @param event the ScriptEvent to be added
     */
    public void addEvent(I_ScriptEvent event)
    {
        event.assignTimeSlice(this);
        events.add(event);
        Collections.sort(events);
    }

    /**
     * Get the X position of this timeslice on the GUI timeline. Affected by
     * zoom level.
     *
     * @return Screen distance from the start of the timeline that this
     * timeslice occurs
     */
    public int getX()
    {
        return seconds / ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION
                * ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
    }

    /**
     * Get the start time of this timeSlice.
     *
     * @return the start time of the timeslice in seconds, from the beginning of
     * the simulation
     */
    public int getTime()
    {
        return seconds;
    }

    /**
     * Shift the start position of this timeslice to the right by some amount.
     *
     * @param amnt the number of seconds forward in time to push this slice
     */
    public void shift(int amnt)
    {
        seconds += amnt;
    }

    /**
     * Get the number of seconds that events which start in this timeslice are
     * active.
     *
     * @return The duration of the longest-lasting event in this slice.
     */
    public int getEffectiveDuration()
    {
        int dur = 0;
        for (I_ScriptEvent evt : events)
        {
            // save the largest of current dur or this evt length
            dur = Math.max(dur, evt.getLength());
        }
        return dur;
    }

    /**
     * Get the text to be displayed when hovering over this timeSlice.
     *
     * @param y the y-position of the cursor on the GUI
     * @return The text to be displayed: all events are listed if we're on the
     * main incident line, but only one event is listed if we're hovering over
     * that particular event
     */
    public String getToolTipText(int y)
    {
        int i = (y - ScriptBuilderGuiConstants.SCRIPT_EVENT_ICON_TOP_MARGIN);
        if (i < 0)
        {
            if (i > (-1 * ScriptBuilderGuiConstants.SCRIPT_EVENT_ICON_STEP))
            {
                String s = toString();
                if (s.equals(""))
                {
                    return null;
                }

                return "<html>" + s.replace("\n", "<br/>") + "</html>";
            }
            else
            {
                return null;
            }
        }

        i /= ScriptBuilderGuiConstants.SCRIPT_EVENT_ICON_STEP;
        if (i < events.size())
        {
            return events.get(i).toString();
        }
        return null;
    }

    /**
     * List all the events attached to this timeslice, with line breaks between.
     *
     * @return A list of the form: event1_type - event1_description event2_type
     * - event2_description ...
     */
    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();

        for (I_ScriptEvent event : events)
        {
            sb.append(event.toString());
            sb.append('\n');
        }

        return sb.toString();
    }

    /**
     * Order the timeslices by start time. Earlier start times come first.
     *
     * @param o the other timeSlice to be compared
     * @return -1, 0 , or 1 depending on if this timeSlice comes before,
     * simultaneously, or after the other
     */
    @Override
    public int compareTo(Object o)
    {
        if (o instanceof TimeSlice)
        {
            TimeSlice other = (TimeSlice) o;
            if (this.getTime() < other.getTime())
            {
                return -1;
            }
            else if (this.getTime() > other.getTime())
            {
                return 1;
            }
        }
        return 0;
    }

    /**
     * Converts the contents of this timeslice to a correctly formatted
     * <ScriptEvent> XML element.
     *
     * @return XML conversion of this timeslice.
     */
    @Override
    public String toXML()
    {
        ArrayList<I_ScriptEvent> eventsCopy = new ArrayList<I_ScriptEvent>();
        ArrayList<I_ScriptEvent> eventsCopy2 = new ArrayList<I_ScriptEvent>();

        for (I_ScriptEvent e : events)
        {
            eventsCopy.add(e);
        }

        String output = XMLWriter.openTag(ELEMENT.SCRIPT_EVENT.tag);
        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
        df.setTimeZone(TimeZone.getTimeZone("GMT"));
        output += XMLWriter.openTag(ELEMENT.TIME_INDEX.tag) + df.format(new Date(seconds * 1000))
                + XMLWriter.closeTag(ELEMENT.TIME_INDEX.tag);

        output += XMLWriter.openTag(ELEMENT.INCIDENT.tag + " LogNum=\"" + thisIncident.number + "\"");
        output += thisIncident.name + XMLWriter.closeTag(ELEMENT.INCIDENT.tag);

        if ((cadData != null && cadData.hasCadData()) || containsCADIncidentEvent())
        {
            output += XMLWriter.openTag(ELEMENT.CAD_DATA.tag);
            if (cadData != null)
            {
                output += cadData.toXML();
            }

            if (containsCADIncidentEvent())
            {
                output += XMLWriter.openTag(ELEMENT.CAD_INCIDENT_EVENT.tag);
                for (I_ScriptEvent ev : eventsCopy)
                {
                    if (ev instanceof I_XML_Writable && isCADIncidentEvent(ev))
                    {
                        I_XML_Writable ex = (I_XML_Writable) ev;
                        output += ex.toXML();
                    }
                    else
                    {
                        eventsCopy2.add(ev);
                    }
                }

                eventsCopy = eventsCopy2;
                output += XMLWriter.closeTag(ELEMENT.CAD_INCIDENT_EVENT.tag);
            }

            output += XMLWriter.closeTag(ELEMENT.CAD_DATA.tag);
        }

        if (cadData != null && cadData.hasGeneralInfo())
        {
            output += XMLWriter.openTag(ELEMENT.GENERAL_INFO.tag);

            output += XMLWriter.simpleTag(cadData.General_Title, ELEMENT.TITLE);

            output += XMLWriter.simpleTag(cadData.General_Text, ELEMENT.TEXT);

            output += XMLWriter.closeTag(ELEMENT.GENERAL_INFO.tag);

        }

        for (I_ScriptEvent ev : eventsCopy)
        {
            if (ev instanceof I_XML_Writable)
            {
                I_XML_Writable ex = (I_XML_Writable) ev;
                output += ex.toXML();
            }
        }
        output += XMLWriter.closeTag(ELEMENT.SCRIPT_EVENT.tag);
        return output;
    }

    private boolean containsCADIncidentEvent()
    {
        for (I_ScriptEvent ev : events)
        {
            if (isCADIncidentEvent(ev))
            {
                return true;
            }
        }
        return false;
    }

    private boolean isCADIncidentEvent(I_ScriptEvent ev)
    {
        return ev instanceof AudioEvent || ev instanceof UnitEvent
                || ev instanceof ParamicsEvent || ev instanceof TowEvent
                || ev instanceof WitnessEvent || ev instanceof CADEvent;
    }

    public void checkEmpty()
    {
        if (this.events.isEmpty())
        {
            this.thisIncident.removeTimeSlice(this.seconds);
        }
    }
}
