#include "comline.hpp"
//#include <exception>
#include <stdio.h>
#include "comline.h"

static void* __cdecl operator new (unsigned int size)
{ 
	return malloc (size);
}

static void __cdecl operator delete (void *buffer)
{
	free (buffer);
}

using namespace NCOMLINE;

CCommandLineServer *NCOMLINE::n_CurServer = NULL;

void ExecuteLine(const unsigned char * pszString)
{
	if (n_CurServer)
		n_CurServer->ExecuteLine ((const char *) pszString);
}
int	 NumOutputLines(void)
{
	if (n_CurServer)
		return n_CurServer->NumOutputLines ();
	return 0;
}
void GetOutputLine(int size,unsigned char * pszString)
{
	if (n_CurServer)
		n_CurServer->GetOutputLine (size,pszString);
}
void Shutdown(void)
{
#ifdef LAB_ONLY
  RPC_STATUS status;

  status = RpcMgmtStopServerListening(NULL);
  if (!status) 
	{
	  status = RpcServerUnregisterIf(NULL, NULL, FALSE);
  }
#endif
}

DWORD WINAPI NCOMLINE::ServerThreadProc (void *p1)
{
#if LAB_ONLY
	CCommandLineServer *data;

	data = static_cast<CCommandLineServer *> (p1);

	WaitForSingleObject (data->m_Semaphore,INFINITE);
	ReleaseSemaphore (data->m_ExitSemaphore,1,NULL);

	ReleaseSemaphore (data->m_Semaphore,1,NULL);
  RPC_STATUS status;
  unsigned char * pszProtocolSequence1 = (unsigned char *) "ncacn_ip_tcp";	 
  unsigned char * pszEndpoint1         = (unsigned char *) "1404";

  unsigned char * pszProtocolSequence2 = (unsigned char *) "ncalrpc";
  unsigned char * pszEndpoint2         = (unsigned char *) "ComLine";
  unsigned char * pszSecurity         = (unsigned char *) NULL;
  unsigned int    cMinCalls           = 1;
  unsigned int    cMaxCalls           = 20;
  unsigned int    fDontWait           = FALSE;


//	status = RpcServerUseAllProtseqs (cMaxCalls,pszSecurity);
  status = RpcServerUseProtseqEp(pszProtocolSequence1,cMaxCalls,pszEndpoint1,pszSecurity);  // Security descriptor
  if (status)
		return (status);

  status = RpcServerUseProtseqEp(pszProtocolSequence2,cMaxCalls,pszEndpoint2,pszSecurity);  // Security descriptor
  if (status)
		return (status);

	RPC_BINDING_VECTOR *inq;
	status = RpcServerInqBindings (&inq);

  status = RpcServerRegisterIf(ComLine_ServerIfHandle,  // interface to register
                               NULL,   // MgrTypeUuid
                               NULL);  // MgrEpv; null means use default
  if (status)
		return (status);

  status = RpcServerListen(cMinCalls,cMaxCalls,fDontWait);
  if (status)
		return status;

	WaitForSingleObject (data->m_ExitSemaphore,INFINITE);
#endif
	return 0;
}

void CCommandLineServer::PushLine (const LineData& data)
{
	CRITICAL_CONTROL (m_Critical);
	if (!m_Inited)
		return;
	if (m_NumLines == m_SizeLines)
	{
		int i;
		LineData *temp;

		temp = new LineData[m_SizeLines+50];
		for (i=0;i<m_NumLines;i++)
		{
			temp[i] = m_Lines[i];
		}
		delete[] m_Lines;
		m_Lines = temp;
		m_SizeLines+=50;
	}
	m_Lines[m_NumLines] = data;
	m_NumLines++;
}

void CCommandLineServer::RemoveLine (void)
{
	int i;

	CRITICAL_CONTROL (m_Critical);
	if (!m_Inited)
		return;
	assert (m_NumLines);
	m_NumLines--;
	for (i=0;i<m_NumLines;i++)
	{
		m_Lines[i] = m_Lines[i+1];
	}
}

void CCommandLineServer::PushCommand (const CommandMap& data)
{
	CRITICAL_CONTROL (m_Critical);
	if (!m_Inited)
		return;
	if (m_NumLines == m_SizeLines)
	{
		int i;
		CommandMap *temp;

		temp = new CommandMap[m_SizeCommands+50];
		for (i=0;i<m_NumCommands;i++)
		{
			temp[i] = m_Commands[i];
		}
		delete[] m_Commands;
		m_Commands = temp;
		m_SizeCommands+=50;
	}
	m_Commands[m_NumCommands] = data;
	m_NumCommands++;
}

CCommandLineServer::CCommandLineServer (void)
{
	m_Inited = false;
	m_Lines = NULL;
	m_NumLines = 0;
	m_SizeLines = 0;
	m_Commands = NULL;
	m_NumCommands = 0;
	m_SizeCommands = 0;
	m_Semaphore = NULL;
	m_ExitSemaphore = NULL;
	m_Thread = NULL;

	hMappedFile=NULL;
#if LAB_ONLY
	hMappedFile=CreateFileMapping( (HANDLE)0xffffffff,	//-1 hFile flag
																				NULL,	// no sharing allowed
																				PAGE_READONLY|SEC_RESERVE, // no writing or physical space allocated
																				0,	// high-byte size
																				4,	// low byte size
																				"AHF_COMLINE_FILE");	// use class string as file name
	if (hMappedFile!=NULL && GetLastError() == ERROR_ALREADY_EXISTS)
	{
		//another T2 is running, it just hasn't created it's window yet
		CloseHandle(hMappedFile);
		hMappedFile=NULL;
		return;
	}

	m_Lines = new LineData[50];
	m_NumLines = 0;
	m_SizeLines = 50;
	PushLine (LineData ("Command Line server started",LINE_OUTPUT));
	m_Commands = new CommandMap[50];
	m_NumCommands = 0;
	m_SizeCommands = 50;

	m_Semaphore = CreateSemaphore (NULL,0,1,NULL);
	if (!m_Semaphore)
		assert (false);
	m_ExitSemaphore = CreateSemaphore (NULL,0,1,NULL);
	if (!m_ExitSemaphore)
		assert (false);
	m_Thread = CreateThread (NULL,0,NCOMLINE::ServerThreadProc,(void *) this,0,&m_ThreadID);
	if (!m_Thread)
		assert (false);

	ReleaseSemaphore (m_Semaphore,1,NULL);
	WaitForSingleObject (m_ExitSemaphore,INFINITE);
	WaitForSingleObject (m_Semaphore,INFINITE);

	n_CurServer = this;
	m_Inited = true;
#endif
}

CCommandLineServer::~CCommandLineServer (void)
{

	if (!m_Inited)
		return;
#ifdef LAB_ONLY
  RPC_STATUS status;
  status = RpcMgmtStopServerListening(NULL);
  if (!status) 
	{
	  status = RpcServerUnregisterIf(NULL, NULL, FALSE);
  }
#endif
	n_CurServer = NULL;

	ReleaseSemaphore (m_ExitSemaphore,1,NULL);

	WaitForSingleObject (m_Thread,INFINITE);

	CloseHandle (m_ExitSemaphore);
	CloseHandle (m_Semaphore);
	CloseHandle (m_Thread);

	if (hMappedFile)
		CloseHandle (hMappedFile);
	hMappedFile = NULL;

	delete[] m_Lines;
	delete[] m_Commands;
	m_Inited = false;

}

void CCommandLineServer::PushLine (const char *line)
{
	CRITICAL_CONTROL (m_Critical);
	if (!m_Inited)
		return;
	LineData data (line,LINE_OUTPUT);

	PushLine (data);
}

void CCommandLineServer::ExecuteLine (const char *line)
{
	CRITICAL_CONTROL (m_Critical);
	int iter;
	LineData data (line,LINE_COMMAND);
	CCom_String name (line);
	
	if (!m_Inited)
		return;
	ParseLine (data);
	if (data.m_ComBreak != name.GetLength ())
	{
		name.SetAt (data.m_ComBreak,0);
	}
	for (iter = 0;iter<m_NumCommands;iter++)
	{
		if (m_Commands[iter].m_Command == name)
			break;
	}
	if (iter != m_NumCommands)
	{
		if (!m_Commands[iter].m_Function (this,data))
			PushLine (LineData ("Function failed",LINE_OUTPUT));
	}
	else
	{
		PushLine (LineData ("Function not found",LINE_OUTPUT));
	}
}

void CCommandLineServer::ParseLine (LineData& data)
{
	char *buf;
	int i,size;
	bool ignore;

	CRITICAL_CONTROL (m_Critical);
	ignore = false;
	data.m_Data.TrimLeft ();
	data.m_ComBreak = -1;
	size = data.m_Data.GetLength ();
	buf = data.m_Data.GetBuffer (0);
	for (i=0;i<size;i++,buf++)
	{
		if ((*buf == ' ') && !ignore)
		{
			data.m_ArgBreak[data.m_NumArgs] = i;
			data.m_NumArgs++;
		}
		else if (*buf == '\"')
		{
			ignore = !ignore;
		}
	}
	data.m_Data.ReleaseBuffer ();
	data.m_ArgBreak[data.m_NumArgs] = data.m_Data.GetLength ();
	data.m_NumArgs++;
	data.m_ComBreak = data.m_ArgBreak[0];
}

int	CCommandLineServer::NumOutputLines(void)
{
	return m_NumLines;
}

void CCommandLineServer::GetOutputLine(int size,unsigned char * pszString)
{
	CRITICAL_CONTROL (m_Critical);
	LineData *iter;

	iter = m_Lines;

	if (m_NumLines)
	{
		const char *buffer = (LPCTSTR) iter->m_Data;
		strncpy ((char *) pszString,buffer,size-1);
		pszString[size-1] = 0;
		RemoveLine ();
	}
	else
		pszString[0] = 0;
}

bool CCommandLineServer::AddCommand (NCOMLINE::ComFunc func,const CCom_String& name)
{
	CRITICAL_CONTROL (m_Critical);
	int iter;

	if (!m_Inited)
		return false;
	for (iter = 0;iter<m_NumCommands;iter++)
	{
		if (m_Commands[iter].m_Command == name)
			break;
	}
	if (iter != m_NumCommands)
		m_Commands[iter].m_Function = func;
	else
		PushCommand (CommandMap (name,func));
	return true;
}

bool CCommandLineServer::RemoveCommand (const CCom_String& name)
{
	CRITICAL_CONTROL (m_Critical);
	int iter;
	int i;
	if (!m_Inited)
		return false;
	for (iter = 0;iter<m_NumCommands;iter++)
	{
		if (m_Commands[iter].m_Command == name)
			break;
	}
	if (iter == m_NumCommands)
		return false;
	m_NumCommands--;
	for (i=iter;i<m_NumCommands;i++)
	{
		m_Commands[i] = m_Commands[i+1];
	}
	return true;

}

