package atmsdriver.model;

import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * A Station is a simulation of a station in a traffic network.
 *
 * A Station (LDS) contains static meta data about the station, and two dynamic
 * attributes, MLTotVol and OppTotVol.  *
 * A single LDS contains multiple LoopDetectors, which contain data for a single
 * lane on one direction of the freeway. A LDS is specific to a single freeway,
 * direction, and postmile.
 *
 * @author John A. Torres
 * @version 9/10/2017
 */
public class Station implements Comparable {
    /* Static Station meta data */

    final private int lineNum;
    final private int ldsID; // double check
    final private int drop;
    final private String location;
    final private List<LoopDetector> loops;
    final private int freeway;
    final private double postmile;
    final private DIRECTION direction;

    /* Dynamic Station data */
    private int MLTotVol;
    private int OppTotVol;

    /* Constructor */
    public Station(int lineNum, int ldsID, int drop,
            String location, List<LoopDetector> loops, int fwy,
            DIRECTION direction, double postmile) {
        this.lineNum = lineNum;
        this.ldsID = ldsID;
        this.drop = drop;
        this.loops = loops;
        this.location = location;
        this.postmile = postmile;
        this.direction = direction;
        this.freeway = fwy;

        this.MLTotVol = 0;
        this.OppTotVol = 0;
    }

    public int getHighwayNumber()
    {
        return this.freeway;
    }
    
    public DIRECTION getDirection()
    {
        return this.direction;
    }
    
    /**
     * Returns the station metadata in condensed form. This is just a quick
     * script function to make a proper highway metadata configuration file, so
     * that we can read the network faster.
     *
     * @return station metadata
     */
    public String getStationMeta() {
        StringBuilder build = new StringBuilder();
        build.append(Integer.toString(this.ldsID));
        build.append(" ");
        build.append(Integer.toString(this.drop));
        build.append(" ");
        build.append(Integer.toString(this.freeway));
        build.append(" ");
        build.append(this.direction.name);
        build.append(" ");
        build.append(Double.toString(this.postmile));
        build.append(" ");
        build.append(Integer.toString(loops.size()));
        build.append(" ");
        build.append(this.location);
        build.append("\n");
        for (LoopDetector loop : loops) {
            build.append(loop.getLoopMeta());
        }
        return build.toString();
    }

    public double getPostmile() {
        return postmile;
    }

    @Override
    public int compareTo(Object otherStation) {
        // check that Object is of type Station, if not throw exception
        if (!(otherStation instanceof Station)) {
            throw new ClassCastException("A Station object expected.");
        }

        // get difference of values
        double otherStationPostmile = ((Station) otherStation).getPostmile();
        double val = this.postmile - otherStationPostmile;

        // set appropriate comparable return value
        int retval = 0;
        if (val > 0) {
            retval = 1;
        } else if (val < 0) {
            retval = -1;
        }

        return retval;
    }

    private static enum XML_TAGS {

        STATION("Station"),
        LDS_ID("LDS_ID"),
        LINE_NUM("Line_Num"),
        DROP("Drop"),
        LOOPS("Loops"),
        LOCATION("Location"),
        POST_MILE("Post_Mile"),
        DIRECTION("Direction"),
        FREEWAY("Freeway"),
        ML_TOT_VOL("ML_Tot_Vol"),
        OPP_TOT_VOL("Opp_Tot_Vol");

        String tag;

        private XML_TAGS(String n) {
            tag = n;
        }
    }

    public void toXML(Element currElem) {
        Document theDoc = currElem.getOwnerDocument();

        Element stationElement = theDoc.createElement(XML_TAGS.STATION.tag);
        currElem.appendChild(stationElement);

        Element ldsIDElement = theDoc.createElement(XML_TAGS.LDS_ID.tag);
        ldsIDElement.appendChild(theDoc.createTextNode(String.valueOf(this.ldsID)));
        stationElement.appendChild(ldsIDElement);

        Element lineNumElement = theDoc.createElement(XML_TAGS.LINE_NUM.tag);
        lineNumElement.appendChild(theDoc.createTextNode(String.valueOf(this.lineNum)));
        stationElement.appendChild(lineNumElement);

        Element dropElement = theDoc.createElement(XML_TAGS.DROP.tag);
        dropElement.appendChild(theDoc.createTextNode(String.valueOf(this.drop)));
        stationElement.appendChild(dropElement);

        Element locationElement = theDoc.createElement(XML_TAGS.LOCATION.tag);
        locationElement.appendChild(theDoc.createTextNode(this.location));
        stationElement.appendChild(locationElement);

        Element postMileElement = theDoc.createElement(XML_TAGS.POST_MILE.tag);
        postMileElement.appendChild(theDoc.createTextNode(String.valueOf(this.postmile)));
        stationElement.appendChild(postMileElement);

        Element directionElement = theDoc.createElement(XML_TAGS.DIRECTION.tag);
        directionElement.appendChild(theDoc.createTextNode(this.direction.name));
        stationElement.appendChild(directionElement);

        Element freewayElement = theDoc.createElement(XML_TAGS.FREEWAY.tag);
        freewayElement.appendChild(theDoc.createTextNode(String.valueOf(this.freeway)));
        stationElement.appendChild(freewayElement);

        Element mlElement = theDoc.createElement(XML_TAGS.ML_TOT_VOL.tag);
        mlElement.appendChild(theDoc.createTextNode(String.valueOf(this.MLTotVol)));
        stationElement.appendChild(mlElement);

        Element oppElement = theDoc.createElement(XML_TAGS.OPP_TOT_VOL.tag);
        oppElement.appendChild(theDoc.createTextNode(String.valueOf(this.OppTotVol)));
        stationElement.appendChild(oppElement);

        Element loopsElement = theDoc.createElement(XML_TAGS.LOOPS.tag);
        stationElement.appendChild(loopsElement);

        for (LoopDetector loop : loops) {
            loop.toXML(loopsElement);
        }
    }

    /**
     * Enum for freeway direction.
     *
     * @author John A. Torres
     * @version 9/10/2017
     */
    public static enum DIRECTION {

        NORTH("N"),
        SOUTH("S"),
        EAST("E"),
        WEST("W");

        String name;

        DIRECTION(String name) {
            this.name = name;
        }

        /**
         * Returns the direction enum, given a string value.
         *
         * @param name str value for enum
         * @return enum for given str value
         */
        public static DIRECTION getEnum(String name) {
            switch (name) {
                case "S":
                    return SOUTH;
                case "N":
                    return NORTH;
                case "E":
                    return EAST;
                case "W":
                    return WEST;
                default:
                    return null;
            }
        }
    }
}
