package scriptbuilder.structures;

import java.awt.Color;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import scriptbuilder.structures.ScriptEvent.ScriptEventType;
import scriptbuilder.structures.ScriptIncident.IncidentFocusedEvent;
import scriptbuilder.structures.ScriptIncident.SliceChangedEvent;

/**
 * Representation of the script to be run by the TMC Simulator. Holds a list of
 * incidents, which have start and end times and contain events.
 *
 * @author Greg Eddington <geddingt@calpoly.edu>
 *
 * @author Bryan McGuffin <bmcguffi@calpoly.edu>
 * @version 2017/06/22
 */
public class SimulationScript extends Observable
{

    /**
     * All default options for GUI colorings of incidents.
     */
    public static final Color[] incidentColors =
    {
        Color.BLACK,
        Color.BLUE,
        Color.RED,
        new Color(0x38, 0x5E, 0x0F),
        new Color(128, 0, 128),
        Color.MAGENTA,
        new Color(0x23, 0x6B, 0x8E),
        Color.ORANGE,
        new Color(0x60, 0x33, 0x11),
        Color.GRAY
    };

    /**
     * The incidents displayed by the GUI.
     */
    public List<ScriptIncident> incidents;

    //Somewhere in the code, something assumes that the list of incidents
    //contains exactly 10 items. Until I can find and un-break that, this will do.
    private final int INCIDENT_FILL_COUNT = 10;

    /**
     * Number of incidents currently displayed.
     */
    private int numberOfIncidents;

    /**
     * Script handler for parsing incoming XML files.
     */
    private MyScriptHandler sh;

    //TODO: Pretty much everything in this constructor is dummy data.
    //Replace all of it.
    /**
     * Constructor. Backfill incident list with null objects, to be replaced
     * later.
     */
    public SimulationScript()
    {
        sh = new MyScriptHandler(this);
        incidents = new ArrayList<ScriptIncident>();
        numberOfIncidents = 0;

        // Create the media event
        /*incidents.add(new ScriptIncident(incidentColors[0], 100, "Media",
         "An incident for the media in CAD.", 10800, this));
         numberOfIncidents++;
         // Create the "other" event
         incidents.add(new ScriptIncident(incidentColors[9], 999, "Other",
         "An incident for small-scale events, false events, "
         + "and noise.  All events added to this incident will "
         + "receive a randomly generated incident number.",
         10800, this));
         numberOfIncidents++;*/
        //Backfill with null incidents
        for (int i = numberOfIncidents; i < INCIDENT_FILL_COUNT; i++)
        {
            incidents.add(null);
        }
        /*
         // Add some demo events
         incidents.add(new ScriptIncident(incidentColors[1], 174, "Blueberry Truck",
         "Blueberry truck crashed on the freeway.", 8800, this));
         numberOfIncidents++;
         incidents.add(new ScriptIncident(incidentColors[2], 175, "Construction Crash",
         "Crash at construction site on Red Road.", 6800, this));
         numberOfIncidents++;
         incidents.add(new ScriptIncident(incidentColors[3], 176, "Car Freeway Flip",
         "Car flipped across the lane divider on the freeway.", 7200, this));
         numberOfIncidents++;
         incidents.add(new ScriptIncident(incidentColors[4], 177, "Two Lane Crash",
         "Crash taking two lanes on Tree Road.", 4200, this));
         numberOfIncidents++;
         incidents.add(new ScriptIncident(incidentColors[5], 178, "Stalled Truck",
         "Truck stalled on the freeway.", 2800, this));
         numberOfIncidents++;
         //incidents.add(new ScriptIncident(incidentColors[6], 179, "Tomato Truck Spill",
         //        "Tomato trucked spilt tomatos all over the freeway.", 3200, this));
         //incidents.add(new ScriptIncident(incidentColors[7], 180, "Crash at Intersection",
         //        "Crash at the intersection of Tree Road and Red Road.", 4300, this));
         //incidents.add(new ScriptIncident(incidentColors[8], 181, "Bomb Threat",
         //        "Bomb threat and Road X.", 6000, this));
         incidents.add(null);
         incidents.add(null);
         incidents.add(null);
         Random rng = new Random();
         ScriptEventType[] eventTypes = ScriptEventType.values();
         if (numberOfIncidents > 0)
         {
         for (int i = 0; i < 300; i++)
         {
         int n = rng.nextInt();
         int s = rng.nextInt();
         int e = rng.nextInt();
         if (n < 0)
         {
         n *= -1;
         }
         if (s < 0)
         {
         s *= -1;
         }
         if (e < 0)
         {
         e *= -1;
         }
         incidents.get(n % numberOfIncidents).slices.get(s % (incidents.get(n % numberOfIncidents).slices.size())).addEvent(new ScriptEvent(eventTypes[e % eventTypes.length]));
         }  
         incidents.get(1).setOffset(200);
         incidents.get(2).setOffset(3400);
         incidents.get(3).setOffset(1400);
         incidents.get(4).setOffset(4400);
         incidents.get(5).setOffset(1400);
         //incidents.get(6).setOffset(7600);
         //incidents.get(7).setOffset(2400);
         //incidents.get(8).setOffset(4800); 
         }
         */
    }

    /**
     * Update the script's observers.
     *
     */
    public void update()
    {
        // The script has changed, notify observers
        setChanged();
        notifyObservers(this);
    }

    /**
     * Tell this script's observers that there is a new slice event.
     *
     * @param e the slice focus event
     */
    public void broadcastEvent(SliceChangedEvent e)
    {
        // The slice focus has changed; pass the message
        setChanged();
        notifyObservers(e);
    }

    /**
     * Tell this script's observers that there is a new slice event.
     *
     * @param e the incident focus event
     */
    public void broadcastEvent(IncidentFocusedEvent e)
    {
        // The slice focus has changed; pass the message
        setChanged();
        notifyObservers(e);
    }

    /**
     * Load in an existing script from an XML file.
     *
     * @param f the file containing the script
     */
    public void loadScriptFromFile(File f)
    {
        try
        {

            SAXParserFactory.newInstance().newSAXParser().parse(f, sh);

            Vector<ScriptIncident> inc = sh.getIncidents();

            for (ScriptIncident sci : inc)
            {
                if (numberOfIncidents < INCIDENT_FILL_COUNT)
                {
                    incidents.set(numberOfIncidents++, sci);
                }
            }
        }
        catch (Exception ex)
        {
            System.out.println("ERROR LOADING SCRIPT");
            ex.printStackTrace();
        }
        this.update();
    }
}
