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

package org.geotools.GpsSim;

import java.util.Date;
import java.text.SimpleDateFormat;

import java.io.File;
import java.awt.geom.Point2D;
import org.geotools.data.FeatureSource;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;

import org.geotools.feature.FeatureIterator;

import org.geotools.referencing.GeodeticCalculator;

import org.geotools.geometry.jts.JTSFactoryFinder;

import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;


import java.net.*;


/**
 *
 * @author BURKAY
 */
public class DataGenerator extends Thread{
    public DataGenerator(){
    }
    private void init() throws Exception{
        trainID = SimulationInterface.trainID.getText();
        ipAddress = SimulationInterface.ipAddress.getText();
        portNumber = Integer.parseInt(SimulationInterface.portNumber.getText());
        String shpFileName = SimulationInterface.shpFile.getText();
        
        String stationsOnRoute_str[] = SimulationInterface.stationsOnRoute.getText().split("\\,");
        String stationsToStop_str[]  = SimulationInterface.stationsToStop.getText().split("\\,");

        int[] stationsOnRoute = new int[stationsOnRoute_str.length];
        int[] stationsToStop  = new int[stationsToStop_str.length];
        int[] intervalLengths = new int[stationsToStop_str.length];

        for(int i=0; i<stationsOnRoute.length; i++){
            stationsOnRoute[i] = Integer.parseInt(stationsOnRoute_str[i]);
            if(stationsOnRoute_str[i].length()==1){
                stationsOnRoute_str[i] = "0"+stationsOnRoute_str[i];
            }
        }
        for(int i=0; i<stationsToStop.length; i++){
            stationsToStop[i] = Integer.parseInt(stationsToStop_str[i]);
            if(stationsToStop_str[i].length()==1){
                stationsToStop_str[i] = "0"+stationsToStop_str[i];
            }
        }
        for(int i=0, j=0, k=0; i<stationsOnRoute.length; i++){
            if(stationsOnRoute[i]==stationsToStop[j]){
                intervalLengths[j]=i-k;
                j++;
                k=i;
            }
        }
        File shpFile = new File(shpFileName);

        FileDataStore store = FileDataStoreFinder.getDataStore(shpFile);
        FeatureSource featureSource = store.getFeatureSource();

        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
        MultiLineString[] multiLineList = new MultiLineString[stationsToStop.length-1];
        simulationPaths = new LineString[stationsToStop.length-1];
        simulationPathsLengths = new double[stationsToStop.length-1];


        for(int g=1, i=1; g<stationsToStop.length; g++){
            LineString[] linesToAppend = new LineString[intervalLengths[g]];
            for(int h=0; h<intervalLengths[g]; h++, i++){
                String searchID = "";
                if(stationsOnRoute[i-1]<stationsOnRoute[i]){
                    searchID = stationsOnRoute_str[i-1] + stationsOnRoute_str[i];
                } else{
                    searchID = stationsOnRoute_str[i] + stationsOnRoute_str[i-1];
                }
                FeatureIterator  iterator = featureSource.getFeatures().features();
                while(iterator.hasNext()){
                    org.opengis.feature.Feature f = iterator.next();
                    String lineID = f.getProperty("id").getValue().toString();
                    if((lineID.compareTo(searchID)) == 0){
                        MultiLineString tmpMLS = (MultiLineString) f.getProperty("the_geom").getValue();
                        linesToAppend[h] = geometryFactory.createLineString(tmpMLS.getCoordinates());
                        if(stationsOnRoute[i-1]>stationsOnRoute[i]){
                            linesToAppend[h] = (LineString) linesToAppend[h].reverse();
                        }
                        break;
                    }
                }
            }
            multiLineList[g-1] = geometryFactory.createMultiLineString(linesToAppend);
        }

        for(int i=0; i<multiLineList.length; i++){
            simulationPaths[i] = geometryFactory.createLineString(multiLineList[i].getCoordinates());
            System.out.println(simulationPaths[i]);
        }
        Coordinate tmp = simulationPaths[0].getCoordinateN(0);
        longitude = tmp.x;
        latitude = tmp.y;

        for(int i=0; i<simulationPaths.length; i++){
            simulationPathsLengths[i] = getLineLength(simulationPaths[i]);
            distanceTotal += simulationPathsLengths[i];
        }
        SimulationInterface.distanceTotalMon.setText(decForm.format(distanceTotal/1000));
        SimulationInterface.distanceNextStatMon.setText(decForm.format(simulationPathsLengths[lastStationPassed]/1000));
    }

    private double getLineLength(LineString lineString){
        double retValue = 0.0;
        GeodeticCalculator tmpGeoCalc = new GeodeticCalculator();

        int numberOfPoints = lineString.getNumPoints();
        Coordinate[] coordinates = lineString.getCoordinates();
        for (int i = 0; i < (numberOfPoints - 1); i++) {
            tmpGeoCalc.setStartingGeographicPoint(coordinates[i].x, coordinates[i].y);
            tmpGeoCalc.setDestinationGeographicPoint(coordinates[i+1].x, coordinates[i+1].y);
            retValue += tmpGeoCalc.getOrthodromicDistance();
        }
        return retValue;
    }

    private String generateData() throws Exception{
        int rand = Integer.valueOf(df_rand.format(new Date()));
        int deviation = rand%2;
        int target = 0;
        maxSpeed = 240;     //query needed
        SimulationInterface.speedLimitMon.setText(Integer.toString(maxSpeed));
        if(autoGenerate){
            target = maxSpeed;
        } else {
            target = Integer.valueOf(SimulationInterface.speedMon.getText());
        }

        if(deviation == 0){
            target += deviation;
        } else{
            target -= deviation;
        }
        acceleration = (int)(java.lang.Math.log1p(java.lang.Math.abs((double) target-speed))*3);
        SimulationInterface.accelerationMon.setText(Integer.toString(acceleration));
        if(deviation == 0){
            acceleration += deviation;
        } else{
            acceleration -= deviation;
        }

        if(speed < target){
            speed += acceleration;
            if(speed > target){
                speed = target;
            }
        } else {
            speed -= acceleration;
            if(speed < target){
                speed = target;
            }
        }
        if(speed<0)
            speed = 0;

        lastDistanceCovered = speed/3600.0;
        distanceTraveled += lastDistanceCovered;
        SimulationInterface.distanceTraMon.setText(decForm.format(distanceTraveled));

        setNewPosition();

        String ret_value = trainID + " | " +
                speed + " | " +
                longitude + " | " +
                latitude + " | " +
                df.format(new Date());
        return ret_value;
    }

    private void setNewPosition() throws Exception{
        if(reachedAStation){
            coords = simulationPaths[lastStationPassed].getCoordinates();
            System.out.println(lastStationPassed);
            if(lastStationPassed<simulationPaths.length-1)
                lastStationPassed++;
            reachedAStation=false;
            coordsIndex = 1;
            secondsLeft = 5;
            System.out.println(distanceTraveled+" "+distanceNextStat+" "+distanceTotal);
            while(secondsLeft>=0){
                SimulationInterface.dataNow.setText("Leaving in " + secondsLeft + " seconds.");
                secondsLeft--;
                sleep(990);
            }
        }
        GeodeticCalculator geodeticCalculator = new GeodeticCalculator();
        while(coordsIndex<coords.length){
            geodeticCalculator.setStartingGeographicPoint(longitude, latitude);
            geodeticCalculator.setDestinationGeographicPoint(coords[coordsIndex].x, coords[coordsIndex].y);
            double dist = geodeticCalculator.getOrthodromicDistance();
            lastDistanceCovered*=1000;      //to meters
            if(dist < lastDistanceCovered){
                longitude = coords[coordsIndex].x;
                latitude = coords[coordsIndex].y;
                lastDistanceCovered -= dist;
                coordsIndex++;
            } else{
                double azimuth = geodeticCalculator.getAzimuth();
                geodeticCalculator.setDirection(azimuth, lastDistanceCovered);
                Point2D point = geodeticCalculator.getDestinationGeographicPoint();
                longitude = point.getX();
                latitude = point.getY();
                break;
            }
        }
        if(coordsIndex == coords.length)
            reachedAStation = true;

    }

    private void sendData(String data) throws Exception{

        DatagramSocket socket = new DatagramSocket();
        
        byte[] buf = new byte[256];
        buf = data.getBytes();
        InetAddress addr = InetAddress.getByName(ipAddress);
        DatagramPacket packet = new DatagramPacket(buf, buf.length, addr, portNumber);
        
        socket.send(packet);
    }

    public void run(){
        df = new SimpleDateFormat("HH.mm.ss.SSS");
        df_rand = new SimpleDateFormat("SSS");
        while(true){
            try{
                if(simMode == 0){   //initialize
                    SimulationInterface.dataNow.setText("Initializing..");
                    init();
                    simMode = 1;
                    
                } else if (simMode == 1){   //generate
                    String simData = generateData();
                    sendData(simData);
                    SimulationInterface.dataNow.setText(simData);
                    SimulationInterface.dataHistory.setText(simData+"\n"+
                            SimulationInterface.dataHistory.getText());

                } else {    //pause
                    SimulationInterface.dataNow.setText("");
                    
                } 
                sleep(990);
            } catch (Exception ex){
                ex.printStackTrace();
            }
        }
    }

    public static LineString[] simulationPaths;
    public static double[] simulationPathsLengths;
    public static Coordinate[] coords;
    public static int coordsIndex=0;
    public static int speed;
    public static String trainID;
    public static String ipAddress="192.168.254.255";
    public static int portNumber=5000;
    public static double longitude;
    public static double latitude;
    public static String clock;
    public static int simMode = 2;
    SimpleDateFormat df;
    SimpleDateFormat df_rand;
    public static boolean autoGenerate = true;
    public static int acceleration;
    public static int maxSpeed;
    public static double distanceTraveled=0;
    public static double distanceNextStat=0;
    public static double distanceTotal=0;
    java.text.DecimalFormat decForm = new java.text.DecimalFormat("####.##");
    public static int lastStationPassed = 0;
    public static double lastDistanceCovered=0;
    public static boolean reachedAStation=true;
    public static int secondsLeft;
    

}
