package tmcsim.highwaymodel;

import tmcsim.highwaymodel.LoopDetector.DOTCOLOR;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * A Station (VDS or Vehicle Detector Station) represents a group of lane detectors
 * across all lanes in ONE direction at a particular point on a highway. A station is identified
 * by its highway number and postmile. A station has an associated direction
 * used to establish which direction is Main and which is Opposite. The MLTotVol
 * and OppTotVol for a station can be dynamically updated. A station has other
 * attributes: lineNum, vdsID, drop, and location which are used by the FEP. A
 * station can be compared to other stations by its postmile.
 *
 * @author John A. Torres, jdalbey
 * @version 9/10/2017, 3/22/2019
 */
public final class Station implements Comparable
{
    /* Static Station meta data */
    final public int lineID;
    final public int vdsID; // double check
    final public int drop;
    final public String location;
    final public List<LoopDetector> loops;
    final public int routeNumber;
    final public double postmile;
    final public DIRECTION direction;

    /* Dynamic Station data */
    private int totalVolume;

    /* Constructor */
    public Station(int lineID, int vdsID, int drop,
            String location, List<LoopDetector> loops, int hwy,
            DIRECTION direction, double postmile)
    {
        this.lineID = lineID;
        this.vdsID = vdsID;
        this.drop = drop;
        this.loops = loops;
        this.location = location;
        this.postmile = postmile;
        this.direction = direction;
        this.routeNumber = hwy;

        this.totalVolume = calcTotalVolume();
    }

    /**
     * Calculates the total lane Volume.
     * Reserved for future use.
     * @return total lane volume.
     */
    private int calcTotalVolume()
    {
        int mlTotVol = 0;
        for (LoopDetector loop : loops)
        {
                mlTotVol += loop.vol;
        }
        return mlTotVol;
    }

    /**
     * Compare this Station to another by postmile. Note: This might be better
     * as a Comparator since it checks only one field.
     */
    @Override
    public int compareTo(Object otherStation)
    {
        // check for identity
        if (this == otherStation)
        {
            return 0;
        }
        // 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).postmile;
        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;
    }

    /**
     * See if this station matches the specified attributes.
     * @param dir
     * @param postmile
     * @return true if this station's attributes match the given ones.
     */
    public boolean matches(DIRECTION dir, double postmile)
    {
        double val = this.postmile - postmile;
        return (Math.abs(val) < 0.01) && this.direction.equals(dir);
    }
    /**
     * Determine which lane fields to update based on given direction and update
     * all the loop detectors with the given color.
     *
     * @param direction desired highway direction
     * @param dotColor desired dot color
     */
    public void updateByDirection(DIRECTION direction, DOTCOLOR dotColor)
    {
        // Is this station going in the desired direction?
        if (direction.equals(this.direction))
        {
            outputUpdateMessage(dotColor, direction.toString());

            // Set the values for all lanes at this station
            for (LoopDetector loop : loops)
            {
                // Set loop detector attributes given the desired color
                loop.setAttributes(dotColor);
            }

            this.totalVolume = calcTotalVolume();
        }
    }
    /**
     * Compute the color for the lanes in a given direction.
     * @return DOTCOLOR of this station's traffic flow
     */
    public DOTCOLOR getColor()
    {
        /* For now just use the color of the first lane. 
         * TODO: Average the color in ALL the lanes  */

        String laneDir = "";
        
        // FOR FUTURE USE we will need to examine all detectors for this station
        // and perform an average
        // for (LoopDetector loop : loops)
        {
            // for now, Return color according to loop volume of first lane
            if (loops.get(0).vol == 1)
            {
                return DOTCOLOR.RED;
            }
            if (loops.get(0).vol == 3)
            {
                return DOTCOLOR.YELLOW;
            }
            if (loops.get(0).vol == 0)
            {
                return DOTCOLOR.GREEN;
            }
        }
        
        // Default case for invalid data
        return DOTCOLOR.GREEN;
    }

    /**
     * Output for updateByDirection. Logs the update to the console.
     *
     * @param dotcolor
     * @param OPP_ML
     */
    private void outputUpdateMessage(DOTCOLOR dotcolor, String OPP_ML)
    {
        System.out.printf("Updating %-3.3s %-5.5s %-3.3s lanes\t %-12.12s "
                + "at postmile %-6.6s to %-7.7s\n",
                Integer.toString(this.routeNumber), this.direction.name(),
                OPP_ML, this.location, Double.toString(this.postmile),
                dotcolor.name());
    }

    /**
     * Enum for freeway direction.
     *
     * @author John A. Torres
     * @version 9/10/2017
     */
    public static enum DIRECTION
    {
        NORTH,
        SOUTH,
        EAST,
        WEST;

        // All the first letters of the values, in order.
        private static String allLetters = "NSEW";

        /**
         * Return the first letter of this enum.
         *
         * @return String first letter of this enum.
         */
        public String getLetter()
        {
            return this.toString().substring(0, 1);
        }
        
        public DIRECTION getOpposite()
        {
            switch (this)
            {
                case NORTH:
                    return SOUTH;
                case SOUTH:
                    return NORTH;
                case EAST:
                    return WEST;
                case WEST:
                    return EAST;
            }
            return null;
        }

        /**
         * Returns a direction given its first character.
         *
         * @param letter the first character of a direction
         * @return direction corresponding to letter
         * @pre letter must be one of allLetters
         */
        public static DIRECTION toDirection(String letter)
        {
            if(letter.indexOf(letter.charAt(0)) == -1)
            {
                return null;
            }
            return values()[allLetters.indexOf(letter.charAt(0))];
        }
    }
    
    @Override
    public String toString()
    {
        return Integer.toString(this.vdsID)+this.getColor();
    }
}
