//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "stdafx.h"
#include "service_helpers.h"


static CRITICAL_SECTION g_CtrlHandlerMutex;

static void (*g_pInternalServiceFn)( void *pParam ) = NULL;
static void *g_pInternalServiceParam = NULL;

static volatile bool g_bShouldExit = false;


SERVICE_STATUS          MyServiceStatus; 
SERVICE_STATUS_HANDLE   MyServiceStatusHandle = NULL;


void WINAPI MyServiceCtrlHandler( DWORD Opcode )
{ 
    DWORD status; 
 
    switch(Opcode) 
    { 
        case SERVICE_CONTROL_STOP: 
			// Do whatever it takes to stop here. 
			ServiceHelpers_ExitEarly();

            MyServiceStatus.dwWin32ExitCode = 0; 
            MyServiceStatus.dwCurrentState  = SERVICE_STOPPED; 
 
            if ( !SetServiceStatus( MyServiceStatusHandle, &MyServiceStatus) )
            { 
                status = GetLastError(); 
                Msg( "[MY_SERVICE] SetServiceStatus error %ld\n", status ); 
            } 
 
            Msg( "[MY_SERVICE] Leaving MyService \n" ); 
            return; 
 
        case SERVICE_CONTROL_INTERROGATE: 
			// Fall through to send current status. 
            break; 
 
        default: 
            Msg("[MY_SERVICE] Unrecognized opcode %ld\n", Opcode ); 
    } 
 
    // Send current status. 
    if ( !SetServiceStatus( MyServiceStatusHandle, &MyServiceStatus ) )
    { 
        status = GetLastError(); 
        Msg( "[MY_SERVICE] SetServiceStatus error %ld\n", status ); 
    } 
}


void WINAPI MyServiceStart( DWORD argc, LPTSTR *argv ) 
{ 
    DWORD status; 

    MyServiceStatus.dwServiceType        = SERVICE_WIN32; 
    MyServiceStatus.dwCurrentState       = SERVICE_START_PENDING; 
    MyServiceStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP; 
    MyServiceStatus.dwWin32ExitCode      = 0; 
    MyServiceStatus.dwServiceSpecificExitCode = 0; 
    MyServiceStatus.dwCheckPoint         = 0; 
    MyServiceStatus.dwWaitHint           = 0; 
 
    MyServiceStatusHandle = RegisterServiceCtrlHandler( "MyService", MyServiceCtrlHandler ); 
    if ( MyServiceStatusHandle == (SERVICE_STATUS_HANDLE)0 ) 
    { 
        Msg("[MY_SERVICE] RegisterServiceCtrlHandler failed %d\n", GetLastError() );
        return; 
    } 

    // Initialization complete - report running status. 
    MyServiceStatus.dwCurrentState       = SERVICE_RUNNING; 
 
    if ( !SetServiceStatus( MyServiceStatusHandle, &MyServiceStatus ) )
    { 
        status = GetLastError(); 
        Msg( "[MY_SERVICE] SetServiceStatus error %ld\n", status );
    } 


	// Run the app's main in-thread loop.    
	g_pInternalServiceFn( g_pInternalServiceParam );


	// Tell the SCM that we're stopped.        
	MyServiceStatus.dwCurrentState       = SERVICE_STOPPED; 
    MyServiceStatus.dwWin32ExitCode      = NO_ERROR;
    MyServiceStatus.dwServiceSpecificExitCode = 0; 
    SetServiceStatus( MyServiceStatusHandle, &MyServiceStatus );


    // This is where the service does its work. 
    Msg( "[MY_SERVICE] Returning the Main Thread \n" ); 
}


void ServiceHelpers_Init()
{
	InitializeCriticalSection( &g_CtrlHandlerMutex );
}


bool ServiceHelpers_StartService( const char *pServiceName, void (*pFn)( void *pParam ), void *pParam )
{
	// Ok, just run the service.
	const SERVICE_TABLE_ENTRY DispatchTable[2] = 
	{
		{ (char*)pServiceName, MyServiceStart },
		{ NULL, NULL }
	};

	g_pInternalServiceFn = pFn;
	g_pInternalServiceParam = pParam;

	if ( StartServiceCtrlDispatcher( DispatchTable ) ) 
	{
		return true;
	}
	else
	{ 											    
		Msg( "StartServiceCtrlDispatcher error = '%s'\n", GetLastErrorString() ); 
		return false;
	}
}


void ServiceHelpers_ExitEarly()
{
	EnterCriticalSection( &g_CtrlHandlerMutex );
	g_bShouldExit = true;
	LeaveCriticalSection( &g_CtrlHandlerMutex );
}


bool ServiceHelpers_ShouldExit()
{
	EnterCriticalSection( &g_CtrlHandlerMutex );
	bool bRet = g_bShouldExit;
	LeaveCriticalSection( &g_CtrlHandlerMutex );

	return bRet;
}


char* GetLastErrorString()
{
	static char err[2048];
	
	LPVOID lpMsgBuf;
	FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &lpMsgBuf,
		0,
		NULL 
	);

	strncpy( err, (char*)lpMsgBuf, sizeof( err ) );
	LocalFree( lpMsgBuf );

	err[ sizeof( err ) - 1 ] = 0;

	return err;
}