/* ***** BEGIN LICENSE BLOCK ***** 
 * Version: RCSL 1.0/RPSL 1.0 
 *  
 * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
 *      
 * The contents of this file, and the files included with this file, are 
 * subject to the current version of the RealNetworks Public Source License 
 * Version 1.0 (the "RPSL") available at 
 * http://www.helixcommunity.org/content/rpsl unless you have licensed 
 * the file under the RealNetworks Community Source License Version 1.0 
 * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
 * in which case the RCSL will apply. You may also obtain the license terms 
 * directly from RealNetworks.  You may not use this file except in 
 * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
 * applicable to this file, the RCSL.  Please see the applicable RPSL or 
 * RCSL for the rights, obligations and limitations governing use of the 
 * contents of the file.  
 *  
 * This file is part of the Helix DNA Technology. RealNetworks is the 
 * developer of the Original Code and owns the copyrights in the portions 
 * it created. 
 *  
 * This file, and the files included with this file, is distributed and made 
 * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
 * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
 * 
 * Technology Compatibility Kit Test Suite(s) Location: 
 *    http://www.helixcommunity.org/content/tck 
 * 
 * Contributor(s): 
 *  
 * ***** END LICENSE BLOCK ***** */

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

#include "hxtypes.h"
#include "hxcom.h"
#include "hxresult.h"
#include "ihxtevnts.h"
#include "setdllac.h"

#ifdef _WINDOWS
#include <direct.h>
#else
#include <unistd.h>
#include <dlfcn.h>
#endif

#ifdef _MACINTOSH
#include "macstuff.h"
#endif

#include "crmevapp.h"

const UINT16 MIN_RMEVENTS_ARGS = 2;
const UINT32 MAX_STRING_SIZE = 65535;
const UINT16 MAX_ERR_STRING_SIZE = 2048;
const UINT16 MAX_LOG_STRING_SIZE = 256;

static char gLogString[MAX_LOG_STRING_SIZE];
const UINT32 MAX_BUFFER_SIZE = 255;


#ifdef _WIN32
#define OS_SEPARATOR_STRING	"\\"
#elif defined _MACINTOSH
#define OS_SEPARATOR_STRING	":"
#endif


CRMEventsApp::CRMEventsApp()
{
	m_pEvents = NULL;
	m_bHasInputFile = FALSE;
	m_bHasOutputFile = FALSE;
	m_bHasDumpFile = FALSE;
	m_bUseLogFile = FALSE;
	m_bUsageOnly = FALSE;
	m_RmtoolsDLL = 0;
}

CRMEventsApp::~CRMEventsApp()
{
	if(m_pEvents)
	{
		HX_RELEASE(m_pEvents);
		m_pEvents = NULL;
	}

#ifdef _WINDOWS
	if (m_RmtoolsDLL)
		::FreeLibrary(m_RmtoolsDLL);
#else
	if (m_RmtoolsDLL)
		dlclose(m_RmtoolsDLL);
#endif
}

HX_RESULT CRMEventsApp::CreateEventsSDK(void)
{
	IUnknown* pUnk = NULL;
	HX_RESULT res = HXR_OK;

	if(m_pEvents)
	{
		HX_RELEASE(m_pEvents);
		m_pEvents = NULL;
	}

	// This sample app must be run from the %sdk_install_dir%\bin directory.  Applications using the
	// SDK can be run from anywhere as long as SetDLLAccessPaths is called with the location of
	// the SDK binaries.
	char szCurrentDir[MAX_BUFFER_SIZE] = "";
#ifdef _WINDOWS	
	_getcwd(szCurrentDir, MAX_BUFFER_SIZE);
#else
	getcwd(szCurrentDir, MAX_BUFFER_SIZE);
#endif

	if (strstr(szCurrentDir, "bin") == NULL)
	{
		printf("\n*** Sample app must be run from %sdk_install_dir%\\bin directory ***\n");
		res = HXR_FAIL;
	}

    // Create a null delimited, double-null terminated string containing DLL category
    // name/path pairs.  The default structure of the SDK has discrete subdirectories for each DLL category.  If
	// you wish, all DLLs can be placed within a single directory.  The path passed to SetDLLAccessPath simply
	// needs to reflect this.
	char szDllPath[2048] = "";
	if (SUCCEEDED(res))
	{   
		memset(szDllPath, 0, 2048);
		UINT32 ulNumChars = 0;
		
		// Assume the current working directory is %sdk_install_dir%\bin -- this would normally be set to an absolute path
#ifdef _WINDOWS	
		const char* pCurrentDir = ".\\";
#else
		const char* pCurrentDir = "./";
#endif
		
		ulNumChars += sprintf(szDllPath+ulNumChars, "%s=%s%s", "DT_Plugins", pCurrentDir, "plugins") + 1;
		ulNumChars += sprintf(szDllPath+ulNumChars, "%s=%s%s", "DT_Codecs", pCurrentDir, "codecs") + 1;
		ulNumChars += sprintf(szDllPath+ulNumChars, "%s=%s%s", "DT_EncSDK", pCurrentDir, "tools") + 1;
		ulNumChars += sprintf(szDllPath+ulNumChars, "%s=%s%s", "DT_Common", pCurrentDir, "common") + 1;
	}
	
    // Load the rmtools binary (DLL/so) to get the SetDLLAccessPath entry point
	if (SUCCEEDED(res))
	{		
#ifdef _WINDOWS
		m_RmtoolsDLL = ::LoadLibrary(".\\tools\\rmto3260.dll");
#else
		m_RmtoolsDLL = dlopen("./tools/rmtools.so.6.0", RTLD_LAZY);
#endif
		if (m_RmtoolsDLL)
		{
			// Get the SetDLLAccessPath entry point
#ifdef _WINDOWS
			FPRMBUILDSETDLLACCESSPATH fpSetDllAccessPath = (FPRMBUILDSETDLLACCESSPATH)(::GetProcAddress(m_RmtoolsDLL, "SetDLLAccessPath"));		
#else
			FPRMBUILDSETDLLACCESSPATH fpSetDllAccessPath = (FPRMBUILDSETDLLACCESSPATH)(dlsym(m_RmtoolsDLL, "SetDLLAccessPath"));
#endif
			// Set the DLL access paths
			if (fpSetDllAccessPath)
			{
				res = (*fpSetDllAccessPath)(szDllPath);
			}
			else
			{
				res = HXR_FAIL;
			}
		}
		else
		{
			res = HXR_FAIL;
		}
	}

	// Get the RMACreateRMJobFactory entry point from the rmsesion binary
	if (SUCCEEDED(res))
	{		
#ifdef _WINDOWS
		FPCREATEINSTANCE fpCreateEdit = (FPCREATEINSTANCE)(::GetProcAddress(m_RmtoolsDLL, "RMACreateRMEvents"));
#else
		FPCREATEINSTANCE fpCreateEdit = (FPCREATEINSTANCE)(dlsym(m_RmtoolsDLL, "RMACreateRMEvents"));
#endif
		// Create IHXRMEvents instance
		if (fpCreateEdit)
		{
			res = (*fpCreateEdit)(&pUnk);
		}
		else
		{
			res = HXR_FAIL;
		}
	}

	// Get the interface to rmevents
	if (SUCCEEDED(res))
	{
		res = pUnk->QueryInterface(IID_IHXRMEvents,(void**)&m_pEvents);
		HX_RELEASE(pUnk); // don't need the IUnknown anymore
	}

	return res;
}


HX_RESULT CRMEventsApp::Initialize(UINT16 argc, const char ** argv)
{
	HX_RESULT res = HXR_OK;
	const char *arg;
	UINT16 i;
	BOOL bFound = FALSE;

	res = CreateEventsSDK();

	if(SUCCEEDED(res))
	{
		Log("\n");
		if(argc < MIN_RMEVENTS_ARGS)
		{
			if(argc == 1)
			{
				Usage();
				return HXR_OK;
			}
			else
			{
				res = HXR_INVALID_PARAMETER;
			}
		}
	}

	// check if we need to open a log file
	bFound = FALSE;
	for(i = 1; i < argc && res == HXR_OK && !bFound; i++)
	{
		arg = argv[i];

		if(arg[0] == '-' && i < (argc - 1))
		{
			if(arg[1] == 'l') // check for the log option
			{
				i++;
				res = m_pEvents->OpenLogFile(argv[i]); // open the requested log file
				m_bUseLogFile = res == HXR_OK;
				sprintf(gLogString,"Log File: %s\n",argv[i]);
				Log(gLogString);
				bFound = TRUE;
			}
		}
	}		

	for(i = 1; i < argc && res == HXR_OK; i++)
	{
		arg = argv[i];
		
		// handle the command line option and check if the user entered a option value
		if(arg[0] == '-')
		{
			// make sure we have enough arguments for the command
			if(arg[1] != '?' && i == argc - 1)
			{
				res = HXR_INVALID_PARAMETER;
			}

			if(SUCCEEDED(res))
			{
				switch(arg[1])
				{
				case 'i':
					i++;
					res = m_pEvents->SetInputFile(argv[i]);
					sprintf(gLogString,"Input File: %s\n",argv[i]);
					Log(gLogString);
					m_bHasInputFile = (res == HXR_OK);
					break;

				case 'o':
					i++;
					res = m_pEvents->SetOutputFile(argv[i]);
					sprintf(gLogString,"Output File: %s\n",argv[i]);
					Log(gLogString);
					m_bHasOutputFile = (res == HXR_OK);
					break;

				case 'e':
					i++;
					res = m_pEvents->SetEventFile(argv[i]);
					sprintf(gLogString,"Event File: %s\n",argv[i]);
					Log(gLogString);
					break;

				case 'm':
					i++;
					res = m_pEvents->SetImageMapFile(argv[i]);
					sprintf(gLogString,"Image Map File: %s\n",argv[i]);
					Log(gLogString);
					break;

				case 'd':
					i++;
					res = m_pEvents->SetDumpFile(argv[i]);
					sprintf(gLogString,"Dump File: %s\n",argv[i]);
					Log(gLogString);
					m_bHasDumpFile = (res == HXR_OK);
					break;

				case 'l':
					i++; // skip over the -l options since we already handled them above
					break;

				case '?':
					i++;
					m_bUsageOnly = TRUE;
					break;

				default:
					res = HXR_INVALID_PARAMETER;
					break;
				}
			}
		}
		else
		{
			res = HXR_INVALID_PARAMETER;
		}
	}

	// print out the usage text if an error occurred
	if(res != HXR_OK)
	{
		Usage();
		HandleSDKError(res);
	}

	return res;
}
 
HX_RESULT CRMEventsApp::Process(void)
{
	HX_RESULT res = HXR_OK;

	if(m_bUsageOnly)
	{
		Usage();
		return HXR_OK;
	}

	if(!m_pEvents)
	{
		res = HXR_NOT_INITIALIZED;
	}
	
	if(SUCCEEDED(res) && m_bHasInputFile)
	{
		if(m_bHasOutputFile || m_bHasDumpFile)	
		{
			Log("\nProcessing...\n");

			res = m_pEvents->Process();

			if(SUCCEEDED(res))
			{			
				Log("Processing complete!\n");
			}
		}
		else
		{
			printf("\nError: Please specify an outfile or dump file\n\n");
			Usage();
		}
	}

	if(!SUCCEEDED(res))
	{
		HandleSDKError(res);
	}

	Log("***********************************************\n");

	return res;
}

void CRMEventsApp::Usage(void)
{
	fprintf(stdout, "Usage:\n");

    fprintf(stdout, "\trmevents -i <input> -o <output> -e <events> -m <imagemaps> -d <dump file>\n");

	fprintf(stdout,"\nWhere:\n");
    fprintf(stdout,"input\t- the path to the input file.\n");
    fprintf(stdout,"output\t- the path to the output file that will contain the output file.\n");
    fprintf(stdout,"events\t- the path to the text file containing the events to be added to the output file.\n");
    fprintf(stdout,"imagemaps - the path to the text file containing the image maps to be added to the output file.\n");
	fprintf(stdout,"dump file- the path to the dump file to hold the image maps and events dumped from the input file.\n");
	fprintf(stdout,"log file - the path to the log file.\n");

	fprintf(stdout,"\nTips:\n");
	fprintf(stdout,"Enter rmevents -d <dump file> to dump the events and image maps from the input file. \n");
}

void CRMEventsApp::HandleSDKError(HX_RESULT theRes)
{
	char errString[MAX_ERR_STRING_SIZE];

	if(theRes != HXR_OK)
	{
		if(m_pEvents)
		{
			HX_RESULT res = m_pEvents->GetErrorString(theRes, errString,MAX_ERR_STRING_SIZE);                   

			if(SUCCEEDED(res))
			{
				sprintf(gLogString,"\nError: %s\n",errString);
				Log(gLogString);
			}
			else
			{
				sprintf(gLogString,"\nError: %ld\n",theRes);
				Log(gLogString);
			}
		}
		else if(theRes == HXR_ENC_INVALID_DLL)
		{
#ifdef _MACINTOSH
			sprintf(gLogString,"\nError: Cannot locate rmtools shared library\n",theRes);
#else
			sprintf(gLogString,"\nError: Cannot locate rmto dll\n",theRes);
#endif
			Log(gLogString);
		}
		else
		{
			sprintf(gLogString,"\nError: %ld\n",theRes);
			Log(gLogString);
		}
	}
}

void CRMEventsApp::Log(const char* pLogString)
{
	if(pLogString)
	{
		fprintf(stdout,"%s",pLogString);

		if(m_bUseLogFile)
		{
			m_pEvents->Log(pLogString);
		}
	}
}
