package scriptbuilder.structures;

import java.awt.Color;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Observable;
import java.util.TreeMap;
import scriptbuilder.structures.events.I_ScriptEvent;

/**
 * A script incident. It has an ID number, a name, and a description. It may
 * contain several script events. It also has a color in the GUI window, and is
 * collapsible. Incidents may start as soon as the script begins to run, or they
 * can be offset from the start of the script.
 *
 * @author Greg Eddington <geddingt@calpoly.edu>
 * @author Bryan McGuffin
 * @version 2017/06/29
 */
public class ScriptIncident implements I_XML_Writable
{

    /**
     * The moments in time which have associated events.
     */
    public TreeMap<Integer, TimeSlice> slices;

    /**
     * GUI display color of this slice.
     */
    public Color color;

    /**
     * ID number for this incident.
     */
    public int number;

    /**
     * Name of the incident.
     */
    public String name;

    /**
     * Description of the incident.
     */
    public String description;

    /**
     * Length, in seconds, of the incident.
     */
    public int length = 0;

    /**
     * If true, incident appears minimized.
     */
    public boolean collapsed = false;

    /**
     * Number of seconds between start of simulation and start of this incident.
     */
    public int offset = 0;

    /**
     * Start position of the latest timeslice.
     */
    private int latestStart = 0;

    /**
     * Number of events in this incident.
     */
    public int eventCount = 0;

    SimulationScript script;

    /**
     * Basic constructor.
     *
     * @param number The incident ID number
     * @param name The name of the incident
     * @param description The description of the incident
     * @param script The script object holding this incident
     */
    public ScriptIncident(int number, String name, String description,
            SimulationScript script)
    {
        color = Color.BLACK;
        this.number = number;
        this.name = name;
        this.description = description;
        this.script = script;
        slices = new TreeMap<Integer, TimeSlice>();
    }

    /**
     * Constructor with color parameter.
     *
     * @param color The color to use in the GUI for this event
     * @param number The incident ID number
     * @param name The name of the incident
     * @param description The description of the incident
     * @param script The script object holding this incident
     */
    public ScriptIncident(Color color, int number, String name,
            String description, SimulationScript script)
    {
        this.color = color;
        this.number = number;
        this.name = name;
        this.description = description;
        this.script = script;
        slices = new TreeMap<Integer, TimeSlice>();
    }

    /**
     * Constructor with color and offset parameters.
     *
     * @param color The color to use in the GUI for this event
     * @param number The incident ID number
     * @param name The name of the incident
     * @param description The description of the incident
     * @param script The script object holding this incident
     * @param offset Number of seconds after 00:00:00 that this incident begins
     */
    public ScriptIncident(Color color, int number, String name,
            String description, SimulationScript script,
            int offset)
    {
        this.color = color;
        this.number = number;
        this.name = name;
        this.description = description;
        this.script = script;
        slices = new TreeMap<Integer, TimeSlice>();
        this.setOffset(offset);
    }

    /**
     * Set whether or not the incident is fully visible or in a compacted state.
     *
     * @param collapsed True if the event is compacted
     */
    public void setCollapsed(boolean collapsed)
    {
        this.collapsed = collapsed;
        script.update();
    }

    /**
     * Set the delay time between the start of the script and the start of this
     * incident.
     *
     * @param offset Number of seconds after 00:00:00 that this incident begins
     */
    public void setOffset(int offset)
    {
        this.offset = offset;
        script.update();
    }

    /**
     * Add a new script event to this incident.
     *
     * @param ev The new event
     * @param start Start time of this event, in seconds, from the beginning of
     * the simulation
     */
    public void addNewEvent(I_ScriptEvent ev, int start)
    {
        TimeSlice t = slices.get(start);
        if (t == null)
        {
            //System.out.println("Generating new slice at time " + start);
            t = new TimeSlice(start, this);
            t.addEvent(ev);
            slices.put(start, t);
        }
        else
        {
            t.addEvent(ev);
        }
        eventCount++;

        if (start > latestStart)
        {
            latestStart = start;
            //System.out.println("Latest Start: " + latestStart);
        }
        if (start < offset)
        {
            offset = start;
            //System.out.println("Offset: " + offset);
        }
        updateLength();
    }

    /**
     * Get an array of all valid timeSlices.
     *
     * @return List of timeSlices which are not null
     */
    public ArrayList<TimeSlice> getSlices()
    {
        ArrayList<TimeSlice> arr = new ArrayList<TimeSlice>();
        for (int i = 0; i <= latestStart; i++)
        {
            TimeSlice ts = slices.get(i);
            if (ts != null)
            {
                arr.add(ts);
            }
        }
        return arr;
    }

    public void saveIncidentToFile(File f)
    {
        try
        {
            f.createNewFile();

            BufferedWriter bw = new BufferedWriter(new FileWriter(f));
            bw.write(this.toXML());
            bw.flush();
            bw.close();

        }
        catch (Exception ex)
        {
            System.out.println("ERROR SAVING SCRIPT");
            ex.printStackTrace();
        }
    }

    @Override
    public String toXML()
    {
        String output = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
        output += "<!DOCTYPE TMC_SCRIPT SYSTEM \"script.dtd\">";
        output += openTag(ELEMENT.TMC_SCRIPT.tag + " title=\"" + this.script.title + "\"");

        for (TimeSlice slice : slices.values())
        {
            output += slice.toXML();
        }
        output += closeTag(ELEMENT.TMC_SCRIPT.tag);
        return output;
    }

    @Override
    public String openTag(String s)
    {
        return "<" + s + ">";
    }

    @Override
    public String closeTag(String s)
    {
        return "</" + s + ">\n";
    }

    @Override
    public String emptyTag(String s)
    {
        return "<" + s + "/>\n";
    }

    void insertCadData(long currentEventTime, CadData cad)
    {
        if(number == 100)
        {
            System.out.println("\n\n~~~INCIDENT 100 GOT HERE~~~\n");
        }
        
        
        int time = (int) currentEventTime;

        TimeSlice slice;

        if (slices.get(time) == null)
        {
            slices.put(time, new TimeSlice(time, this));
        }
        slice = slices.get(time);
        slice.cadData = cad;
    }

    /**
     * Update the offset and apparent length of this incident. The offset is the
     * start time of the earliest event in the incident. The length is the time
     * that the latest, longest-lasting event ends, minus the offset.
     */
    private void updateLength()
    {
        int lengthSoFar = 0;
        for (int i = 0; i <= latestStart; i++)
        {
            TimeSlice ts = slices.get(i);
            if (ts != null)
            {
                int reach = ts.getTime() + ts.getEffectiveDuration() - offset;
                if (reach > lengthSoFar)
                {
                    lengthSoFar = reach;
                }
            }
        }
        length = lengthSoFar;
    }

    /**
     * An event which is fired if the focused slice changes.
     */
    public static class SliceChangedEvent
    {

        public TimeSlice slice;

        SliceChangedEvent(TimeSlice slice)
        {
            this.slice = slice;
        }
    }

    /**
     * Update and cause the system to focus on the given timeslice.
     *
     * @param i Index of the slice to focus on
     */
    public void setSliceActive(int i)
    {
        if (this.slices.get(i) != null)
        {
            script.broadcastEvent(new SliceChangedEvent(this.slices.get(i)));
        }
    }

    /**
     * An event which is fired if the focused incident changes.
     */
    public static class IncidentFocusedEvent
    {

        public ScriptIncident incident;

        IncidentFocusedEvent(ScriptIncident i)
        {
            incident = i;
        }
    }

    /**
     * Update and cause the system to focus on this incident.
     */
    public void setIncidentActive()
    {
        script.broadcastEvent(new IncidentFocusedEvent(this));
    }

    /**
     * String representation of this incident.
     *
     * @return String of the form "[Incident number] - [Incident name]"
     */
    @Override
    public String toString()
    {
        return this.number + " - " + this.name;
    }
}
