namespace edu.neu.ccs.demeterf.http.server{

using edu.neu.ccs.demeterf.http.classes;
using System.IO;
using System.Net;
using System.Net.Sockets;
using edu.neu.ccs.demeterf.lib;
using System.Threading;
using System;

/** Represents the Listening/Dispatch portion of an HTTP Server.  Given a Map of Paths
 *    to Methods, it is responsible for calling methods when given a specific request.
 */
public class ServerThread{
    static void p(String s){ Factory.p(s); }
    /** Set to true in order to get Output for Server/Dispatch descisions. */
    public static void setVerbose(bool v){ Factory.setVerbose(v); }

    private Socket socket;
    private ServerDispatch dispatch;
    private List<DispatchThread> servants = List<DispatchThread>.create();
    private bool single = false;
    private bool done = false;
    private Thread current;

    /** Create a ServerThread... */
    public ServerThread(int port, bool sing, ServerDispatch disp){
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.Bind(new IPEndPoint(IPAddress.Any, port));
        dispatch = disp; single = sing;
        current = new Thread(run);
        current.Start();
    }

    /** Run!! */
    public void run(){
        socket.Listen(10);
        while (!done) {
            try {
                p("Waiting for connection...");
                Socket req = socket.Accept();
                p("Got One from: " + req.RemoteEndPoint);
                DispatchThread t = new DispatchThread(req, dispatch, this);
                addServant(t);
                t.start();
                try{
                    if(single)t.join();
                }catch(Exception ie){ }
            } catch(Exception e) {
                if (!done) {
                    Console.WriteLine(" ServerThread Exception: " + e.Message +
                                      "\n" + e.StackTrace);
                    done = true;
                }
            }
        }
    }
    /** Add a Servant Thread to the List */
    void addServant(DispatchThread t){ lock(this){ servants = servants.push(t); } }
    /** Remove a Servant Thread from the List */
    void removeServant(DispatchThread t){
        lock(this){
            servants = servants.remove(t);
        }
    }
    /** Await the completion of all Servant Threads */
    public void waitServants(){
        lock(this){
            p("Waiting for Servants...");
            while(!servants.isEmpty()){
                try{ current.Join(1000);
                }catch(Exception ie){}
                p("Still Waiting...");
            }
            p("Done");
        }
    }
    /** Kill the Server listening thread, though workers will continue/complete */
    public void shutdown(){
        done = true;
        socket.Close();
        waitServants();
    }

    /** Handles the dispatch of a Request to a Server Method */
    private class DispatchThread{
        Socket sock;
        ServerDispatch dispatch;
        ServerThread parent;
        Thread thread;

        public DispatchThread(Socket s, ServerDispatch disp, ServerThread p) {
            sock = s; dispatch = disp; parent = p;
            thread = new Thread(run);
        }
        public void start(){ thread.Start(); }
        public void join(){ thread.Join(); }
        public void run(){
            p("In Dispatch Thread...");
            HTTPReq req = null;
            try {
                req = HTTPReq.fromSocket(sock);
            }catch(Exception e){
                try{ sock.Close(); }catch(Exception ee){}
                this.parent.removeServant(this);
                throw e;
            }

            try{
                HTTPResp res = dispatch.handle(req, sock);
                res.toSocket(sock);
                sock.Close();
            }catch(Exception e){
                parent.removeServant(this);
                throw e;
            }
            parent.removeServant(this);
        }
    }
}

}