#include <Message.h>
#include <AppDefs.h>
#include "scheduler.h"
#include <stdio.h>
#include "AppServer.h"
#include "ServerApp.h"
#include "Desktop.h"
#include "ServerProtocol.h"
#include "PortLink.h"
#include "DisplayDriver.h"

AppServer::AppServer(void) : BApplication("application/x-vnd.obe-OBAppServer")
{
//	printf("AppServer()\n");

	mouseport=create_port(30,SERVER_INPUT_PORT);
	messageport=create_port(20,SERVER_PORT_NAME);
//	printf("Server message port: %ld\n",messageport);
//	printf("Server input port: %ld\n",mouseport);
	applist=new BList(0);
	quitting_server=false;
	exit_poller=false;

	// Set up the Desktop
	init_desktop(3);

	// Spawn our input-polling thread
	poller_id = spawn_thread(PollerThread, "Poller", B_NORMAL_PRIORITY, this);
	if (poller_id >= 0)
		resume_thread(poller_id);
	driver=get_gfxdriver();
}

AppServer::~AppServer(void)
{
	shutdown_desktop();

	ServerApp *temp;
	for(int32 i=0;i<applist->CountItems();i++)
	{
		temp=(ServerApp *)applist->ItemAt(i);
		if(temp!=NULL)
			delete temp;
	}
	delete applist;
}

thread_id AppServer::Run(void)
{
	//printf("AppServer::Run()\n");
	
	MainLoop();
	return 0;
}

void AppServer::MainLoop(void)
{
	// Main loop (duh). Monitors the message queue and dispatches as appropriate.
	// Input messages are handled by the poller, however.

	//printf("AppServer::MainLoop()\n");

	int32 msgcode;
	int8 *msgbuffer=NULL;
	ssize_t buffersize,bytesread;
	
	for(;;)
	{
		buffersize=port_buffer_size(messageport);
		if(buffersize>0)
			msgbuffer=new int8[buffersize];
		bytesread=read_port(messageport,&msgcode,msgbuffer,buffersize);

		if (bytesread != B_BAD_PORT_ID && bytesread != B_TIMED_OUT && bytesread != B_WOULD_BLOCK)
		{
			switch(msgcode)
			{
				case CREATE_APP:
				case DELETE_APP:
				case B_QUIT_REQUESTED:
					DispatchMessage(msgcode,msgbuffer);
					break;
				default:
					//printf("Server received unexpected code %ld\n",msgcode);
					break;
			}

		}

		if(buffersize>0)
			delete msgbuffer;
		if(msgcode==DELETE_APP || msgcode==B_QUIT_REQUESTED)
		{
			if(quitting_server==true && applist->CountItems()==0)
				break;
		}
	}
	// Make sure our polling thread has exited
	if(find_thread("Poller")!=B_NAME_NOT_FOUND)
		kill_thread(poller_id);

}


void AppServer::DispatchMessage(int32 code, int8 *buffer)
{
	int8 *index=buffer;
	switch(code)
	{
		case CREATE_APP:
		{
			// Create the ServerApp to node monitor a new BApplication
			
			//printf("AppServer: Create App\n");
			
			// Attached data:
			// 1) port_id - receiver port of a regular app
			// 2) char * - signature of the regular app

			// Find the necessary data
			port_id app_port=*((port_id*)index);
			index+=sizeof(port_id);

			char *app_signature=(char *)index;
			
			// Create the ServerApp subthread for this app
			ServerApp *newapp=new ServerApp(app_port,app_signature);
			applist->AddItem(newapp);
			newapp->Run();
			break;
		}
		case DELETE_APP:
		{
			// Delete a ServerApp. Received only from the respective ServerApp when a
			// BApplication asks it to quit.
			
			//printf("AppServer: Delete App\n");
			
			// Attached Data:
			// 1) thread_id - thread ID of the ServerApp to be deleted
			
			int32 i, appnum=applist->CountItems();
			ServerApp *srvapp;
			thread_id srvapp_id=*((thread_id*)buffer);
			
			// Run through the list of apps and nuke the proper one
			for(i=0;i<appnum;i++)
			{
				srvapp=(ServerApp *)applist->ItemAt(i);
				if(srvapp!=NULL && srvapp->monitor_thread==srvapp_id)
				{
					srvapp=(ServerApp *)applist->RemoveItem(i);
					delete srvapp;
				}
			}
			break;
		}
		case B_QUIT_REQUESTED:
		{
			//printf("Shutdown sequence initiated.\n");

			// Attached Data:
			// none
			
			// We've been asked to quit, so (for now) broadcast to all
			// test apps to quit. This situation will occur only when the server
			// is compiled as a regular Be application.
			PortLink *applink=new PortLink(0);
			ServerApp *srvapp;
			int32 i,appnum=applist->CountItems();
			
			applink->SetOpCode(QUIT_APP);
			for(i=0;i<appnum;i++)
			{
				srvapp=(ServerApp *)applist->ItemAt(i);
				if(srvapp!=NULL)
				{
					applink->SetPort(srvapp->receiver);
					applink->Flush();
				}
			}

			delete applink;
			// So when we delete the last ServerApp, we can exit the server
			quitting_server=true;
			exit_poller=true;
			break;
		}
		default:
			//printf("Unexpected message %ld (%lx) in AppServer::DispatchMessage()\n",code,code);
			break;
	}
}

void AppServer::Poller(void)
{
	// This thread handles nothing but input messages for mouse and keyboard
	int32 msgcode;
	int8 *msgbuffer=NULL,*index;
	ssize_t buffersize,bytesread;
	
	for(;;)
	{
		buffersize=port_buffer_size(mouseport);
		if(buffersize>0)
			msgbuffer=new int8[buffersize];
		bytesread=read_port(mouseport,&msgcode,msgbuffer,buffersize);

		if (bytesread != B_BAD_PORT_ID && bytesread != B_TIMED_OUT && bytesread != B_WOULD_BLOCK)
		{
			switch(msgcode)
			{
				case B_MOUSE_DOWN:
				{
					//printf("AppServer: Mouse Down\n");
					
					// Attached data:
					// 1) int64 - time of mouse click
					// 2) float - x coordinate of mouse click
					// 3) float - y coordinate of mouse click
					// 4) int32 - modifier keys down during click
					// 5) int32 - buttons down
					// 6) int32 - number of clicks
					break;
				}
				case B_MOUSE_MOVED:
				{
					// Attached data:
					// 1) int64 - time of mouse click
					// 2) float - x coordinate of mouse click
					// 3) float - y coordinate of mouse click
					// 4) int32 - buttons down
					
					index=msgbuffer;

					// Throw away the time sent - for now
					index += sizeof(int64);
					
					float tempx=0,tempy=0;
					tempx=*((float*)index);
					index+=sizeof(float);
					tempy=*((float*)index);
					index+=sizeof(float);

					driver->MoveCursorTo((int)tempx,(int)tempy);
					break;
				}
				case B_MOUSE_UP:
				{
					//printf("AppServer: Mouse Up\n");
					
					// Attached data:
					// 1) int64 - time of mouse click
					// 2) float - x coordinate of mouse click
					// 3) float - y coordinate of mouse click
					// 4) int32 - buttons down
		
					break;
				}
				default:
					//printf("Server::Poller received unexpected code %ld\n",msgcode);
					break;
			}

		}

		if(buffersize>0)
			delete msgbuffer;

		if(exit_poller)
			break;
	}
}

int32 AppServer::PollerThread(void *data)
{
	//printf("Starting Poller thread...\n");
	AppServer *server=(AppServer *)data;
	
	server->Poller();	// This won't return until it's told to
	
	//printf("Exiting Poller thread...\n");
	exit_thread(B_OK);
	return B_OK;
}

int main( int argc, char** argv )
{
	AppServer *obas = new AppServer();
	obas->Run();
	delete obas;
	return 0;
}

 