package server;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TCPServer {
    public static void main(String[] args) throws Exception {
        TCPServer app = new TCPServer();
        app.start();
    }
    private ServerSocket server;
    private final String logPath = "/data/logs/";
    private final long startTime = System.currentTimeMillis();
    private final long startDataTime = 1351742401;

    private final long roundLength = 7200;
    private int round = 0;

    public TCPServer() throws Exception {
        this.server = new ServerSocket(2345, 1, InetAddress.getLocalHost());
        System.out.println("\r\nRunning Server: " + "Host=" + getSocketAddress().getHostAddress() + " Port=" + getPort() + " Hostname=" + getSocketAddress().getHostName());
    }

    void start() throws IOException {
        ExecutorService executor = Executors.newCachedThreadPool();
        while (true) {
            //this will block until a connection is made
            Socket client = this.server.accept();
            System.out.println("\r\nNew connection from " + client.getInetAddress().getHostAddress());
            executor.execute(new ServerThread(client));
        }
    }

    public InetAddress getSocketAddress() {
        return this.server.getInetAddress();
    }

    public int getPort() {
        return this.server.getLocalPort();
    }

    private class ServerThread implements Runnable {
        private Socket client;

        public ServerThread(Socket client) {
            this.client = client;
        }

        @Override
        public void run() {
            try {
                listen();
                this.client.close();
            } catch (IOException | ParseException | ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }

        void listen() throws IOException, ParseException, ExecutionException, InterruptedException {
            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            ExecutorService executor = Executors.newSingleThreadExecutor();
            String data = null;
            List<Future<?>> tasks = new ArrayList<>();
            while((data = in.readLine()) != null) {
                System.out.println("Message from " + client.getInetAddress().getHostAddress() + ": " + data);

                JSONObject obj = (JSONObject) new JSONParser().parse(data);
                long timestamp = Long.parseLong(String.valueOf(obj.get("timestamp")));
                long user_id = Long.parseLong(String.valueOf(obj.get("user_id")));
                JSONArray arr = (JSONArray) obj.get("hashtags");
                String[] hashtags = new String[arr.size()];
                for(int i=0; i<arr.size(); i++)
                    hashtags[i]= String.valueOf(arr.get(i));
                Future<?> task = executor.submit(new Logger(user_id, hashtags, timestamp, client.getInetAddress().getHostAddress()));
                tasks.add(task);
            }
            for(Future task : tasks)
                task.get();
            System.out.println("listening done");
        }
    }

    class Logger implements Runnable {
        private Object lock = new Object();
        private String[] hashtags;
        private long timestamp;
        private String senderAddress;
        private long id;

        Logger(long user_id, String[] hashtags, long timestamp, String senderAddress) {
            this.hashtags = hashtags;
            calculateTimeDiff(timestamp);
            this.senderAddress = senderAddress;
            this.id = user_id;
        }

        @Override
        public void run() {
            log();
        }

        void log() {
            try {
                File file = new File(logPath + "/slogs.txt");
                boolean exists = file.exists();
                if (!exists) {
                    file.createNewFile();
                }
                FileWriter fw = new FileWriter(file, true);
                BufferedWriter bw = new BufferedWriter(fw);
                synchronized (lock) {
                    if (exists)
                        bw.newLine();
                    String hashtagsString = "";
                    for(String s : hashtags)
                        hashtagsString = hashtagsString + (s + " ");

                    bw.write( timestamp + "\t" + id + "\t" + hashtagsString.trim());
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private void calculateTimeDiff(long stamp) {
            //long currentTime = System.currentTimeMillis();
            //long diffCurrentTime = currentTime - startTime;//in milis
            //this.timestamp = startDataTime + diffCurrentTime/1000;// in sec
            long diffStartTime = stamp - startDataTime;
            int inRound = (int)(diffStartTime / roundLength) + 1;
            this.timestamp = startDataTime + roundLength * inRound;
        }
    }
}
