#include "StdAfx.h"
#include "housekeeper.h"

// the one and only HouseKeeper object
HouseKeeper hk;

HouseKeeper::HouseKeeper()
{
	PingPeriod = 30000;
	dead = 0;
	// create the event handles 
	MessageNotify = CreateEvent(NULL,TRUE,FALSE,NULL);
    // Initialize the critical section.
    InitializeCriticalSection(&CriticalSection); 
}

HouseKeeper::~HouseKeeper(void)
{
	CloseHandle(MessageNotify);
    DeleteCriticalSection(&CriticalSection);
}

int HouseKeeper::PostMessageAndWait(Message & msg, int timeout)
{
	msg.NotifyDone = CreateEvent(NULL,TRUE,NULL,NULL);
	PostMessage(msg);	
	WaitForSingleObject(msg.NotifyDone,timeout);
	CloseHandle(msg.NotifyDone);
	return msg.result;
}

void HouseKeeper::PostMessage(Message & msg)
{
    // Request ownership of the critical section.
    __try 
    {
        EnterCriticalSection(&CriticalSection); 

        // Access the shared resource.	
		if(msg.NotifyDone){
			Q.push_front(&msg);
		} else {
			Message * msgcopy = msg.Copy();
			Q.push_front(msgcopy);
		}
		PulseEvent(MessageNotify);
    }
    __finally 
    {
        // Release ownership of the critical section.
        LeaveCriticalSection(&CriticalSection);
    }
}

// this gets called every PingPeriod ms to perform house-keeping operations
void HouseKeeper::IdleProcess()
{
	Vector<int> eventIds;
	int i;

	switch(db.GetAllActiveEvents(eventIds)){
		case dbException:
			break;
		case dbSuccess:
			// check for events that started
			for(i=0;i<eventIds.length;i++){
				if(ActiveEvents.find(eventIds[i])==-1){
					// this event has started
					ClientContext::NotifyEventMembers(eventIds[i],bcEvent,eventIds[i],bcActive,0);
				}
			}

			// check for events that ended
			for(i=0;i<ActiveEvents.length;i++){
				if(eventIds.find(ActiveEvents[i])==-1){
					// this event has ended
					ClientContext::NotifyEventMembers(ActiveEvents[i],bcEvent,ActiveEvents[i],bcIdle,0);
				}
			}

			// update the current Active events
			ActiveEvents = eventIds;
			break;
	}

	// make sure each location record denotes the correct # of active events
	Vector<int> locationIds;
	db.GetAllLocations(locationIds);
	for(i=0;i<locationIds.length;i++){
		LocationRecord record;

		// get the location record
		if(db.GetLocation(locationIds[i],record)==dbSuccess){
			
			// get the number of active events at this location
			if(db.GetActiveEventsByLocation(locationIds[i],eventIds)==dbSuccess){

				// change the location record if it needs updating
				if(record.nEvents!=eventIds.length){
					db.SetLocationEventCount(locationIds[i],eventIds.length);
					ClientContext::NotifyOnlineUsers(bcLocation,locationIds[i],bcModified,eventIds.length);
				}

			} else { // failed to get active events
				print(L"HouseKeeper::IdleProcess - unable to get active events at location id=%d\n",locationIds[i]);
			}

		} else { // failed to get location record
			print(L"HouseKeeper::IdleProcess - unable to get location id=%d\n",locationIds[i]);
		}
	}
}

int HouseKeeper::MessagePump()
{
	IdleProcess();
	while(!dead){
		if(WaitForSingleObject(MessageNotify,PingPeriod)==WAIT_TIMEOUT){
			if(dead) break;
			IdleProcess();
		} else { 
			if(dead) break;
			// the pump is being notified to take care of a message from outside this thread
			while(Q.size()>0){
				Message * msg = Q.back();
				switch(msg->type){
					case Message::KeepHouse:
						IdleProcess();
						break;
					case Message::Quit:
						dead = 1;
						break;
					case Message::Unknown:
					default: // unknown message
						break;
				}
				Q.pop_back();
				if(msg->NotifyDone) // this means that this message is being waited on, so don't delete it (see PostMessageAndWait)
					SetEvent(msg->NotifyDone);
				else
					delete msg;
			}
		}
	}

	return dead;
}

void HouseKeeper::Die()
{
	dead = 1;
	PulseEvent(MessageNotify);
}

void HouseKeeper::Start()
{
	hThread = CreateThread(NULL,0,StartMessagePump,this,NULL,NULL);
}

DWORD WINAPI HouseKeeper::StartMessagePump(LPVOID p)
{
	HouseKeeper * x = (HouseKeeper*)p;
	x->MessagePump();
	Sleep(5000);
	delete x;
	return 0;
}