package gy7sockets;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;

public class NamedaysServer implements Runnable {

    private final int port = 6969;
    private Socket mySocket = null;
    private int id;
    private static int count = 0;
    private static Map<String, List<String>> dayToName, nameToDay;

    public NamedaysServer() {
        id = 0;
    }

    public void startServer() {
        new Thread() {

            @Override
            public void run() {
                while (true) {
                    if (Thread.activeCount() > 100) {
                        System.err.println("thread count reached 100, halting");
                        System.exit(1);
                    }
                    try {
                        sleep(2000);
                    } catch (InterruptedException iex) {
                    }
                }
            }
        }.start();
        new NamedaysServer().listen();
        System.err.println("server crashed");
    }

    private NamedaysServer(Socket s) {
        this.mySocket = s;
        id = ++count;
    }

    private void listen() {
        readNameDays("namedays.txt");
        ServerSocket ss;
        try {
            ss = new ServerSocket(port);
        } catch (IOException ioex) {
            error("server socket cannot be created", ioex);
            return;
        }
        log("server socket created, listening...");
        while (true) {
            Socket s;
            try {
                s = ss.accept();
            } catch (IOException ioex) {
                error("cannot accept connections on server socket", ioex);
                return;
            }
            log("connection accepted from " + s.getInetAddress() + " " + s.getInetAddress().getCanonicalHostName());
            NamedaysServer newThread = new NamedaysServer(s);
            new Thread(newThread).start();
            log("forked, thread count is now " + Thread.activeCount());
        }
    }

    private void error(String msg, IOException ioex) {
        System.err.println("error message from thread " + id + " " + msg + ": " + ioex.getMessage());
    }

    private void log(String msg) {
        System.out.println("log message from thread " + id + " " + msg);
    }

    public void run() {
        log("thread starts");
        Scanner sc;
        PrintWriter pw;
        try {
            sc = new Scanner(mySocket.getInputStream());
            pw = new PrintWriter(mySocket.getOutputStream());
        } catch (IOException ioex) {
            error("streams cannot be opened", ioex);
            log("thread exits");
            return;
        }

        while (sc.hasNextLine()) {
            String req = sc.nextLine();
            log("recieved req: " + req);
            if (req.equals("exit")) {
                break;
            } else {
                log("answering: ");
                if (req.matches("\\d+")) {
                    if (dayToName.containsKey(req)) {
                        for (String string : dayToName.get(req)) {
                            pw.println(string);
                            log(string);
                        }
                    }
                } else {
                    if (nameToDay.containsKey(req)) {
                        for (String string : nameToDay.get(req)) {
                            pw.println(string);
                            log(string);
                        }
                    }
                }
                pw.println(".");
                pw.flush();
                log("answer done");
            }
        }

        try {
            pw.close();
            sc.close();
            log("streams closed");
            mySocket.close();
            log("socket closed");
        } catch (IOException ioex) {
            error("socket or streams cannot be closed", ioex);
            log("thread exits");
            return;
        }
    }

    private void readNameDays(String fileName) {
        try {
            Scanner sc = new Scanner(new FileInputStream(fileName));
            log("namedays file opened");
            dayToName = new TreeMap<String, List<String>>();
            nameToDay = new TreeMap<String, List<String>>();
            while (sc.hasNextLine()) {
                String line = sc.nextLine();
                String[] a = line.split(" ");
                String day = a[0] + a[1];
                String name = a[2];
                if (!dayToName.containsKey(day)) {
                    dayToName.put(day, new ArrayList<String>());
                }
                dayToName.get(day).add(name);
                if (!nameToDay.containsKey(name)) {
                    nameToDay.put(name, new ArrayList<String>());
                }
                nameToDay.get(name).add(day);
            }
            sc.close();
            log("namedays file parsed");
        } catch (IOException ioex) {
            error("", ioex);
        }
    }
}
