package w3c.jigsaw.tests ;

import java.util.* ;
import java.net.* ;
import java.io.* ;

import w3c.util.* ;

public class FastHttpd
{
    private byte[] content  = null ;
    private ClientPool pool = null ;
    private ServerSocket socket = null ;

    private String fname = null ;
    private boolean fromMem = true ;

    public static void main(String[] args)
    {
	try {
	    if(args.length != 4) {
		System.out.println("Usage:") ;
		System.out.println("java w3c.jigsaw.tests.FastHttpd "+
				   "<fname> <#threads> <async/sync> <file/mem>") ;
		return ;
	    }
	    
	    FastHttpd server = new FastHttpd(args[0],
					     Integer.parseInt(args[1]),
					     args[2].charAt(0)=='a',
					     args[3].charAt(0)=='m') ;
	    server.go() ;
	} catch(Exception ex) {
	    ex.printStackTrace() ;
	}
    }

    public FastHttpd(String fname, int numclients, boolean useAsync, boolean fromMem)
	throws Exception
    {
	this.fname = fname ;
	this.fromMem = fromMem ;
	if(fromMem) readFile() ;
	
	pool = new ClientPool(this,numclients,useAsync) ;
	
	socket = new ServerSocket(9999,127) ;
    }

    private void readFile()
	throws IOException
    {
	File inf = new File(fname) ;
	FileInputStream infs = new FileInputStream(inf) ;
	
	if(content == null)
	    content = new byte[(int) inf.length()] ;
	infs.read(content) ;
	infs.close() ;
    }

    public void go()
	throws Exception
    {
	while(true) {
	    Socket csock = socket.accept() ;
	    if(csock != null)
		pool.handleConnection(csock) ;
	}
    }

    public final byte[] getContent()
	throws IOException
    {
	if(!fromMem) readFile() ;
	return content ;
    }
    
}


class ClientPool {
    private static final boolean debug = false;

    private FastHttpd server = null ;
    LRUList readyQueue = null ;
    LRUList busyQueue = null ;

    int numbusy ;

    public ClientPool(FastHttpd server, int size, boolean useAsync)
    {
	this.server = server ;

	if(useAsync) {
	    readyQueue = new AsyncLRUList() ;
	    busyQueue = new AsyncLRUList() ;
	} else {
	    readyQueue = new SyncLRUList() ;
	    busyQueue = new SyncLRUList() ;
	}
	for(int i=0;i<size;i++) 
	    readyQueue.toHead(new Client(server, this)) ;
	numbusy = 0 ;
    }

    public synchronized void handleConnection(Socket socket)
	throws Exception
    {
	Client cl = (Client) readyQueue.getHead() ;
	if(cl == null) {
	    if(debug) System.out.println("Connection rejected");
	    socket.close() ;
	} else {
	    readyQueue.remove(cl) ;
	    busyQueue.toHead(cl) ;
	    numbusy++ ;
	    cl.bind(socket) ;
	}
	if(debug) System.out.println("busy clients: "+numbusy) ;
    }

    public synchronized void freeClient(Client cl)
    {
	if(debug) System.out.println("ClientPool.freeClient()") ;
	busyQueue.remove(cl) ;
	readyQueue.toHead(cl) ;
	numbusy-- ;
	if(debug) System.out.println("busy clients: "+numbusy) ;
    }


}

class Client extends LRUNode implements Runnable {
    private static final boolean debug = false;

    private FastHttpd server = null ;
    private ClientPool pool = null ;
    private DataInputStream input = null ;
    private DataOutputStream output = null ;
    private Socket socket = null ;
    private Thread thread = null ;
    
    public Client(FastHttpd server, ClientPool pool)
    {
	this.server = server ;
	this.pool = pool ;
	this.thread = new Thread(this) ;
	this.thread.start() ;
    }

    public synchronized void bind(Socket s)
    {
	if(debug) 
	    System.out.println("Client.bind()") ;
	this.socket = s ;
	notify() ;
    }

    public void run()
    {
	try {
	    while(true) {
		loopForBinding() ;
		if(socket != null)
		    runConnection() ;
	    }
	} catch(Exception ex) {
	    ex.printStackTrace() ;
	}
    }

    private synchronized void loopForBinding()
    {
	while(socket==null) {
	    try { wait() ; }
	    catch(InterruptedException ex) { }
	    return ;
	}
    }

    private void runConnection()
	throws Exception
    {
	output = (new DataOutputStream
		  (new BufferedOutputStream
		   (socket.getOutputStream()))) ;

	input = (new DataInputStream
		 (new BufferedInputStream
		  (socket.getInputStream()))) ;

	loop() ;

	socket = null ;
	pool.freeClient(this) ;
    }

    private void loop()
	throws Exception
    {
	// It will assume that all requests are GET and have no content
	try {
	    //	    while(true) {
		String line = null ;
		do {
		    line = input.readLine() ;
		} while(line != null && line.length() != 0) ;
		if(debug) System.out.println("Replying..") ;
		output.write(server.getContent()) ; // should include headers
		output.flush() ;
		//	    }
	} catch(IOException ex) {
	} finally {
	    input.close() ;
	    output.close() ;
	}
    }
	
}
