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

import CommonFunctions.debugPrinter;
import hash.hashUtil;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import mdot.Main;
import structures.Constants;
import structures.HashContext;
import structures.NeighborStateDBValue;

/**
 * class Deserialize
 * Deserializes string received from another vehicle and constructs or updates
 * the hashtable for that vehicle.
 */
public class Deserialize {

    @SuppressWarnings("static-access")
    public static void deserializeHashContext(String receivedString, String IPAddress) {

        /*This is how the string looks:
        HASHCODE@@VIN&PASSWORD&PORT_NO&hashtableentry::hashtableentry::hashtableentry::hashtableentry::hashtableentry
        hashtableentry = keyname;scalar(s)or vector(v);type(string (s) or double(d));value

        Example:
        HASHCODE@@JAMIE&PASSWORD&PORT_NO&overSpeedLimit;s;d;0::name;s;s;JAMIE::type;s;d;1::speed;s;d;65::approxLocation;s;d;-1
         */
        receivedString = receivedString.trim();
        String[] subString = null;
        String hash = null;
        String password = null;
        String portno = null;
        String[] entries = null;
        double calculatedHash = -1;

        Hashtable<String, String> newKeys = new Hashtable<String, String>();
        Vector<String> keysToBeRemoved = new Vector<String>();
        HashContext selectedHash = null;

        //Split the string up into it's components:
        //DELIMITER_FOR_HASHCODE: @@
        //DELIMITER_FOR_VIN: &
        //DELIMITER_ENTRY: ::
        //DELIMITER_SINGLE_ENTRY: ;
        try {
            hash = receivedString.split(Constants.DELIMITER_FOR_HASHCODE)[0];
            subString = receivedString.split(Constants.DELIMITER_FOR_HASHCODE)[1].split(Constants.DELIMITER_FOR_VIN);
            password = subString[1];
            portno = subString[2];
            entries = subString[3].split(Constants.DELIMITER_ENTRY);
        } catch (Exception ex) {
            return;
        }

        //Calculate the hash of the received string
        //Compare value to the hashcode sent with the string
        //If they are the same, nothing was lost or tampered with during transmission
        try {
            calculatedHash = hashUtil.getHash(receivedString.split(Constants.DELIMITER_FOR_HASHCODE)[1]);
            if (calculatedHash != Double.parseDouble(hash)) {
                return;
            }
        } catch (Exception ex) {
            return;
        }

        //Entry example> ::overSpeedLimit;s;d;0::
        for (int i = 0; i < entries.length; i++) {

            String[] singleEntry = null;

            //split up each entry into individual segments
            try {
                singleEntry = entries[i].split(Constants.DELIMITER_SINGLE_ENTRY);
            } catch (Exception ex) {
                return;
            }

            String vin = subString[0];

            String key = "";
            String scalar_or_vector = "";
            String type = "";
            String value = "";

            if (singleEntry != null) {
                key = singleEntry[0];
                scalar_or_vector = singleEntry[1];
                type = singleEntry[2];
                value = singleEntry[3];

                //add keys to the newKeys hashtable
                newKeys.put(key, key);
                //newKeys.put(key, value);

                //get the hashtableof the car with this VIN
                HashContext tmpHash = Main.getContext().get(vin);

                //System.out.println("DESERIALIZE...GETVIN.............." + tmpHash.GetObjectAsString(Constants.HASH_ID));

                //ifthis vehicle isn't already in the context, make a hashtable for it
                if (tmpHash == null) {

                    tmpHash = new HashContext();

                    //if it is scalar..
                    if (scalar_or_vector.equals(Constants.SCALAR)) {

                        //if the scalar is of type double
                        if (type.equals(Constants.TYPE_DOUBLE)) {
                            tmpHash.setObjectAsDouble(key, Double.parseDouble(value));

                        //the type is string
                        } else {
                            tmpHash.setObjectAsString(key, value);
                        }

                    //it is a vector of doubles
                    } else {
                        tmpHash.setObjectAsDoubleVector(key, value);
                    }

                    //put this new hashtable into the context
                    Main.getContext().put(vin, tmpHash);
                   
                    //Add the new vehicle to the neighbor state database
                    NeighborStateDBValue tmp = Main.neighbors.neighborDatabase.get(vin);
                    if (tmp == null) {
                        tmp = new NeighborStateDBValue(IPAddress, Integer.parseInt(portno), password);
                        Main.neighbors.neighborDatabase.put(vin, tmp);
                        System.out.println("VIN>>>>>>>>>>>>>>" + vin);

                        
                    }

                //there is already a hashtable for this vechile, so update the values
                } else {
                    selectedHash = tmpHash;
                    //System.out.println("UPDATING:.............." + tmpHash.GetObjectAsString(Constants.HASH_ID));

                    //if it is scalar...
                    if (scalar_or_vector.equals(Constants.SCALAR)) {

                        //scalar double
                        if (type.equals(Constants.TYPE_DOUBLE)) {
                            //if this key doesn't exist, just insert it
                            if (tmpHash.GetObjectAsDouble(key) == -1) {
                                try {
                                    tmpHash.setObjectAsDouble(key, Double.parseDouble(value));
                                } catch (Exception ex) {
                                }
                            } //if the key does exist, remove old and insert new
                            else {
                                //in case , if you want to check if the values are different
                                tmpHash.removeObject(key);
                                tmpHash.setObjectAsDouble(key, Double.parseDouble(value));
                            }

                        //scalar string
                        } else {
                            //if this key doesn't exist, just insert it
                            if (tmpHash.GetObjectAsString(key) == null) {
                                tmpHash.setObjectAsString(key, value);
                            } //if key does exist, remove old and insert new
                            else {
                                //in case , if you want to check if the values are different
                                tmpHash.removeObject(key);
                                tmpHash.setObjectAsString(key, value);
                            }
                        }
                    } //it is vector
                    else {

                        //if this key doesn't exist, just insert it
                        if (tmpHash.GetObjectAsDoubleVector(key) == null) {
                            tmpHash.setObjectAsDoubleVector(key, value);
                        } //if the key does exist, remove old and insert new
                        else {
                            tmpHash.removeObject(key);
                            tmpHash.setObjectAsDoubleVector(key, value);
                        }
                    }

                    
                }
            }

        }
        //Garbage Collection for keys that aren't present anymore
        //Search through all the keys and if one doesn't exsit anymore,remove it
        if (selectedHash != null) {
            Enumeration<String> enKeys = selectedHash.keys();
            while (enKeys.hasMoreElements()) {
                String key = enKeys.nextElement();
                if (!key.equals("result") && newKeys.get(key)==null && !key.equals(Constants.HASH_LOCATION) && !key.equals(Constants.HASH_LOCATION1)) {
                    keysToBeRemoved.add(key);
                }
            }

            enKeys = keysToBeRemoved.elements();
            while (enKeys.hasMoreElements()) {
                String key = enKeys.nextElement();
                selectedHash.removeObject(key);
            }
        }

        //Update the debug window
        if (Main.DEBUG) {
            if (Main.treeView != null) {
                while (true) {
                    if (!Main.treeView.busy) {
                        Main.treeView.UpdateTree();
                        break;
                    } else {
                        debugPrinter.printLine("busy update tree");
                    }
                }
            }
        }
    }
}