package tmcsim.utilities;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This utility program is used to create the highway map file used as part
 * of the configuration files needed by the simulator.
 * It merges info from three input files to create highway map file.
 * Input: 1. VDS file is the district VDS metadata file downloaded from the PeMS
 *      Data Clearinghouse.  It gives identifying info about each VDS.
 *      2. Loop detector (lane) file that we found on the ATMS server.
 *      It lists all the lanes associated with each VDS. 
 *      3. LDS file that has the fep line number for each LDS.
 * Output: 1. The highway map file that lists VDS info and the lanes it governs,
 *      in a format similar to what FEP simulator uses.
 * @author jdalbey, jtorres
 */
public class BuildHighwayFile
{
    final public static String dirpath = "config/vds_data/old_vds_data/";
    final public static String vdsFileName = "d12_vds_meta.txt";
    final public static String loopFileName = "cleaned_chu_atms_loop_data.txt";
    final public static String ldsFileName = "cleaned_chu_atms_lds_data.txt";
    final public static String highwayFile = "highways_complete.txt";
    /** A dictionary of ldsIDs to VDSids */
    Map<String,StationAddr> ldsDict;
    /** A dictionary of all VDSids linked to a given FEP line number */
    Map<String,List<String>> feplines;
    /** A dictionary to lookup VDS data given vdsID */
    Map<String,VehicleDetectionStation> vdsDict;
    /** A list of used VDS ids - FOR DEBUGGING*/
    Set<String> vdsUsed = new HashSet<String>();
    int ignored = 0; // count of ignored VDS ids
    /** IDs Reported missing during lookup */
    Set<String> missingVDS = new HashSet<String>();
    Set<String> missingLDS = new HashSet<String>();
    
    /** Once all the input data has been read and the lookup dictionaries
     * created we can build the highway file.
     */
    public void createHighwayFile()
    {
        try
        {
            PrintWriter hwyWriter = new PrintWriter(new File(dirpath+highwayFile));
            hwyWriter.print(feplines.size()+"\n");
            // for each fep line
            for (String fep_line: feplines.keySet()) 
            {
                hwyWriter.print(fep_line+" 0 ");
                List<String> vdsList = feplines.get(fep_line);
                hwyWriter.print(vdsList.size()+"\n");
                //    output the vds info
                for (String vds: vdsList)
                {
                    VehicleDetectionStation vdsItem = vdsDict.get(vds);
                    if (vdsItem == null)
                    {
                        System.out.println(vds + " not found in vdsDict");
                    }
                    else
                    {
                        // Output the VDS identifying info
                        hwyWriter.print(vdsItem.id + " ");
                        hwyWriter.print(vdsItem.toString() + " ");
                        hwyWriter.print(vdsItem.getLaneList().size() + " ");
                        hwyWriter.print(vdsItem.street+"\n");
                        List<String> laneList = vdsItem.getLaneList();
                        //    output all the lanes
                        for (String lane: laneList)
                        {
                            hwyWriter.print(lane);
                            hwyWriter.print("\n");
                        }
                    }
                }
            }
            hwyWriter.close();
        }catch (FileNotFoundException ex)
        {
            Logger.getLogger(BuildHighwayFile.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex)
        {
            ex.printStackTrace();
            Logger.getLogger(BuildHighwayFile.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
    
    /** Read the loop file to create a dictionary to lookup all the lanes
     * for a given VDS.
     */
    public void createLanelookup()
    {   
        feplines = new HashMap<String,List<String>> ();
            
        try
        {
            Scanner loopScanner = new Scanner(new File(dirpath+loopFileName));
            
            loopScanner.nextLine();  // Skip the column headers

            // Read all the lines in the loop file
            while(loopScanner.hasNextLine())
            {
                String line = loopScanner.nextLine();
                Scanner lineScanner = new Scanner(line);
                
                Integer fwy = lineScanner.nextInt();
                String dir = lineScanner.next();
                String postmile = lineScanner.next();
                String ldsID = lineScanner.next();
                String vdsID = lineScanner.next();
                String loopID = lineScanner.next();
                String shortLoc = lineScanner.next();
                Integer laneNum = lineScanner.nextInt();
                String loop_name = lineScanner.nextLine().trim();
//              String loop_name = line.substring(73).trim(); // grab rest of line
                loop_name = loop_name.replace(" ", "_");
                
                lineScanner.close();
                
                // Combine fields for one lane description
                String laneDesc = loopID + " " + shortLoc + " " + loop_name;
                // Add the lane to the lookup table
                if (vdsDict.containsKey(vdsID))
                {
                    vdsDict.get(vdsID).addLane(laneDesc);
                    vdsUsed.add(vdsID);
                }
                else
                {
                    // If we're missing a VDS id record it
                    // but we're only interested in two lane types.
                    boolean desiredType = shortLoc.equals("ML") || shortLoc.equals("OS");
                    if (desiredType && !missingVDS.contains(vdsID))
                    {
                        System.out.println("createLaneLookup(): vdsID: "+vdsID+
                                " of type "+shortLoc+" not found. "+ String.format("%3s %s %5s",fwy,dir,postmile));
                        missingVDS.add(vdsID);
                    }
                }

                // Also Add this vdsID to the list associated with a fepline
                // lookup which fepline to use for this VDS (using ldsid as 
                //  intermediate key.
                StationAddr sa = ldsDict.get(ldsID);
                if (sa == null)
                {
                    if (!missingLDS.contains(ldsID))
                    {
                        System.out.println("missing ldsID in Station Addr lookup: "+ldsID+" "+ String.format("%3s %s %5s",fwy,dir,postmile));
                        missingLDS.add(ldsID);
                    }
                }
                else
                {
                    String fep_line = sa.line_num;
                    // Fetch the info about this VDS
                    VehicleDetectionStation curr = vdsDict.get(vdsID);
                    // Some VDS were ignored because they weren't Mainline
                    if (curr != null)
                    {
                        // Assign the station address for this VDS
                        curr.setStaAddr(ldsDict.get(ldsID).station_address);

                        if (fep_line == null)
                        {
                            System.out.println("No fepline for ldsID "+ldsID+ " :vdsid "
                                    +vdsID+"  "+fwy+dir+postmile);
                        }
                        else
                        {
                            // Add vdsID to list of feplines
                            if (feplines.containsKey(fep_line))
                            {
                                List<String> vdsList = feplines.get(fep_line);
                                // only add it if it isn't already there
                                if (!vdsList.contains(vdsID))
                                {
                                    feplines.get(fep_line).add(vdsID);
                                }
                            }
                            else // Create a new one
                            {
                                List<String> arraylist1 = new ArrayList<String>();
                                arraylist1.add(vdsID);
                                feplines.put(fep_line, arraylist1);
                            }
                        }
                    }
                }
            }
            
            loopScanner.close();
            
        } catch (FileNotFoundException ex)
        {
            Logger.getLogger(BuildHighwayFile.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }
    /** Create a dictionary of VehicleDetectionStations */
    public void createVDSdict()
    {   
        vdsDict = new HashMap<String,VehicleDetectionStation>(); 
        try
        {
            Scanner vdsScanner = new Scanner(new File(dirpath+vdsFileName));
            
            // Read the tab-delimited vds file
            while(vdsScanner.hasNextLine())
            {
                String line = vdsScanner.nextLine();
                Scanner lineScanner = new Scanner(line).useDelimiter("\t");
                VehicleDetectionStation vds = new VehicleDetectionStation(lineScanner);
                // We only want stations that are mainline, ignore offramps, etc.
                if (vds.type.equals("ML"))
                {
                    vdsDict.put(vds.id, vds);
                }
                else
                {
                    ignored++;
                }
                lineScanner.close();
            }
            vdsScanner.close();
            System.out.println("Ignored "+ignored+ " non-Mainline VDS's");
            
        } catch (FileNotFoundException ex)
        {
            Logger.getLogger(BuildHighwayFile.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }
    /** Read the lds file to create a dictionary to lookup the FEP line number.
     */
    public void createLDSdict()
    {   
        ldsDict = new HashMap<String,StationAddr> ();
            
        try
        {
            Scanner ldsScanner = new Scanner(new File(dirpath+ldsFileName));
            
            ldsScanner.nextLine();  // Skip the column headers

            // Read all the lines in the loop file
            while(ldsScanner.hasNextLine())
            {
                String line = ldsScanner.nextLine();
                Scanner lineScanner = new Scanner(line);
                
                String ldsID = lineScanner.next();
                String line_num = lineScanner.next();
                String stn_address = lineScanner.next();
                // Save the fep linenum and station address for this LDS
                StationAddr sa = new StationAddr(line_num,stn_address);
                lineScanner.close();
                // Add the ldsID to the dict
                ldsDict.put(ldsID, sa);
            }
            ldsScanner.close();
        } catch (FileNotFoundException ex)
        {
            Logger.getLogger(BuildHighwayFile.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }

    /** Display the set of VDS in the dict that didn't get used (weren't found
     * in loop file.
     */
    private void showLeftoverVDS()
    {
        Set<String> vdsItems = vdsDict.keySet(); // get all the VDS ids
        vdsItems.removeAll(vdsUsed); // remove the ones that were used
        System.out.println("Here are the unused VDS ids:");
        // list the remaining items
        for (String vdsitem: vdsItems)
        {
            System.out.print(vdsitem+"  ");
        }
        System.out.println("");            
    }
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        BuildHighwayFile app = new BuildHighwayFile();
        app.createLDSdict();
        app.createVDSdict();
        app.createLanelookup();
        app.createHighwayFile();
        app.showLeftoverVDS();
        System.out.println("Build complete, output file: "+highwayFile);
    }

    /** A record for a fep line_num  and Station Address pair */
    final class StationAddr
    {
        public final String line_num;
        public final String station_address;
        public StationAddr(String line, String addr)
        {
            this.line_num = line;
            this.station_address = addr;
        }
    }
}

