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

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
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 CommonFunctions.debugPrinter;
import communication.RMCommunicationThread;
import communication.SNCommunicationThread;
import communication.StaticNeighborStateDB;
import communication.UDPRecvThread;
import communication.UDPSendThread;
import gps.GPS;
import structures.NeighborStateDB;
import structures.NeighborStateDBValue;
import utility.StringUtil;
import xmlpackage.NeighborsSuperNodeReaderNew;
import xmlpackage.RoadSignReaderNew;
import xmlpackage.RuleReaderNew;

/**
 *
 * @author baba
 */
public class Main {

    public static Hashtable conditionHash = new Hashtable();
    public static Hashtable actionHash = new Hashtable();
    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 RuleChecker engine = null;
    public static double feetDistance = 0;
    public static List coreHash = null;
    public static boolean DEBUG = false;
    //have to put in Regional Manager
    public static String roadsignFile = "";

    public static void main(String[] args) {

        RuleSet ruleSet = new RuleSet();

        //Assign variables with values from properties file.
        // TODO - The following should be changed to RMS - Record Store
        String fileName = "C:\\Documents and Settings\\Student\\Desktop\\Research\\example2.xml";
        tickTimeString = "1000";
        portno_receive = "4447";
        password = "MOBILE";
        String portno_send = "4448";
        String sendTime = "1000";
        meString = "MOBILE";

        //superNodeFile = "C:\\Documents and Settings\\Student\\Desktop\\Research\\supernode.xml"; 
        superNodeFile = "C:\\Documents and Settings\\Student\\Desktop\\SuperNode\\SuperNode\\supernode.xml";

        roadsignFile = "C:\\Documents and Settings\\Student\\Desktop\\Research\\roadsigns.xml";
        String superNode_sendTime = "10000";
        String rm_sendTime = "5000";
        feetDistance = Double.parseDouble("30");
        carbon = "C:\\Documents and Settings\\Student\\Desktop\\Research\\carbon.xml";


        //Load list of supernodes from supernode.xml file
        try {
            NeighborsSuperNodeReaderNew myReader = new NeighborsSuperNodeReaderNew("/res/supernode.xml");
            myReader.beginParse();

        } 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 {


            RoadSignReaderNew myReader = new RoadSignReaderNew("/res/roadsigns.xml");
            myReader.beginParse();

            debugPrinter.printLine("roads loaded = " + Context.staticRoadSignVector.size());

        } catch (Exception ex) {

            ex.printStackTrace();
        }


        PopulateContext();

        
//        //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
        PopulateNeighbors();

        //Create UDP receive socket for communicating with other vehicles.
        try {

            UDPRecvThread receive = new UDPRecvThread(Integer.parseInt(portno_receive));
            receive.start();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        //Create UDP send socket for communicating with other vehicles.
        try {

            UDPSendThread send = new UDPSendThread(Integer.parseInt(sendTime), Integer.parseInt(portno_send));
            send.start();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

//        //Set corehash: variables that MUST be sent to each vehicle
        String tmpCore = "name;speed;gps";
        SetCoreHash(tmpCore);



        //Load the rules from the rules xml file.
        try {

            RuleReaderNew myReader = new RuleReaderNew("/res/example2.xml");
            myReader.beginParse();
            ruleSet = myReader.ruleSet;

            java.lang.System.out.println(ruleSet.getRuleSetVector().toString());

        } catch (Exception ex) {

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

          try {
            GPS gps = new GPS();
        } catch (Exception ex) {

            debugPrinter.printLine("GPS initialization error.");
        }

        //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 rule engine thread
        ruleEngineThread ruleThread = new ruleEngineThread(engine, "engineThread", tickTimeString);
        ruleThread.start();


    }

    //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();
            actionHash = new Hashtable();
            for (int i = 0; i < ConditionInterface.innerClasses.length; i++) {
                String innerClassName = "userdefined.ConditionInterface$" + ConditionInterface.innerClasses[i];
                Class clazz = Class.forName(innerClassName);
                ConditionInterface con = (ConditionInterface) clazz.newInstance();
                String name = con.getName();
                conditionHash.put(name, con);
            }

            for (int i = 0; i < ActionInterface.innerClasses.length; i++) {
                String innerClassName = "userdefined.ActionInterface$" + ActionInterface.innerClasses[i];
                Class clazz = Class.forName(innerClassName);
                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 = StringUtil.split(tmpCore, 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("10.1.1.2", 4445, "BOB"); //10.1.1.2
//        neighbors.neighborDatabase.put("BOB", some);

        NeighborStateDBValue some = new NeighborStateDBValue("192.168.10.12", 4445, "BOB"); //10.1.1.2
        neighbors.neighborDatabase.put("BOB", some);
        NeighborStateDBValue some2 = new NeighborStateDBValue("192.168.10.30", 4445, "ALICE"); //10.1.1.2
        neighbors.neighborDatabase.put("ALICE", some2);
    }

    //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", "BOB");
        hash1.setObjectAsString("type", "10.0");
        hash1.setObjectAsString(Constants.HASH_LATITUDE, "42.251972");//"41.979524");
        hash1.setObjectAsString(Constants.HASH_LONGITUDE, "-85.637759");
        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("type", me_type);
        getMe().setObjectAsString("speed", me_speed);
        getMe().setObjectAsString("overSpeedLimit", me_overLimit);
        getMe().setObjectAsString(Constants.HASH_LATITUDE, "42.255079");//"41.979624");
        getMe().setObjectAsString(Constants.HASH_LONGITUDE, "-85.637759");//"-85.5394972");
        getMe().setObjectAsString(Constants.HASH_BEARING, "0.0");
        getMe().setObjectAsString(Constants.HASH_ID, Main.meString);
        getMe().setObjectAsString(Constants.HASH_HEADING, "E");

        //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 getContext() {
        return Context.context;
    }
}
