package atmsdriver;

import atmsdriver.model.FEPLine;
import atmsdriver.model.Station;
import atmsdriver.model.LoopDetector;
import atmsdriver.model.Station.DIRECTION;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;

/** The NetworkReader loads all static data for the traffic network through
 *  network lookup files. 
 * 
 *  The public method getFEPLines() method can be used to get all the FEPLines 
  within a network. All other methods are private and are used to construct
  the FEPLines.
 
  The "LDSFile" contains Station and FEPLine information.
  ex.
   < Insert file here >
 *  The "loopFile" contains individual LoopDetector information.
 *  ex.
 *   < Insert file here > 
 *
 * @author John A. Torres
 * @version 09/10/2017
 */
public class NetworkLoader {
    // Two network config files
    private final File LDSFile;
    private final File loopFile;
    
    // The list of FEPLines
    private List<FEPLine> lines;
    
    /**
     * Constructor
     * @param LDSFile contains FEPLine and Station static data
     * @param loopFile contains individual LoopDetector static data
     */
    public NetworkLoader(File LDSFile, File loopFile)
    {
        this.LDSFile = LDSFile;
        this.loopFile = loopFile;
        this.lines = new ArrayList<>();
        constructFEPLines();
    }
    
    /**
     * Returns the list of FEPLines in the configured network
    */
    public List<FEPLine> getFEPLines()
    {
        return lines;
    }
    
    // returns a list of the line numbers found in LDSFile
    private ArrayList<Integer> getLineNums(Scanner sc)
    {
        ArrayList<Integer> lineNums = new ArrayList<>();
        
        sc.nextLine(); // skip first line
        
        while(sc.hasNext())
        {
            sc.nextInt(); // skip to line no
            Integer lineNum = sc.nextInt(); // get the line number
            
            /* If the line is new, and has not been added, add it to lineNums */
            if(!lineNums.contains(lineNum))
            {
                lineNums.add(lineNum);
            }
       
            sc.nextLine(); // skip to next line
        }
        
        sc.close();
        
        return lineNums;
    }
    
    // returns a list of station numbers for a given line
    private ArrayList<Integer> getStationNums(Integer lineNum, Scanner sc)
    {
        ArrayList<Integer> stnNums = new ArrayList<>();
        
        sc.nextLine(); // skip first line
        
        while(sc.hasNext())
        {
            Integer stnNum = sc.nextInt();
            Integer theLine = sc.nextInt();
            if(theLine.equals(lineNum))
            {
                stnNums.add(stnNum);
            }
            
            sc.nextLine();
        }
        
        sc.close();
        
        return stnNums;
    }
    
    // returns a list of loop detectors for a given station
    private ArrayList<LoopDetector> getLoops(Integer stnNum, Scanner sc)
    {
        ArrayList<LoopDetector> loops = new ArrayList<>();
        
        sc.nextLine(); // skip first line
        
        while(sc.hasNext())
        {
            sc.next(); // skip FWY
            sc.next(); // skip dir
            sc.next(); // skip postmile
            Integer ldsID = sc.nextInt(); // lds id
            sc.next(); // skip vdsID
            Integer loopID = sc.nextInt(); // loop id
            sc.next(); // skip LOC
            Integer laneNum = sc.nextInt(); // lane number
            String loopLoc = sc.next();
            if(stnNum.equals(ldsID))
            {
                LoopDetector loop = new LoopDetector(loopID, loopLoc, laneNum);
                loops.add(loop);
            }
            sc.nextLine();
        }
        
        sc.close();
        
        return loops;
    }
    
    // method called to actually build the network from config files
    private void constructFEPLines()
    {
        try {
            System.out.println("Building network...");
            
            // Get FEPLine IDs
            ArrayList<Integer> lineNums = getLineNums(new Scanner(LDSFile));
            
            // Create each FEPLine in the network
            for(Integer lineNum : lineNums)
            {
                // Get Station IDs for the current FEPLine
                ArrayList<Integer> stnNums = getStationNums(lineNum,
                        new Scanner(LDSFile));
                
                // Create each Station for the current FEPLine
                ArrayList<Station> stns = new ArrayList<>();
                for(Integer stnNum : stnNums)
                {
                    // Get Loops for the current Station
                    ArrayList<LoopDetector> loops =
                            getLoops(stnNum, new Scanner(loopFile));
                    
                    // Create the Station and add to list for curr FEPLine
                    Station stn =
                            createStation(stnNum, new Scanner(LDSFile), loops);
                    stns.add(stn);
                }
                
                // Now that stations are created, create the actual FEPLine and
                // and to FEPLines list
                FEPLine line = createLine(lineNum, new Scanner(LDSFile), stns);
                lines.add(line);
            }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(NetworkLoader.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    // Creates  line
    private FEPLine createLine(int lineNum, Scanner scLine, ArrayList<Station> stns)
    {
        FEPLine line = null;
        
        scLine.nextLine();
        
        while(scLine.hasNext())
        {
            scLine.nextInt(); // skip ldsID
            int currLineNum = scLine.nextInt(); // linenum
            if(currLineNum == lineNum)
            {
                scLine.nextInt(); // skip drop
                int sch = scLine.nextInt(); // schedule
                int lineinfo = scLine.nextInt(); // lineinfo
                int sysKey = scLine.nextInt(); // sysKey
                int schSeq = scLine.nextInt(); // schSeq
                int globSeq = scLine.nextInt(); // globSeq  
                int count = scLine.nextInt(); // count
                
                line = new FEPLine(lineNum, stns, count, sch, lineinfo, sysKey, globSeq, schSeq);
                
                break;
            }
            
            scLine.nextLine();
        }
        
        scLine.close();
        
        return line;
    }
    
    // Returns the loction given the whole line from the lookup file
    private String getLocation(String line)
    {
        Scanner sc = new Scanner(line);
        sc.nextInt(); // skip lds num
        sc.nextInt(); // skip line num
        int drop = sc.nextInt(); // drop num
        sc.nextInt(); // skip schedule
        sc.nextInt(); // skip lineinfo
        sc.nextInt(); // skp systemkey
        sc.nextInt(); // skip sch_seq
        sc.nextInt(); // skip glob_seq
        sc.nextInt(); // skip count
        sc.nextInt(); // fwy
        DIRECTION dir = DIRECTION.getEnum(sc.next()); // direction
        sc.nextDouble();
        
        /** GRABS FROM CURRENT TO END OF LINE */
        sc.useDelimiter("\\z"); 
        String loc = sc.next().trim();
        sc.close();
        return loc;
    }
    
    // creates a Station
    private Station createStation(int stnNum, Scanner sc, ArrayList<LoopDetector> detectors)
    {
        Station LDS = null;
        
        sc.nextLine();
        
        while(sc.hasNext())
        {        
            String strLine = sc.nextLine();
            Scanner scLine = new Scanner(strLine);
            int ldsID = scLine.nextInt(); // get curr lds id
            if(ldsID == stnNum) // if we are on correct stn
            {
                int lineNum = scLine.nextInt(); // skip line num
                int drop = scLine.nextInt(); // drop num
                scLine.nextInt(); // skip schedule
                scLine.nextInt(); // skip lineinfo
                scLine.nextInt(); // skp systemkey
                scLine.nextInt(); // skip sch_seq
                scLine.nextInt(); // skip glob_seq
                scLine.nextInt(); // skip count
                int fwy = scLine.nextInt(); // fwy
                DIRECTION dir = DIRECTION.getEnum(scLine.next()); // direction
                double postmile = scLine.nextDouble();
                String ldsName = getLocation(strLine); /************* DOESNT GRAB WHOLE???? */////
                LDS = new Station(lineNum, ldsID, drop, ldsName, detectors, fwy, dir, postmile);
                
                break;
            }
            scLine.close();
        }
        
        sc.close();
        
        return LDS;
    }
}
