#include "DynCache.h"

// The name of the service
LPWSTR  SERVICE_NAME = L"DynCache";

// Flag to signal that the service is shutting down
BOOL g_bServiceShutdown = FALSE;

// Handle used to communicate status info to the Service Control Manager.  Created by RegisterServiceHandler
SERVICE_STATUS_HANDLE g_ServiceStatusHandle;

// Found in DynCache.cpp
extern HANDLE g_hEvents[MAX_NOTIFICATIONS];     // Handles to notification events
extern HANDLE g_hPauseService;                  // Event to pause the service
extern BOOL g_bInitializationComplete;
extern size_t  g_MaxCacheSizeBytesLimit;
extern size_t  g_CurrentMinCacheSizeBytes;


// Report an error then clean up
void SvcCleanup( DWORD dwStatus )
{
    DebugMessage(L"DynCache is exiting.\n");
    
    if (dwStatus != ERROR_SUCCESS)
    {
        DebugError(dwStatus);
	}
    
    if (g_bInitializationComplete)
    {
        // Set the cache limits back to what was defined in the registry
        LimitCache(g_MaxCacheSizeBytesLimit, g_CurrentMinCacheSizeBytes);
    }
    
    // Notify the Service Control Manager that we are stopping
	if (g_ServiceStatusHandle)
		SendStatusToSCM(SERVICE_STOPPED, dwStatus, 0, 0, 0);

}



// Dispatches events received from the Service Control Manager
void ServiceCtrlHandler (DWORD dwControlCode)
{
	DWORD   dwCurrentState = SERVICE_RUNNING;

	switch(dwControlCode)
	{
		// There is no START option because the main function gets called when starting

        case SERVICE_CONTROL_STOP:

            // Tell the Service Control Manager that we are stopping
            SendStatusToSCM(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);

            // Stop the service
            StopService();
            return;

        case SERVICE_CONTROL_PAUSE:

            // Tell the Service Control Manager that we are pausing
            SendStatusToSCM( SERVICE_PAUSE_PENDING, NO_ERROR, 0, 1, 5000);
		
            // Pause the service
            PauseService();
        
            // Set our status to paused
            dwCurrentState = SERVICE_PAUSED;
            break;

        case SERVICE_CONTROL_CONTINUE:

            // Tell the Service Control Manager that we are resuming
            SendStatusToSCM(SERVICE_CONTINUE_PENDING, NO_ERROR, 0, 1, 5000);
		
            // Resume the service
            ResumeService();
        
            // Set our status to running
            dwCurrentState = SERVICE_RUNNING;
            break;

		// Do nothing in a shutdown. Could do cleanup here but it must be very quick.
        case SERVICE_CONTROL_SHUTDOWN:
		
            // The Service Control Manager is shutting down, so should we
            StopService();
            return;
	
        default:
            break;
	}

    // Upate the Service Control Manager with our new status
	SendStatusToSCM(dwCurrentState, NO_ERROR, 0, 0, 0);
}



// Resume the service from a paused state
void ResumeService()
{
    DebugMessage(L"Dynamic Cache Service is resuming.\n");
	
    // Set the event so that we are no longer paused
    SetEvent(g_hPauseService);
}



// Pauses the service
void PauseService()
{
    DebugMessage(L"Dynamic Cache Service is pausing.\n");
    
    // Clear the event to pause the service
    ResetEvent(g_hPauseService);
    
    // Issue a wake-up event so that we don't do any more work and immediately pause the service
    SetEvent(g_hEvents[INTERNAL_WAKEUP]);
}



// Stops the service by allowing the main function to complete
void StopService()
{
    DebugMessage(L"Dynamic Cache Service is stopping because the Service Control Manager requested it.\n");
	
    // Set the event so that we are no longer paused
    SetEvent(g_hPauseService);

    // Set the flag that we are shutting down
    g_bServiceShutdown = TRUE;
    
    // Issue a wake-up event so that we don't do any more work and immediately exit
    SetEvent(g_hEvents[INTERNAL_WAKEUP]);
}



// Update the Service Control Manager with our status
BOOL SendStatusToSCM( DWORD dwCurrentState,     // Current state to report
					  DWORD dwWin32ExitCode,    // Win32 exit code to report
					  DWORD dwServiceSpecificExitCode,  // Service exit code to report
					  DWORD dwCheckPoint,       // Checkpoint number to report back to the Service Control Manager
					  DWORD dwWaitHint)         // Wait hint for the Service Control Manager
{
	BOOL bSuccess = TRUE;
	SERVICE_STATUS ServiceStatus;

	// Fill in all the SERVICE_STATUS fields
	ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	ServiceStatus.dwCurrentState = dwCurrentState;

	// If we are in transition then accept no control events, else accept anything
	if ((dwCurrentState == SERVICE_START_PENDING)  || (dwCurrentState == SERVICE_PAUSE_PENDING))
		ServiceStatus.dwControlsAccepted = 0;
	else
		ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;

	// Set the service's Win32 exit code
	if (dwServiceSpecificExitCode == 0)
		ServiceStatus.dwWin32ExitCode = dwWin32ExitCode;
	else
		ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;

    // Set the service's exit code
	ServiceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;

    // Set the checkpoint and wait hint
	ServiceStatus.dwCheckPoint = dwCheckPoint;
	ServiceStatus.dwWaitHint = dwWaitHint;

	// Send the status record to the Service Control Manager
	bSuccess = SetServiceStatus (g_ServiceStatusHandle, &ServiceStatus);

	if (!bSuccess)
    {
        DebugMessage(L"SetServiceStatus failed!\n");
        DebugError(GetLastError());
		StopService();
    }

	return bSuccess;
}



// Main function for this process
__cdecl wmain(void)
{
    DWORD dwStatus = ERROR_SUCCESS;
    
	SERVICE_TABLE_ENTRY ServiceTable[] =
	{
        // Define the service's name and its main function
		{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) DynCacheMain},
		{ NULL, NULL}
	};

	// Register with the Service Control Manager
	if (!StartServiceCtrlDispatcher(ServiceTable))
    {
        DebugMessage(L"StartServiceCtrlDispatcher failed!\n");
        DebugError(GetLastError());
    }
}