/******************************************************************************
 *
 * Copyright (c) 2001	TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *  
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *	HttpServer.cpp
 *
 *****************************************************************************/


#include <sol\ApplicationView.h>
#include <sol\Thread.h>
#include <sol\SocketStream.h>
#include <sol\StringTokenizer.h>
#include <sol\SocketSelector.h>
#include <sol\Date.h>


// Communication thread to exchange a request and
// a response between http client and this http server.
class HttpCommunicator :public Thread {
	SocketStream* socket;

  public:
	HttpCommunicator(SocketStream* sock)
	{ 
		socket = sock;
	}

	void	run() 
	{
		StringBuffer buffer;
		// Read a request line.
		socket->readLine(buffer);

		// Read the rest lines, and discard them.
		while (1) {
			StringBuffer tmp;
			int rc = socket->readLine(tmp);
			if (rc <=2) {
				// Encountered with a new line of "\r\n".
				break;
			}
			tmp.clear();
		}

		String method, path;

		const char* ptr = buffer.getBuffer();
        StringTokenizer tokenizer(buffer);
		if (ptr && strlen(ptr) >0) {
			ptr = tokenizer.getToken(method);
			if (ptr) {
				tokenizer.getToken(path);
			}
        }

		if (method == "GET") {
			sendResponse((const char*)path);
		} 
		socket -> close();
		delete socket;
	
		delete this;	//Strange code.
	}


	void	sendResponse(const char* path)
	{
		//For simplicity, ignore the path and send back the followin string.
		Date date(Date::GMT);

		String string ="<html>\r\n";
		string = string + "<h1>Sample HTTP Server</h1>\r\n";
		string = string + "<h2>\r\n";
		string = string + date.getDate();
		string = string + "</h2>\r\n";
		string = string + "</html>\r\n";
	
		int length = string.getLength();

		// Send response.
		socket -> printf("HTTP/1.0 200 OK\r\n");
		socket -> printf("Content-Length %d\r\n", length);
		socket -> printf("\r\n");

		socket -> printf("%s", (const char*)string);
	}
};


// Listener thread to accept a connection from http client
// and create a communication thread.

class HttpListener :public Thread {
    SocketStream listener;
	Boolean      looping;

  public:
	HttpListener()
		:Thread()
	{
		listener.create(AF_INET, 0);
		InetAddress addr(80, INADDR_ANY);
	
		listener.bind(addr);
		listener.listen(5);
		looping = True;
	}

	void  run() 
	{
		listener.nonBlocking();

		SocketSelector selector;
		selector.setTimeout(0, 100);

		// Thread main loop. 
		while (looping) {   
			selector.clearAll();
			selector.setReadable(&listener);
 
			if (looping == False) {
				break;
			}

            if (selector.select() != SOCKET_ERROR) {           
				if (selector.isReadable(&listener)) {
					InetAddress addr;
					// Got a connect-request from a HTTP client.
					SocketStream* socket = listener.accept(addr);

					// Create a HttpCommunication thread and start it.
					HttpCommunicator* comm = new HttpCommunicator(socket);
					comm -> start();
				}
			}
		}
	}
};


// HttpServer application
class HttpServer :public ApplicationView {
	HttpListener listener;

public:
	HttpServer(Application& applet, const char* caption, Args& args)
		:ApplicationView(applet, caption, args)
	{	
		listener.start();
	}
};



void Main(int argc, char** argv)
{
	const char* appClass = "HttpServer";
	Application applet(appClass, argc, argv);

    Args args;

    args.set(XmNclassName, appClass);
	args.set(XmNwidth, 300);
	args.set(XmNheight, 80);
    HttpServer server(applet, appClass, args);
    server.realize();

    applet.run(); 	
}

