/**********************************************************************
 * Copyright (c) 2005, 2009 IBM, Intel Corporation
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * $Id: ACService.c,v 1.11 2009/04/14 20:33:30 jwest Exp $
 *
 * Contributors:
 * IBM - Initial API and implementation
 * Vishnu K Naikawadi,Intel - Implementation for New Agent Controller
 **********************************************************************/

#ifdef _WIN32     // Windows-specific

#include "ConnectionManager.h"
#include "AgentManager.h"
#include "ConfigurationManager.h"
#include "LoggingService.h"
#include "ACLog.h"
#include "tptp/TPTPCommon.h"
#include <windows.h>
#include <process.h>
#include <direct.h>
#include <shlwapi.h>
#include <stdio.h>
#include <stdlib.h>

#include "ACService.h"



/* Forward declarations */
tptp_int32 initializeProgram();
void handleMainComplete();
void cleanup();
void initializeDataStructures();
tptp_int32 getConfigurationPath(char* serviceName, unsigned char *buffer, tptp_int32 length);


/* Avoid copying these as globals into other code modules */
/*    They get passed as parameters where they are needed */
AgentManager_t         agentManager;
ConnectionManager_t    connectionManager;
ConfigurationManager_t configurationManager;
LoggingService_t       loggingService;
/**
  * Globals
  */
static SERVICE_STATUS_HANDLE _statusHandle;
static SERVICE_STATUS _status;


/**
  * Function prototypes
  */

VOID WINAPI Handler(DWORD fdwControl);
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv);
void trimQuotes(char* str);


/**
 *********************************************************************
 *                                                       
 * @brief                                  
 *    Retrieves the path to the configuarion directory that was
 *    created during the install.
 *                                                       
 * @return
 *    nonzero if program execution should continue
 *    0       if program execution should not continue (i.e. we forked)
 *
 *********************************************************************/
tptp_int32 getConfigurationPath(char* serviceName, unsigned char *buffer, tptp_int32 length) 
{

	HKEY key;
	DWORD type;
	long result;
	char currentWorkingDir[BUFFER_SIZE];

	char *acHome = 0;
	tptp_int32 len;

	acHome = getenv(CONFIGURATION_HOME); /* Check TPTP_AC_HOME environment first */

	/* Bug 68906 */
	if(!acHome) 
	{ /* If no TPTP_AC_HOME defined, try to resolve using the current working directory */
		if(buffer) 
		{
			len = strlen((char*)buffer);

			/* Open the registry and find the name of our service(s) */ 
			result=RegOpenKeyEx(HKEY_LOCAL_MACHINE,	/* Parent key */
								"SOFTWARE\\Eclipse\\TPTP\\ACService",				/* Subkey to open */
								0,					/* Reserved */
								KEY_READ,			/* Open for reading */
								&key);				/* The resulting handle */

			/*  Read  the first entry in the key.  It may be possible to
				support multiple services at the same time but for now we
				are just doing one.
			*/  
			result=RegQueryValueEx(key,				/* Key to query */
								   serviceName,		/* The value to query */
								   NULL,			/* Reserved */
								   &type,			/* String data */
								   buffer,			/* Copy into front of buffer */
								   &length);		/* Size of the buffer to copy into */

			/* Close the registry key */
			RegCloseKey(key);	
						
			if(result != 0) 
			{
				return -1;
			}			

			/* Check buffer size */
			if(strlen((char*)buffer) > BUFFER_SIZE - strlen(PARENT_CONFIG_DIR_PATH)) 
			{
				/* TODO: tptp_setLastError(INSUFFICIENT_BUFFER_SIZE, 0); */
				return -1;
			}

			strcat(buffer, "\\bin\\");
			SetCurrentDirectory(buffer);

			/* Case 1: Absolute path on Windows with a drive letter */
			if((buffer != NULL) && ((buffer[1] == ':') && (buffer[2] == FILE_SEPARATOR))) {
				strcat(buffer, "..");
			}
			/* Case 3: Relative path */
			else 
			{
				char *cwd = (char*)tptp_malloc(sizeof(char) * BUFFER_SIZE);
				BZERO(cwd, BUFFER_SIZE);
				getcwd(cwd, BUFFER_SIZE);

				/* Special case: absolute path on Windows without a driver letter */
				if((buffer != NULL) && (buffer[0] == FILE_SEPARATOR)) 
				{
					acHome = (char*)tptp_malloc(sizeof(char) * BUFFER_SIZE);
					BZERO(acHome, BUFFER_SIZE);
					strncpy(acHome, cwd, 2); /* Prepend with the drive letter */
					strcat(acHome, buffer);
					strcat(acHome, "..");
					strcpy(buffer, acHome);
					tptp_free(cwd);
					tptp_free(acHome);
				}
				else 
				{
					if(strlen((char*)cwd) + 1 + strlen((char*)buffer) + strlen(PARENT_CONFIG_DIR_PATH) > BUFFER_SIZE) 
					{ /* Reserve 1 for FILE_SEPARATOR */
						/* TODO: tptp_setLastError(INSUFFICIENT_BUFFER_SIZE, 0); */
						tptp_free(cwd);
						return -1;
					}
					else 
					{
						acHome = (char*)tptp_malloc(sizeof(char) * BUFFER_SIZE);
						BZERO(acHome, BUFFER_SIZE);
						sprintf(acHome, "%s%c%s..", cwd, FILE_SEPARATOR, buffer);
						strcpy((char*)buffer, acHome);
						tptp_free(cwd);
						tptp_free(acHome);
					}
				}
			}
		}
		else 
		{ /* Both env and argv[0] do not exist */
			/* TODO: tptp_setLastError(RASERVER_HOME_NOT_SET, 0); */
			return -1;
		}
	}
	else 
	{
		/* Check the size of the buffer we were supplied for the data */
		len = strlen(acHome);
		if(len + (tptp_int32) strlen(RELATIVE_CONFIGURATION_DIR) + 1 > length) 
		{
			/* TODO: tptp_setLastError(INSUFFICIENT_BUFFER_SIZE, 0); */
			return -1;
		}
		memcpy(buffer, acHome, len+1);
		strcpy(currentWorkingDir, buffer);
		strcat(currentWorkingDir, "\\bin");
		SetCurrentDirectory(currentWorkingDir);
	}
	/* Bug 68906 */	

	strcat((char*)buffer, RELATIVE_CONFIGURATION_DIR);

	return 0;
}

/**
  * SERVICE_MAIN  **************************************************************
  * This is the main entry point for the service.  The main() function registers
  * this entry point with the Service control manager. 
  */
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv ) 
{

	int serverStarted = 0;
    char configBuffer[BUFFER_SIZE];
    LPTSTR serviceName;
	int configRet = 0;

    /* The service name is the first parameter */
    serviceName=lpszArgv[0];


	_statusHandle=RegisterServiceCtrlHandler(
                   serviceName,
                   &Handler);

	/* Set the initial state of the service */
	_status.dwServiceType=SERVICE_WIN32_OWN_PROCESS;
	_status.dwCurrentState=SERVICE_START_PENDING;
	_status.dwWin32ExitCode= NO_ERROR;
	_status.dwWaitHint=1000;
	_status.dwServiceSpecificExitCode=NO_ERROR;
	_status.dwControlsAccepted=0;
	_status.dwCheckPoint=1;
	SetServiceStatus(_statusHandle, &_status);

	/* Initialize the server data tables */
	initializeXMLPlatformUtils();
	initializeDataStructures();

	/* Bug 175696 starts */
	if(!isEnvironmentValid()) {
		_status.dwWin32ExitCode= ERROR_SERVICE_SPECIFIC_ERROR;
		_status.dwCurrentState=SERVICE_STOPPED;
		SetServiceStatus(_statusHandle, &_status);
		TPTP_LOG_ERROR_MSG(am, "The TEMP environment variable does not point to a valid directory.");
		TPTP_LOG_ERROR_MSG(am, "Agent Controller will not start.");
		return;
	}
	/* Bug 175696 ends */

    /* Locate our configuration file */
    if(getConfigurationPath(serviceName, configBuffer, BUFFER_SIZE)<0) 
	{
        _status.dwWin32ExitCode= ERROR_SERVICE_SPECIFIC_ERROR;
		//_status.dwServiceSpecificExitCode=tptp_getLastError();
        _status.dwCurrentState=SERVICE_STOPPED;
		SetServiceStatus(_statusHandle, &_status);
		return;
    }
	else
	{
		configurationManager_setConfigDirectory( &configurationManager, configBuffer );
	}
	
	/* Read our configuration settings */
	/* Process the configuration file */
	configRet = configurationManager_readConfigFile( &configurationManager );
	if(configRet<0) 
	{
		_status.dwWin32ExitCode= ERROR_SERVICE_SPECIFIC_ERROR;
		//_status.dwServiceSpecificExitCode=ra_getLastErrorMajor();
        _status.dwCurrentState=SERVICE_STOPPED;
		SetServiceStatus(_statusHandle, &_status);
	}	

	TPTP_LOG_DEBUG_MSG( am, "Ready to start servers" );

	/* Next try and get the server running */
	/* start the transport layers */
	serverStarted = connectionManager_startServers( &connectionManager );

	if(serverStarted != 0)  
	{
		TPTP_LOG_DEBUG_MSG( am, "Error starting transport layers, Agent controller exiting.");
		/* We couldn't get the server running, therfore set the
		   state to SERVICE_STOPPED and exit
		*/
		_status.dwWin32ExitCode= ERROR_SERVICE_SPECIFIC_ERROR;
		//_status.dwServiceSpecificExitCode=ra_getLastErrorMajor();
        _status.dwCurrentState=SERVICE_STOPPED;
		SetServiceStatus(_statusHandle, &_status);

	}
	else 
	{
    	agentManager_startAutoloadAgents( &agentManager );
		TPTP_LOG_DEBUG_MSG( am, "Everything is running" );
			
		/* Notify the Service Control Manager we are running and
		   wait until we get a SERVICE_CONTROL_STOP message
		*/
//		_hStopEvent=CreateEvent(NULL, TRUE, FALSE, NULL);
		_status.dwCurrentState=SERVICE_RUNNING;
		_status.dwControlsAccepted=SERVICE_ACCEPT_STOP;
		SetServiceStatus(_statusHandle, &_status);
//		WaitForSingleObject(_hStopEvent, INFINITE);
	}

	handleMainComplete();
	terminateXMLPlatformUtils();
}


/**
 *********************************************************************
 *                                                       
 * @brief                                  
 *    Unlike shutdownServer above, this function causes THIS instance
 *    of the AC to be shutdown.  shutdownServer shutsdown a peer AC.
 *                                                       
 *********************************************************************/
tptp_int32 agentController_shutdown()
{
    HANDLE hStopEvent;
	hStopEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, "TPTPACStopEvent");
	SetEvent( hStopEvent );
	return 0;
}


void initializeDataStructures()
{

	loggingService_init( &loggingService );
	
	/* Tell ourcomponents about each other */
	agentManager.connectionManager = &connectionManager;
	agentManager.configurationManager = &configurationManager;
	agentManager.loggingService = &loggingService;
	connectionManager.agentManager = &agentManager;
	connectionManager.loggingService = &loggingService;
	configurationManager.connectionManager = &connectionManager;
	configurationManager.agentManager = &agentManager;
	configurationManager.loggingService = &loggingService;

	/* Initialize */
	connectionManager_init( &connectionManager );
	agentManager_init( &agentManager );
	configurationManager_init( &configurationManager );

	return;
}


/** 
  * HANDLER  *******************************************************************
  * This is the handler function that will be called by Windows with various
  * service messages.  These messages are start/stop/shutdown, etc.
  */
VOID WINAPI Handler(DWORD fdwControl) {

	switch(fdwControl) {
	case SERVICE_CONTROL_STOP: {
		_status.dwCurrentState=SERVICE_STOP_PENDING;
		_status.dwWaitHint=1000;
		if(!SetServiceStatus(_statusHandle, &_status)) {
			DWORD status=GetLastError();
		}
		//ra_stopServer();//TODO
		//ra_processServiceStopConfiguration();//TODO
		cleanup();

		_status.dwCurrentState=SERVICE_STOPPED;
		_status.dwWin32ExitCode=NO_ERROR;
		_status.dwServiceSpecificExitCode=0;
		_status.dwWaitHint=0;
		//SetEvent(_hStopEvent);
		break;
	}
	case SERVICE_CONTROL_PAUSE:
	case SERVICE_CONTROL_CONTINUE:
	case SERVICE_CONTROL_INTERROGATE:
	case SERVICE_CONTROL_SHUTDOWN:
		_status.dwCurrentState=SERVICE_RUNNING;
		_status.dwWin32ExitCode=NO_ERROR;
		break;
	}
	/* We should always return the status when  the handler is called */
	SetServiceStatus(_statusHandle, &_status);
	if(!SetServiceStatus(_statusHandle, &_status)) {
			DWORD status=GetLastError();
	}
}

void handleMainComplete()
{
	/* wait */
    HANDLE hStopEvent;
	hStopEvent = CreateEvent(NULL, TRUE, FALSE, "TPTPACStopEvent");
	WaitForSingleObject(hStopEvent, INFINITE);

	/* cleanup(); */ /* Bug 192176 */
	Handler(SERVICE_CONTROL_STOP); /* Bug 192176 */
}

void cleanup()
{
	connectionManager_cleanup( &connectionManager );
	agentManager_cleanup( &agentManager );
	configurationManager_cleanup( &configurationManager );
}


/**
  * MAIN  **********************************************************************
  * This is the entry point for the application.  This main() must be called by 
  * Windows to start this application as a service as opposed to an application.
  * In this function we search for our service name in the registry and setup our
  * service dispatch table.
  */
int main (int argc, char **argv) {
    long result;
    HKEY key;
    char nameBuffer[BUFFER_SIZE], pathBuffer[BUFFER_SIZE];
    DWORD nameSize=BUFFER_SIZE, pathSize=BUFFER_SIZE, type;
    LPTSTR cmdLine;

    int i;

    /* Extract our command line.  When a Service is Started on Windows our command line
       consists of the fully qualified name of the service executable.
    */
    cmdLine=GetCommandLine();

    /* Just in case somebody enables some means of passing additional parameters we should cut them off. */
//    exePath=strchr(cmdLine, ' ');
//    if(exePath) {
//        *exePath='\0';
//    }


	/* Open the registry key that has a list of all of the Hyades service(s) */ 
	result=RegOpenKeyEx(HKEY_LOCAL_MACHINE,	                                /* Parent key */
						"SOFTWARE\\Eclipse\\TPTP\\ACService",				/* Subkey to open */
						0,					                                /* Reserved */
						KEY_READ,			                                /* Open for reading */
						&key);                              				/* The resulting handle */

    /*  Search through the values in the key.  We are looking for a value
        that corresponds to the current fully qualified path of the executable
        we are running.  We need to do this so that we can determine what our
        proper service name is.
    */
    i=0;
    do {
        char fullPath[BUFFER_SIZE];
        nameSize=pathSize=BUFFER_SIZE;
        result=RegEnumValue(key,
                            i,
                            nameBuffer,
                            &nameSize,
                            NULL,
                            &type,
                            pathBuffer,
                            &pathSize);

        if(result==ERROR_NO_MORE_ITEMS) {
            return -1;
        }

        strcpy(fullPath, pathBuffer);
        fullPath[pathSize]='\0';
        strcat(fullPath, "\\bin\\ACWinService.exe");
		trimQuotes(fullPath);
		trimQuotes(cmdLine);
        if(strcmp(cmdLine, fullPath)==0) {
            break;
        }
        else {
            i++;
        }
    }while(TRUE);



    /* Close the registry key */
    RegCloseKey(key);

	if(result != ERROR_SUCCESS) {
		return -1;
	}

    /* Setup the service dispatch table with the proper service name and the entry point. */
    {
	    SERVICE_TABLE_ENTRY dispatchTable[] = {
		    { nameBuffer, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
		    { NULL, NULL }
		    };
	    StartServiceCtrlDispatcher(dispatchTable);
    }

	return 0;
}

void trimQuotes(char* str) {
	unsigned int i;

	/* Trim leading quotes */
	while(str[0] == '\"') {
		for(i = 0; i < strlen(str) - 1; i++) {
			str[i] = str[i+1];
		}
		str[strlen(str) - 1] = '\0';
	}

	/* Trim trailing quotes */
	while(str[strlen(str) - 1] == '\"') {
		str[strlen(str) - 1] = '\0';
	}

	return;
}

#endif // Windows Specific
