/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mdot;

import CommonFunctions.CommonFunctions;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;
import ruleengine.RuleChecker;
import ruleengine.ruleEngineThread;
import structures.Constants;
import database.Context;
import structures.HashContext;
import structures.RuleSet;
import userdefined.ActionInterface;
import userdefined.ConditionInterface;
import xmlpackage.RuleReader;
import CommonFunctions.debugPrinter;
import applications.ChargePointThread;
import client.TMCWebServiceClient;
import communication.RMCommunicationThread;
import communication.SNCommunicationThread;
import communication.StaticNeighborStateDB;
import communication.StaticSocket;
import communication.UDPRecvThread;
import communication.UDPSendThread;
import edu.wmich.cs.nest.agora.vis.qor.PortRead;
import gps.GPS;
import java.net.DatagramSocket;
import java.util.Enumeration;
import mjua_edit.VoipControl;
import sensors.Sensor;
import serial.SerialComm;
import serial.SerialReadThread;
import sphinx.SphinxControl;
import structures.NeighborStateDB;
import structures.NeighborStateDBValue;
import swingpackage.TreeView2;
import swingtest.JavaFXToSwingTest;
import xmlpackage.ChargePointReader;
import xmlpackage.ChargesReader;
import xmlpackage.NeighborsSuperNodeReader;
import xmlpackage.RoadSignReader;
import javax.comm.*;
/**
 *
 * @author baba
 */
public class Main {

    
    public static Hashtable<String, ConditionInterface> conditionHash = new Hashtable<String, ConditionInterface>();
    public static Hashtable<String, ActionInterface> actionHash = new Hashtable<String, ActionInterface>();
    public static NeighborStateDB neighbors = StaticNeighborStateDB.neighbors;
    public static String meString = "";
    public static String tickTimeString = "";
    public static String portno_receive = "";
    public static String password = "";
    public static String superNodeFile = "";
    public static String carbon = "";
    public static String wallet = "";
    public static String chargepoints = "";
    public static String sphinx = "";
    public static TreeView2 treeView = null;
    public static RuleChecker engine = null;
    public static double feetDistance = 0;
    public static List coreHash = null;
    public static boolean DEBUG = true;
    public static String tmc = "";
    public static String voipURL = "";
    public static String voipCFG = "";
    public static Properties properties = new Properties();

    //have to put in Regional Manager
    public static String roadsignFile = "";

    @SuppressWarnings("static-access")
    public static void main(String[] args) {
        
         try {
            properties.load(new FileInputStream(Constants.PROPERTIES_FILE));
        } catch (IOException e) {
            e.printStackTrace();
        }

            // Quality of Ride related code
	    CommPortIdentifier portId;
	    Enumeration portList;
	     String driverName = "com.sun.comm.Win32Driver";
	       try{
	       	CommDriver commdriver = (CommDriver)Class.forName(driverName).newInstance( );
	       	commdriver.initialize();
	       }
	       catch (Exception e2){
	        	e2.printStackTrace();
	        }

	        portList = CommPortIdentifier.getPortIdentifiers();
	        while (portList.hasMoreElements()) {
	                   portId = (CommPortIdentifier) portList.nextElement();
	                   if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
	                  	 if (portId.getName().equals("COM6")) {
                                     System.out.println("STARTING PORT READ!!!");
	                		              	PortRead reader = new PortRead(portId,portList,1000);
	                		}
	            	}
	         }

        Boolean flag = Boolean.parseBoolean(properties.getProperty(Constants.HAS_RFID));
        
        try{
            if(flag.booleanValue() == true)
            {
                CommonFunctions common = new CommonFunctions();
                common.scanRFID(args);
            }
            else
            {
                initVIS(args, null);
            }
        }catch(Exception e)
        {
            System.out.println("VIS initialization error!");
        }
    }

    
    public static void initVIS(String[] args, String rfid) throws Exception {       
        
        //Locate the properies file
        TMCWebServiceClient wsclient = new TMCWebServiceClient(properties.getProperty(Constants.PROPERTIES_TMC));
        int accessLevel = wsclient.checkAccessOnRFID(properties.getProperty(Constants.PROPERTIES_ME), rfid);
        System.out.println("ACCESS LEVEL FOR BOB: "+accessLevel);
        args = new String[1];
        args[0] = Integer.toString(accessLevel);
       
        RuleSet ruleSet = new RuleSet();

        //Assign variables with values from properties file.
        String fileName = properties.getProperty(Constants.PROPERTIES_RULES_FILES);
        tickTimeString = properties.getProperty(Constants.PROPERTIES_TICK);
        portno_receive = properties.getProperty(Constants.PROPERTIES_MY_PORT_RECEIVE);
        password = properties.getProperty(Constants.PROPERTIES_PASSWORD);
        String portno_send = properties.getProperty(Constants.PROPERTIES_MY_PORT_SEND);
        String sendTime = properties.getProperty(Constants.PROPERTIES_SEND_TICK);
        meString = properties.getProperty(Constants.PROPERTIES_ME);
        superNodeFile = properties.getProperty(Constants.PROPERTIES_SUPERNODES);
        roadsignFile = properties.getProperty(Constants.PROPERTIES_ROADSIGN_FILE);
        String superNode_sendTime = properties.getProperty(Constants.PROPERTIES_SUPERNODE_TICK);
        String rm_sendTime = properties.getProperty(Constants.PROPERTIES_RM_TICK);
        feetDistance = Double.parseDouble(properties.getProperty(Constants.PROPERTIES_SIGN_DISTANCE));
        carbon = properties.getProperty(Constants.PROPERTIES_CARBON);
        wallet = properties.getProperty(Constants.PROPERTIES_WALLET);
        chargepoints = properties.getProperty(Constants.PROPERTIES_CHARGE_POINT);
        sphinx = properties.getProperty(Constants.PROPERTIES_SPHINX);
        String obd_port = properties.getProperty(Constants.PROPERTIES_OBD_PORT);
        String obd_baud = properties.getProperty(Constants.PROPERTIES_OBD_BAUD);
        String gps_port = properties.getProperty(Constants.PROPERTIES_GPS_PORT);
        tmc = properties.getProperty(Constants.PROPERTIES_TMC);
        voipURL = properties.getProperty(Constants.PROPERTIES_VOIP);
        voipCFG = properties.getProperty(Constants.PROPERTIES_VOIP_CONFIG);

        if (properties.getProperty(Constants.PROPERTIES_DEBUG).equals(Constants.PROPERTIES_TRUE)) {
            DEBUG = true;
        }

        //Load list of supernodes from supernode.xml file
        try {
            
            File file = new File(superNodeFile);
            NeighborsSuperNodeReader neighborRead = new NeighborsSuperNodeReader();
            neighborRead.ExtractFromFile(file);
            debugPrinter.printLine("supernodes loaded");


        } catch (Exception ex) {
            debugPrinter.printLine("Supernodes not loaded. Check Location in ApplicationSettings.properties file in the RULE tag");
            return;
        }

        //Load the list of roadsigns in the area from roadsigns xml file.
        try {
            File file = new File(roadsignFile);
            RoadSignReader roadReader = new RoadSignReader();
            roadReader.ExtractFromFile(file);
            debugPrinter.printLine("roads loaded = " + Context.staticRoadSignVector.size());

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        //Load the list of chargepoints in the area from chargepoint xml file.
        try {
            File file = new File(chargepoints);
            ChargePointReader chargeReader = new ChargePointReader();
            chargeReader.ExtractFromFile(file);
            debugPrinter.printLine("chargepoints loaded = " + Context.chargePointVector.size());

        } catch (Exception ex) {
            ex.printStackTrace();
        }


        PopulateContext();


        //Setup the serial communication with OBDii
        //Initialize the sensors that will be read.
        SerialComm scomm = new SerialComm();
        Sensor.initializeSensors();

        if (scomm.setupSerialPort(obd_port, obd_baud) == -1) {
        System.out.println("ERROR INITIALIZING SERIAL PORT....SHUTTING DOWN.");

        } else {
        SerialReadThread serialReader = new SerialReadThread("serialReader");
        serialReader.start();
        }
  
        //Setup TCP for communicating with the supernode.
        try {
        SNCommunicationThread tcp = new SNCommunicationThread(Integer.parseInt(superNode_sendTime));
        tcp.start();
        } catch (Exception ex) {
        ex.printStackTrace();
        }

        //Setup TCP for communicating with the regional manager.
        try {
            RMCommunicationThread tcp = new RMCommunicationThread(Integer.parseInt(rm_sendTime));
        tcp.start();
        } catch (Exception ex) {
        ex.printStackTrace();
        }
  
        //Used for testing WITHOUTH SN and RM
        PopulateNeighbors();

        //Create UDP receive socket for communicating with other vehicles.
        try {
        StaticSocket.setReceiveSocket(new DatagramSocket(Integer.parseInt(portno_receive)));
        UDPRecvThread receive = new UDPRecvThread(StaticSocket.getReceiveSocket());
        receive.start();
        } catch (Exception ex) {
        ex.printStackTrace();
        }

        //Create UDP send socket for communicating with other vehicles.
        try {
        StaticSocket.setSendSocket(new DatagramSocket(Integer.parseInt(portno_send)));
        UDPSendThread send = new UDPSendThread(StaticSocket.getSendSocket(), Integer.parseInt(sendTime));
        send.start();
        } catch (Exception ex) {
        ex.printStackTrace();
        }

       //Set corehash: variables that MUST be sent to each vehicle
        String tmpCore = properties.getProperty(Constants.PROPERTIES_COREHASH);
        SetCoreHash(tmpCore);         

        //Load the rules from the rules xml file.
        try {
            File file = new File(fileName);
            RuleReader read = new RuleReader();
            read.ExtractFromFile(file);
            ruleSet = read.ruleXML;
            debugPrinter.printLine("Rules loaded");

        } catch (Exception ex) {

            debugPrinter.printLine("Rules not loaded. Check Location in ApplicationSettings.properties file in the RULE tag");
        }

        //Populate the chargedVector from the xml file
        try{
            ChargesReader.ExtractFromFile();
            debugPrinter.printLine("Charges loaded");
        }
        catch(Exception ex)
        {
            debugPrinter.printLine("Charges not loaded. Check Location in ApplicationSettings.properties file in the RULE tag");
        }

        //See "Reflect" function below
        try {
            Reflect();
        } catch (Exception ex) {

            debugPrinter.printLine("User Functions not loaded");
        }


        //Create the rule engine
        engine = new RuleChecker(ruleSet);


        //Start the debugger if DEBUG=TRUE
        if (DEBUG) {
            TreeView2.main(args);
        }

        //Start the rule engine thread
        ruleEngineThread ruleThread = new ruleEngineThread(engine, "engineThread", tickTimeString);
        ruleThread.start();

        //Load the GUI
        
        try {
            JavaFXToSwingTest.main(args);
        } catch (Exception ex) {
        }

        //Setup the GPS
        try {
            //GPSClass gps = new GPSClass();
            GPS gps = new GPS(gps_port);
        } catch (Exception ex) {
        }

        try{
            ChargePointThread cpThread = new ChargePointThread();
            cpThread.start();
        }
        catch(Exception ex)
        {
            System.out.println("Error starting ChargePointThread");
        }

        //Turn on VOIP
        VoipControl c = new VoipControl();


        //Turn on Sphinx
        SphinxControl.sphinx.run();
        //SphinxControl s = new SphinxControl();
        //s.sphinx.run();

    }

    //Reflect()
    //Searches Condition/Action Interfaces for all the conditions and actions
    //implemented by the user. Inserts them into the appropriate hashtable.
    public static void Reflect() {
        try {
            conditionHash = new Hashtable<String, ConditionInterface>();
            actionHash = new Hashtable<String, ActionInterface>();
            for (Class clazz : ConditionInterface.class.getClasses()) {
                ConditionInterface con = (ConditionInterface) clazz.newInstance();
                String name = con.getName();
                conditionHash.put(name, con);
            }
            for (Class clazz : ActionInterface.class.getClasses()) {
                ActionInterface act = (ActionInterface) clazz.newInstance();
                String name = act.getName();
                actionHash.put(name, act);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

    public static void SetCoreHash(String tmpCore) {
        String[] core = null;
        core = tmpCore.split(Constants.DELIMITER_CORE);
        coreHash = new ArrayList();
        for (int i = 0; i < core.length; i++) {
            coreHash.add(core[i].toString());
        }

    }

    //Used for testing
    public static void PopulateNeighbors() {
        NeighborStateDBValue some = new NeighborStateDBValue("192.168.10.12", 4445, "BOB");
        neighbors.neighborDatabase.put("BOB", some);
//        NeighborStateDBValue some = new NeighborStateDBValue("10.80.140.150", 4445, "BOB");
//        neighbors.neighborDatabase.put("BOB", some);
         NeighborStateDBValue some2 = new NeighborStateDBValue("192.168.10.30", 4447, "MOBILE");
         neighbors.neighborDatabase.put("MOBILE", some2);


        //NeighborStateDBValue some = new NeighborStateDBValue("10.1.1.3", 4445, "ALICE");
        //neighbors.neighborDatabase.put("ALICE", some);
    }

    //Used for testing
    public static void PopulateContext() {
        HashContext hash1 = new HashContext();
        HashContext hash2 = new HashContext();
        HashContext hash3 = new HashContext();

        String ID1 = "V1";
        String ID2 = "V2";
        String ID3 = "V3";
        String type1 = "0";
        String type2 = "1";
        String type3 = "1";
        String speed2 = "70";

        hash1.setObjectAsString("name", ID1);
        hash1.setObjectAsString("type", "10.0");
        hash1.setObjectAsString(Constants.HASH_LATITUDE, "42.251972");//"41.979524");
        hash1.setObjectAsString(Constants.HASH_LONGITUDE, "-85.641148");
        hash1.setObjectAsString(Constants.HASH_SPEED, "20");
        hash1.setObjectAsString(Constants.HASH_ROADSIGN_ENABLE, "Yes");
        hash1.setObjectAsString(Constants.HASH_HEADING, "S");
        hash1.setObjectAsString(Constants.HASH_BEARING, "180.0");

        hash2.setObjectAsString("name", ID2);
        hash2.setObjectAsString("type", type2);
        hash2.setObjectAsString("speed", speed2);

        hash3.setObjectAsString("name", ID3);
        hash3.setObjectAsString("type", type3);

        hash1.setObjectAsDouble("hash", 0);
        hash2.setObjectAsDouble("hash", 0);
        hash3.setObjectAsDouble("hash", 0);

        hash1.setObjectAsDouble("hash_core", 0);
        hash2.setObjectAsDouble("hash_core", 0);
        hash3.setObjectAsDouble("hash_core", 0);

        String ID4 = "ME";
        String me_type = "1";
        String me_speed = "25";
        String me_overLimit = "0";
        String me_approx = "-1";

        getMe().setObjectAsString("condition", me_type);
        getMe().setObjectAsString("speed", me_speed);
        getMe().setObjectAsString("overSpeedLimit", me_overLimit);
        getMe().setObjectAsString(Constants.HASH_LATITUDE, "42.251952");//"41.979624");
        getMe().setObjectAsString(Constants.HASH_LONGITUDE, "-85.641148");//"-85.5394972");
        getMe().setObjectAsString(Constants.HASH_BEARING, "0.0");
        getMe().setObjectAsString(Constants.HASH_ID, meString);
        getMe().setObjectAsString(Constants.HASH_HEADING, "S");
        //getMe().setObjectAsString(Constants.HASH_HEADING, "N");

        getContext().put(hash1.GetObjectAsString(Constants.HASH_ID), hash1);

    }

    //Synchronized access of this vehicle's data
    public static synchronized HashContext getMe() {
        return Context.me;
    }

    //Synchronized access of this vehicle's context (surrounding vehicles)
    public static synchronized Hashtable<String, HashContext> getContext() {
        return Context.context;
    }

    /*
    public void someFunction() {
    Enumeration enKeys = mdot.Main.getContext().keys();
    while (enKeys.hasMoreElements()) {
    String key = (String) enKeys.nextElement();
    HashContext tmp = mdot.Main.getContext().get(key);
    }
    }
     */
}