package com.prolixtech.jaminid;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

import com.prolixtech.utils.SingletonLogger;
import com.prolixtech.utils.Suspender;

/**
 * As in most HTTP implementations, the Daemon opens a port, listens for
 * incoming connections, and then spawns the connections to serve that
 * connection. Sometimes this is called the server, we already have plenty of
 * things in the works that will be called servers, so we're going for the what
 * was the original name of the process before Marketting got to it.
 * 
 * We're going for a no frills implementation that handles all the law level
 * stuff and delegates the production of content to what we call a
 * ContentOracle. This is a nice clean design that makes this code reusable.
 * 
 * On the subject of why an HTTP server vs a JSP implementation, we find this to
 * be a better approach. Less messy, makes the whole thing easier to install,
 * more controllable, faster - this is no frills. The code is centralized,
 * almost all of it in the server itself.
 * 
 * @author Constantinos Michael
 * @changes
 *   24.05.06 - Marco Strack added constructor to reflect socket binding arguments
 *              
 * 
 * 
 * TODO
 */
public class Daemon extends Thread {
    private int serverPort;

    private ServerSocket serverSocket;

    private Protocol activeProtocol = Protocol.Instance();

    private ContentOracle contentOracle;

    
    public static final String VERSION = "0.99.1";

    private boolean RUNNING = false;
    
    /**
     * Queue Length
     * 
     */
	private int backLog;

	/**
	 * The InetAddress; useful for opening on particular interfaces
	 */
	private InetAddress adr;

    /* some accessor fields */

    /**
     * returns the server port
     * 
     * @return the server port
     */
    public int getServerPort() {
        return this.serverPort;
    }

    /**
     * 
     * @return the protocol instance
     */
    public Protocol getProtocol() {
        return this.activeProtocol;
    }

    /**
     * 
     * @return the oracle instance
     */
    public ContentOracle getOracle() {
        return contentOracle;
    }


    /**
     * creates a new daemon and opens on specific port
     *
     * @param serverPort
     *            the port to open on
     * @param cOra
     *            the content oracle to use
     */
    public Daemon(int serverPort, ContentOracle cOra) {
        //calling with default values
        this(serverPort,50,null,cOra);
    }
    
    /**
     * creates a new daemon and opens on specific port
     *
     * @param serverPort
     *            the port to open on
     * @param backLog
     *                          maximum queue length. more concurrent connections will be
     *                          rejected. default: 50
     * @param adr
     *                          the address to bind to. null means any address found.
     * @param cOra
     *            the content oracle to use
     */
    public Daemon(int serverPort, int backLog, InetAddress adr, ContentOracle cOra) {
        super();
        this.setName("HTTP Daemon on " + this.serverPort);
        if (cOra == null) {
            this.printlog("USING STANDARD CONTENTORACLE");
            contentOracle = ContentOracle.Instance();
        } else {
            contentOracle = cOra;
        }
        printlog("Web Server Started, Logger Connected. Using: "
                + cOra.getClass());
        this.serverPort = serverPort;
        this.backLog = backLog;
        this.adr = adr;
        try {
            serverSocket = new ServerSocket(serverPort,backLog,adr);
            RUNNING = true;
            super.start();
        } catch (Exception e) {
            SingletonLogger.Instance().severe("A major error has occured: " + e);
            SingletonLogger.Instance().severe("Perhaps I could not bind port "
                    + serverPort + ". Exiting.");

            serverSocket = null;

        }

    }

    /**
     * Handles listening for new connections and launching client threads
     * 
     * 
     */
    public void run() {
        super.run();

        try {

            try {

                while (RUNNING) {
                    Socket socket = serverSocket.accept();
                    try {
                        printlog("Accepted Socket: " + socket);
                        new Connection(socket, this);
                    } catch (IOException e) {
                        socket.close();
                    }
                }

            } finally {
                serverSocket.close();
            }

        } catch (Exception e) {
            this.printlog("A major error has occured\n" + e);
            System.exit(-1);
        }

    }

    public String toString() {
        return ("mserve HTTPD daemon (" + this.getClass() + ") version "
                + VERSION + " running on port " + this.serverPort + ".");
    }

    public static void main(String args[]) {

        Daemon testServer = new Daemon(80, StreamingOracle.Instance());

    }

    public void printlog(String message) {
        SingletonLogger.Instance().fine(message);
    }

    /**
     * 
     * @return true if the server is up
     */
    public boolean isRunning() {
        return this.RUNNING;
    }

    /**
     * tears down server
     * 
     */
    public void tearDown() {
        RUNNING = false;
    }

    /**
     * Tears down server and waits for it to tear down
     * 
     */
    public void tearDownAndWait() {
        this.tearDown();
        while (RUNNING) {
            Suspender.suspendSeconds(1);
        }
        // TODO add error throw on time out
    }

}