// ClientComplianceMain.cpp 
// Console application
// Sample c++ application that statically links against ClientComplianceLib

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifdef _WIN32
#include <windows.h>
#include <iostream>
#include <comdef.h>
#define sleep(x) Sleep(x*1000)
#else
#include <unistd.h>
#endif

#include "ClientCompliance.h"

void PrintUsageMessage()
{
	const char *progName = "ClientCompliance";
	printf("Usage: \n");
	printf("       %s -d\n", progName);	// print diagnostic information only
	printf("       %s -c <url> <doc> [<numeric flags>]\n", progName);
	printf("       %s -x <url> <doc> <output xml full path> [<numeric flags>]\n", progName);
	printf("  where:\n");
	printf("    <url> is a site gather url the BESClient is subscribed.\n");
	printf("    <doc> is the name of the compliance document to process.\n");
	printf("    <output xml full path> is the full path of where the response XML will be output.\n");
	printf("  sample:\n");
	printf("    %s -c http://myserver:52311/cgi-bin/bfgather.exe/actionsite ComplianceDoc.xml\n", progName);
	printf("  note:\n");
	printf("    in this example <doc> would be in the __BESData\\actionsite\\__Compliance folder.\n");
	printf("    flags = a number from 0 to %d (the sum of the following values).\n", COMPLIANCE_FLAG_GATHER + COMPLIANCE_FLAG_EVALUATE_ALL + COMPLIANCE_FLAG_IDLE_CPU_USAGE + COMPLIANCE_FLAG_ACTIONHINT );
	printf("          = 1 - COMPLIANCE_FLAG_GATHER         - gather all sites.\n");
	printf("          = 2 - COMPLIANCE_FLAG_EVALUATE_ALL   - evaluate all fixlets.\n");
	printf("          = 4 - COMPLIANCE_FLAG_IDLE_CPU_USAGE - use IDLE cpu resource level.\n");
	printf("          = 8 - COMPLIANCE_FLAG_ACTIONHINT     - treat <doc> arg as action id hint.\n");
}

int ProcessRequest( COMPSTR siteURL, COMPSTR complianceDocument, unsigned int flag, bool copyResponseXMLToDest = false, const COMPSTR& responseXMLDestination = COMPSTR() )
{

	int openError = COMPLIANCE_Open( siteURL, complianceDocument, flag );

	if( openError != 0 )
	{
#ifdef _WIN32
		std::cout << "COMPLIANCE_Open returned error: " << openError << std::endl;
#else
		printf( "COMPLIANCE_Open returned error: %d.\n", openError );
#endif
	}
	else
	{
		// If open succeeds, make sure to call COMPLIANCE_Close
		// to free up resources, but not until you are done looking at results.

		int progress;
		int error;

		do
		{
			unsigned int progressPercent;

			progress = COMPLIANCE_Progress( &progressPercent, &error );

			if( progress == COMPLIANCE_PROGRESS_BUSY )
			{
#ifdef _WIN32
				std::cout << "COMPLIANCE_Progress " << progressPercent << "% completed." << std::endl;
#else
				printf( "COMPLIANCE_Progress %d%% completed.\n", progressPercent );
#endif
				sleep(1);
			}

		} while ( progress == COMPLIANCE_PROGRESS_BUSY );


		if( progress == COMPLIANCE_PROGRESS_COMPLETE )
		{
			if ( copyResponseXMLToDest )
				COMPLIANCE_CopyResponseXMLTo( responseXMLDestination );

			int count = COMPLIANCE_ResultCount();

#ifdef _WIN32
					std::cout << "COMPLIANCE_ResultCount is " << count << std::endl;
#else
					printf("COMPLIANCE_ResultCount is %d.\n", count);
#endif

			for( int index=0; index< count ;index++ )
			{
				COMPSTR designator;  // designator of value found
				COMPSTR result;      // result of evaluating relevance
				COMPSTR resultType;
				COMPSTR evalTime; 
				COMPSTR description; // description accompanying designator
				COMPSTR comment;     // comment accompanying designator

				const int returnCode = COMPLIANCE_IndexedValue(
				    index, &designator, &result, &resultType, &evalTime, &description, &comment );

				if ( returnCode == 0 )
				{
#ifdef _WIN32
					std::cout << "    Designator: " << designator << std::endl;
					std::cout << "        Result: " << result << std::endl;
					std::cout << "   Result Type: " << resultType << std::endl;
					std::cout << "Eval Time (µs): " << evalTime << std::endl;
					std::cout << "          Desc: " << description << std::endl;
					std::cout << "       Comment: " << comment << std::endl;
					std::cout << std::endl;
#else
					printf( "    Designator: %s\n", designator.c_str() );
					printf( "        Result: %s\n", result.c_str() );
					printf( "   Result Type: %s\n", resultType.c_str() );
					printf( "Eval Time (micros): %s\n", evalTime.c_str() );
					printf( "          Desc: %s\n", description.c_str() );
					printf( "       Comment: %s\n", comment.c_str() );
					printf( "\n" );
#endif
				}
				else if ( returnCode == COMPLIANCE_ERROR_DOC_NOT_UTF8 )
				{
					COMPLIANCE_Close();
					return COMPLIANCE_ERROR_DOC_NOT_UTF8;
				}
			}
		}
		else if( progress == COMPLIANCE_PROGRESS_ERROR )
		{
			if( error == COMPLIANCE_ERROR_CHANGING_CONDITIONS )
#ifdef _WIN32
				std::cout << "Client reports not available right now." << std::endl;
#else
				printf( "Client reports not available right now.\n" );
#endif
			else
#ifdef _WIN32
				std::cout << "COMPLIANCE_Progress returned error " << error << "." << std::endl;
#else
				printf( "COMPLIANCE_Progress returned error %d.\n", error );
#endif
		}

		COMPLIANCE_Close();

	}
	return 0;
}

static COMPSTR CharStarToCOMPSTR( const char* const charStar )
{
#ifdef _WIN32
	COMPSTR compStr = _com_util::ConvertStringToBSTR( charStar );
#else
	COMPSTR compStr = charStar;
#endif

	return compStr;
}

static bool CmdLineArgsAllowForOptionalFlags( const int argc, const char* const argv[], const int posOfFlagsArg , const char* const firstArgFlag )
{
	const bool thereAreTheRightNumberOfArguments = ( argc == posOfFlagsArg || argc == (posOfFlagsArg + 1) );
	const bool theFirstArgumentIsTheCorrectFlag = !strcmp( argv[1], firstArgFlag );

	return thereAreTheRightNumberOfArguments && theFirstArgumentIsTheCorrectFlag;
}

enum ReturnCode
{
    ErrorRetCode=-1,
    NoError=0,
    PrintUsage
};

static const char* const GetNextArg( int& nextArgPos, const char* const argv[] )
{
	const char* const nextArg = argv[nextArgPos];
	nextArgPos++;
	return nextArg;
}

static ReturnCode ProceedUsingCmdLinePossiblyWithXMLPath( const int argc, const char* const argv[], const bool getResponseXMLOutputFullPathFromCmdLineArgs )
{
	int nextArgPos = 2;

	const COMPSTR siteURL = CharStarToCOMPSTR( GetNextArg( nextArgPos, argv ) );
	const COMPSTR complianceDocument = CharStarToCOMPSTR( GetNextArg( nextArgPos, argv ) );
	COMPSTR responseXMLOutputFullPath;
	if ( getResponseXMLOutputFullPathFromCmdLineArgs )
		responseXMLOutputFullPath = CharStarToCOMPSTR( GetNextArg( nextArgPos, argv ) );

	unsigned int flags = 0;
	if( argc == (nextArgPos + 1) )
		flags = atoi( GetNextArg( nextArgPos, argv ) );
	if( flags <= (
		COMPLIANCE_FLAG_GATHER         | 
		COMPLIANCE_FLAG_EVALUATE_ALL   | 
		COMPLIANCE_FLAG_IDLE_CPU_USAGE |
		COMPLIANCE_FLAG_ACTIONHINT      ) 
		)
	{
		const int retCode = ProcessRequest( siteURL, complianceDocument, flags, getResponseXMLOutputFullPathFromCmdLineArgs, responseXMLOutputFullPath );
		if ( retCode == COMPLIANCE_ERROR_DOC_NOT_UTF8 )
		{
			fprintf( stderr, "Error: XML is not valid UTF-8.\n" );
			return ErrorRetCode;
		}

		return NoError;
	}

	return PrintUsage;
}

int main(int argc, char* argv[])
{
	if( argc > 1 )
	{
		if( argc == 2 && !strcmp(argv[1], "-d") )
		{
			ProcessRequest( COMPSTR(), COMPSTR(), 0 ); // Diagnostic  request
			return 0;
		}
		else if( CmdLineArgsAllowForOptionalFlags( argc, argv, 4, "-c" ) )
		{
			const ReturnCode retCode = ProceedUsingCmdLinePossiblyWithXMLPath( argc, argv, false );
			if ( retCode != PrintUsage )
				return retCode;
		}
		else if( CmdLineArgsAllowForOptionalFlags( argc, argv, 5, "-x" ) )
		{
			const ReturnCode retCode = ProceedUsingCmdLinePossiblyWithXMLPath( argc, argv, true );
			if ( retCode != PrintUsage )
				return retCode;
		}
	}
	// usage message
	PrintUsageMessage();
	return -1;

}

