package server;

import com.jme3.system.AppSettings;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import visualization.GUI;

public class Server extends Thread {

    public static final String START = "START";
    public static final String OK = "OK";
    public static final String ERR = "ERR";
    public static final String END = "END";
    ServerSocket dataServer;
    ArrayList<Integer> usedIds = null;
    protected static GUI animation;
    private String[] devices = new String[1];
    private boolean end = false;
    private Server.DataThread game = null;


    public static void main(String[] args) throws IOException {
        
        Logger.getLogger("com.jme3").setLevel(Level.SEVERE);
        Server s = new Server();
        s.start();
        AppSettings settings = new AppSettings(true);
        animation = new GUI(s);
        settings.setWidth(1024);
        settings.setHeight(640);
        animation.setSettings(settings);
        animation.setShowSettings(false);
        animation.start();
        
        
    }

    
    public void stopReading(){
        if(game != null){
            try {
                game.closeSocket();
            } catch (IOException ex) {
                Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);    
            }
        } else{
            System.exit(0);
        }
        end = true;
        
    }
    
    
    public Server() throws IOException {
        dataServer = new ServerSocket(1234);
    }

    @Override
    public void run() {
            try {
                System.out.println("Waiting for incoming connections.");
                Socket client = dataServer.accept();
                client.setSoTimeout(1000);
                System.out.println("Connection accepted from: "
                        + client.getInetAddress());

                game = new Server.DataThread(generateId(), client);
                game.start();
            } catch (IOException ex) {
                Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
            }
            while(!end){
                
            }
    }

    public int generateId() {
        Integer new_id = new Integer(0);
        if (usedIds == null) {
            usedIds = new ArrayList<Integer>();
            usedIds.add(new_id);
        } else {
            new_id = usedIds.get(usedIds.size() - 1);
            new_id++;
            usedIds.add(new_id);
        }
        return new_id;
    }

    public class DataThread extends Thread {

        private Socket client;
        private DataOutputStream output;
        private DataInputStream input;
        private ArrayList<String> data;
        @SuppressWarnings("unused")
        private int fields;

        public DataThread(int id, Socket client) {
            super(("Client" + Integer.toString(id)));

            this.client = client;
            data = new ArrayList<String>();
            try {
                output = new DataOutputStream(client.getOutputStream());
                input = new DataInputStream(client.getInputStream());
            } catch (IOException e) {
                try {
                    client.close();
                } catch (IOException e1) {
                    System.out.println("FATAL ERROR");
                    System.exit(0);
                }
                System.out.println("Connection from" + client.getInetAddress() + " closed");
            }
        }

        public void closeSocket() throws IOException{
            client.close();
        }
        
        //Reads an UTF String avoiding deadlocks
        //If the socket is closed throws a SocketException
        private String readString() throws IOException {
            boolean received = false;
            boolean closed = false;
            String message = null;
            while (!received && !closed) {
                try {
                    message = input.readUTF();
                    received = true;
                } catch (SocketTimeoutException e) {
                    if (client.isClosed()) {
                        closed = true;
                        throw new SocketException();
                    }
                    //e.printStackTrace();
                } catch (EOFException e) {
                    closed = true;
                    throw new SocketException();
                } catch (IOException e) {
                    closed = true;
                    throw new IOException();
                }
            }
            return message;
        }


        @Override
        public void run() {
            String message;
            try {
                message = readString();
                while (!message.equals(START)) {
                    output.writeUTF(ERR);
                    output.writeInt(0);
                    message = readString();
                }

                output.writeUTF(OK);



                System.out.println("-->Ready to receive data");
                while (!end) {
                    String next_val = null;
                    String dev = null;
                    String time = null;
                    String end_line = null;
                    try{
                            dev = readString();
                            time = readString();
                            next_val = readString();
                            end_line = readString();
                    }catch (SocketException e){
                            System.out.println("socket closed from client side");
                            end = true;
                    }

                    if(next_val != null && end_line.equals("\n")){
                        
                        if(devices[0] == null){
                            devices[0] = dev;
                            System.out.println("Device 1 connected: " + dev);
                        }/*else if(!devices[0].equals(dev) && devices[1] == null){
                            devices[1] = dev;
                            System.out.println("Device 2 connected: " + dev);
                        }*/
                        int device_num = -1;
                        if(dev.equals(devices[0])){
                            device_num = 0;
                        /*}else if(dev.equals(devices[1])){
                            device_num = 1;*/
                        }else{
                            System.out.println("Critical ERROR: more than 1 device, app will quit!");
                            System.out.println("Writer closed");
                            System.out.println("Else");
                            System.exit(0);
                        }
                        
                        try {
                            String[] values = next_val.split(",");

                            float[] acc = new float[3];
                            acc[0] = (float)Double.parseDouble(values[0]);
                            acc[1] = (float)Double.parseDouble(values[1]);
                            acc[2] = (float)Double.parseDouble(values[2]);
                            
                            float[] gyro = new float[3];
                            gyro[0] = (float)Double.parseDouble(values[3]);
                            gyro[1] = (float)Double.parseDouble(values[4]);
                            gyro[2] = (float)Double.parseDouble(values[5]);
                            
                            float[] mag = new float[3];
                            mag[0] = (float)Double.parseDouble(values[6]);
                            mag[1] = (float)Double.parseDouble(values[7]);
                            mag[2] = (float)Double.parseDouble(values[8].split("\n")[0]); //delete endline

                            try {
                                animation.addValues(acc, gyro, mag, time, device_num);
                            } catch (InterruptedException ex) {
                                Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                            }

                        } catch (NumberFormatException e) {
                            System.out.println("number format exception");
                        }

                    } else {
                        System.exit(0);
                    }



                }
                System.out.println("shutting down");



            } catch (IOException e) {

                System.out.println("Exception");

            } finally {
                try {

                    System.out.println("closing socket...");
                    client.close();

                } catch (IOException e1) {
                }
            }
        }
    }

    
}
