/*
 * Scone - The Web Enhancement Framework
 * Copyright (C) 2009 Harald Weinreich, Volkert Buchmann, Frank Wollenweber, Torsten Haß
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 package scone.ras;


import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Hashtable;

import scone.util.ThreadPool;


public class RasServer extends Thread { 
   
    public static String CON_DEDICATED = Connection.CON_DEDICATED;
    public static String CON_SINGLETON = Connection.CON_SINGLETON;
    public static String OPEN_DEDICATED = Connection.OPEN_DEDICATED;
    public static String CLOSE_DEDICATED = Connection.CLOSE_DEDICATED;
   
    Hashtable handlers = new Hashtable();
    Hashtable dedicated = new Hashtable();
    ServerSocket server;
    ThreadPool pool;
    boolean shutdown = false;
    int port = 0;
    int lastId = 0;

    /**
     * Creates a new RAS-Server at the specified port
     * @param port the port
     */
    public RasServer(int port) throws IOException { 
        this.port = port;
        server = new ServerSocket(port); 
        pool = new ThreadPool();
    }
   
    /**
     * Stops the RAS-Server
     */ 
    public synchronized void shutdown() {
        shutdown = true;
        pool.close();
        try {
            server.close();
        } catch (Exception e) {}
    }
   
    // the thread's run() method
    public void run() {
        try {
            String request = null;

            while (!shutdown) { 
                try {
                    // accept new client
                    Socket client = server.accept(); 

                    // System.out.println ("-> Scone: RAS request accepted from " + client.getInetAddress ()); 

                    // find out which handler is necessary
                    // (a DataInputStream reads the first line of the stream)
                    request = (new DataInputStream(client.getInputStream())).readUTF();
                    // System.out.println("-> Scone: RAS request: "+request);
               
                    // is it a con request?
                    if (request.startsWith(CON_SINGLETON)) {
                        // System.out.println("con singleton");
                        RasHandler h = new RasHandler(client, getHandler(request.substring(CON_SINGLETON.length()).trim()), false); 

                        pool.execute(h);
                    } else 
                    // is it a new dedicated request?
                    if (request.startsWith(OPEN_DEDICATED)) {
                        // System.out.println("open dedicated");
                        RasHandler h = getRasHandler(client, request.substring(OPEN_DEDICATED.length()).trim(), lastId++);

                        pool.execute(h);
                    } else
                    if (request.startsWith(CON_DEDICATED)) {
                        // System.out.println("con dedicated");
                        
                        int pos = request.indexOf(" ");
                        int pos2 = request.indexOf(" ", pos + 1);
                        int id = Integer.parseInt(request.substring(pos, pos2).trim());
                        RasHandler h = getRasHandler(client, request.substring(pos2).trim(), id);
                    } else {
                        // or is it illegal?
                        System.err.println("-> Scone: RAS: Illegal request, connection will be closed!");
                        System.err.println("illegal request was: " + request);
                        client.close();
                    }
                } catch (SocketException se) {
                    // System.out.println("-> Scone: RAS Server at port "+String.valueOf(port)+" closed.");
                    if (shutdown) {
                        return;
                    }
                    se.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                    if (shutdown) {
                        return;
                    }
                }
            }      
        } catch (Exception exc) {
            exc.printStackTrace();
        }
      
    }
   
    void addHandler(String className) {
        try {
            handlers.put(className, Class.forName(className).newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    RasHandler addRasHandler(Socket client, String className, int id) {
        RasHandler handler = null;

        try {
            handler = new RasHandler(client, (ConnectionHandler) Class.forName(className).newInstance(), true);
            handler.connection.write("" + id);
            dedicated.put(className + "/" + id, handler);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return handler;
    }
   
    ConnectionHandler getHandler(String className) {
        ConnectionHandler handler = (ConnectionHandler) handlers.get(className);

        if (handler == null) {
            addHandler(className);
            handler = (ConnectionHandler) handlers.get(className);
        }
        return handler;
    }

    RasHandler getRasHandler(Socket client, String className, int id) {
        RasHandler handler = (RasHandler) dedicated.get(className + "/" + id);

        if (handler == null) {
            handler = addRasHandler(client, className, id);
        } else {
            handler.changeClient(client);
        }
        return handler;
    }
   
}


class RasHandler implements Runnable { 
	 
    Connection connection; 
    ConnectionHandler handler;

    public RasHandler(Socket s, ConnectionHandler handler, boolean dedicated) throws IOException { 
        this.connection = new Connection(s, dedicated);
        this.handler = handler;
    }

    public void changeClient(Socket client) {
        try {
            connection.wake(client);
        } catch (Exception e) {
            e.printStackTrace();
        }
      
    }
	 
    public void run() { 
        if (handler != null) {
            try {
                handler.handle(connection);
                connection.close();
            } catch (Exception e) {}
        } else {
            System.out.println("oops, no ConnectionHandler in RasHandler");
        }
    }
}

