/* ***** 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 "rmflsnk.h"
#include "ihxtedit.h"
#include "ihxtedit2.h"
#include "ihxtfdump.h"
#include "ihxpckts.h"
#include "rmmetain.h"
#include "setdllac.h"

#include "crmedap.h"

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

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

#define USE_RMFILESINK 0 // set USE_RMFILESINK to 1 in order to activate the RMFileSink

const UINT16 MIN_RMEDIT_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;

// For PICS SafeSurf rating parsing
const char* PICS_RATING_LABEL = "PICS-1.1";
const char* PICS_RATING_URL = "http://www.classify.org/safesurf";
const char* PICS_RATING_TAG = "SS~~000";
const char* PICS_META_FORMAT = "(PICS-1.1 \"http://www.classify.org/safesurf\" labels comment \"%s\" ratings (SS~~000 %lu))";
const int g_aContentRatingMapping[] = {1,2,5,8};


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

CRMEditApp::CRMEditApp()
{
	m_pEdit = NULL;
	m_pEdit2 = NULL;
	m_pDump = NULL;
	m_pFileSink = NULL;
	m_bHasInputFile = FALSE;
	m_bHasOutputFile = FALSE;
	m_bListInputFile = FALSE;
	m_bUseLogFile = FALSE;
	m_ulNumInputFiles = 0;
	m_bUsageOnly = FALSE;
	m_RmtoolsDLL = 0;
}

CRMEditApp::~CRMEditApp()
{
	if(m_pFileSink)
	{
		HX_RELEASE(m_pFileSink);
		m_pFileSink = NULL;
	}

	if(m_pEdit)
	{
		m_pEdit->Release();
		m_pEdit = NULL;
	}

	if(m_pEdit2)
	{
		m_pEdit2->Release();
		m_pEdit2 = NULL;
	}
	
	if(m_pDump)
	{
		m_pDump->Release();
		m_pDump = NULL;
	}

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

}

HX_RESULT CRMEditApp::CreateEditSDK(void)
{
	IUnknown* pUnk = NULL;
	HX_RESULT res = HXR_OK;

	if(m_pEdit)
	{
		m_pEdit->Release();
		m_pEdit = NULL;
	}

	if(m_pEdit2)
	{
		m_pEdit2->Release();
		m_pEdit2 = 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, "RMACreateRMEdit"));
#else
		FPCREATEINSTANCE fpCreateEdit = (FPCREATEINSTANCE)(dlsym(m_RmtoolsDLL, "RMACreateRMEdit"));
#endif
		// Create IHXRMEdit instance
		if (fpCreateEdit)
		{
			res = (*fpCreateEdit)(&pUnk);
		}
		else
		{
			res = HXR_FAIL;
		}
	}
	
	// Get the interface to rmedit
	if (SUCCEEDED(res))
	{
		res = pUnk->QueryInterface(IID_IHXRMEdit,(void**)&m_pEdit);
	}

	// Get the interface to rmedit2
	if (SUCCEEDED(res))
	{
		res = pUnk->QueryInterface(IID_IHXRMEdit2,(void**)&m_pEdit2);
		if(res == HXR_NOINTERFACE) // earlier version of the rmeditor SDK did not support the IID_IHXRMEdit2 interface
		{
			res = HXR_OK;
			m_pEdit2 = NULL;
		}
	}

	HX_RELEASE(pUnk); // don't need the IUnknown anymore

	return res;
}

HX_RESULT CRMEditApp::CreateDumpSDK(void)
{
	IUnknown* pUnk = NULL;
	HX_RESULT res = HXR_OK;

	if(m_pDump)
	{
		m_pDump->Release();
		m_pDump = NULL;
	}

	if (m_RmtoolsDLL)
	{		
#ifdef _WINDOWS
		FPCREATEINSTANCE fpCreateEdit = (FPCREATEINSTANCE)(::GetProcAddress(m_RmtoolsDLL, "RMACreateRMFFDump"));
#else
		FPCREATEINSTANCE fpCreateEdit = (FPCREATEINSTANCE)(dlsym(m_RmtoolsDLL, "RMACreateRMFFDump"));
#endif
		// Create IHXRMDump object
		if (fpCreateEdit)
		{
			res = (*fpCreateEdit)(&pUnk);
		}
		else
		{
			res = HXR_FAIL;
		}
	}

	else
	{
		res = HXR_FAIL;
	}


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

	return res;
}

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

	m_ulNumInputFiles = 0;

	res = CreateEditSDK();

	if(SUCCEEDED(res))
	{
		Log("\n");
		if(argc < MIN_RMEDIT_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_pEdit->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;
			}
		}
	}		

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

		if(arg[0] == '-' && i < (argc - 1))
		{
			if(arg[1] == 'd') // check for the dump option
			{
				i++;
				res = CreateDumpSDK();
				if(SUCCEEDED(res))
				{
					res = m_pDump->SetOutputFile(argv[i]);
					sprintf(gLogString,"Dump File: %s\n",argv[i]);
					Log(gLogString);
					bFound = TRUE;
				}
			}
		}
	}		

	for(i = 1; i < argc && res == HXR_OK; i++)
	{
		arg = argv[i];
		int value;
		BOOL bEnabled;
		
		// 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++;

					// for the first input file we call SetInputFile()
					if(m_ulNumInputFiles == 0)
					{
						res = m_pEdit->SetInputFile(argv[i],TRUE);

						if(m_pDump) // set the input file for rmdump
						{
							m_pDump->SetInputFile(argv[i]);
						}
					}
					else // fpr each additional input file in a paste operation we need to call AddInputFile
					{
						res = m_pEdit->AddInputFile(argv[i]);
					}

					m_ulNumInputFiles++;
					sprintf(gLogString,"Input File %ld: %s\n",m_ulNumInputFiles,argv[i]);
					Log(gLogString);
					m_bListInputFile = m_bHasInputFile = (res == HXR_OK);
					break;

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

				case 't':
					i++;
					res = m_pEdit->SetTitle(argv[i]);
					break;

				case 'a':
					i++;
					res = m_pEdit->SetAuthor(argv[i]);
					break;

				case 'c':
					i++;
					res = m_pEdit->SetCopyright(argv[i]);
					break;

				case 'C':
					i++;
					res = m_pEdit->SetComment(argv[i]);
					break;

				case 'r':
					i++;
					value = atoi(argv[i]);
					bEnabled = value != 0 ? TRUE : FALSE;
					res = m_pEdit->SetSelectiveRecord(bEnabled);
					break;

				case 'k':
					i++;
					value = atoi(argv[i]);
					bEnabled = value != 0 ? TRUE : FALSE;
					res = m_pEdit->SetMobilePlayback(bEnabled);
					break;

				case 's':
					i++;
					sprintf(gLogString,"Start Time: %s\n",argv[i]);
					res = m_pEdit->SetStartTime(argv[i]);
					Log(gLogString);
					break;

				case 'e':
					i++;
					sprintf(gLogString,"End Time: %s\n",argv[i]);
					res = m_pEdit->SetEndTime(argv[i]);
					Log(gLogString);
					break;

				case 'l':
				case 'd':
					i++; // skip over the -l -d options since we already handled them above
					break;
					
				case 'q':	// Metafile description
					i++;
					res = SetMetaInformationString(RM_PROPERTY_DESCRIPTION,argv[i]);
					break;
					
				case 'n':	// Metafile keywords
					i++;
					res = SetMetaInformationString(RM_PROPERTY_KEYWORDS,argv[i]);
					break;

				case 'A':	// Metafile Audience Rating (AR)
                                        if (arg[2] == 'R')
                                        {
                                            i++;
                                            value = atoi(argv[i]);

                                            if (value > 4 || value < 1)
                                                value = 1;

                                            // Form the PICS rating string
                                            char szContentRating[300] = "";
                                            sprintf(szContentRating, PICS_META_FORMAT, "", g_aContentRatingMapping[value-1]);
                                            
                                            res = SetMetaInformationString(RM_PROPERTY_CONTENT_RATING, szContentRating);
                                        }
					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 CRMEditApp::SetMetaInformationString(const char* pName, const char* pValue)
{
	HX_RESULT res = HXR_OK;
	IHXValues* pMetaInfo = NULL;

	if(!m_pEdit2)
	{
		res = HXR_NOTIMPL;
	}

	if(SUCCEEDED(res))
	{
		res = m_pEdit2->GetMetaInformation(&pMetaInfo);
	}
	
	if(SUCCEEDED(res))
	{
		// Set the meta information Name Value Pair
		// Here is how you would set the new value string. 
		if(SUCCEEDED(res))
		{
			IHXBuffer* pBuffer = NULL;
			
			// create a new IHXBuffer to hold the new string
			res = m_pEdit->CreateIRMABuffer(&pBuffer);

			if(SUCCEEDED(res))
			{
				pBuffer->AddRef();
	 			pBuffer->Set((UINT8*)pValue, strlen(pValue) + 1);
 				res = pMetaInfo->SetPropertyCString(pName,pBuffer);
			}
			
			HX_RELEASE(pBuffer);
		}
	}
	
	HX_RELEASE(pMetaInfo);
	
	return res;
}

HX_RESULT CRMEditApp::SetMetaInformationInteger(const char* pName, UINT32 value)
{
	HX_RESULT res = HXR_OK;
	IHXValues* pMetaInfo = NULL;

	if(!m_pEdit2)
	{
		res = HXR_NOTIMPL;
	}

	if(SUCCEEDED(res))
	{
		res = m_pEdit2->GetMetaInformation(&pMetaInfo);
	}
	
	if(SUCCEEDED(res))
	{
		// Set the meta information Name Value Pair
		// Here is how you would set the new integer. 
		if(SUCCEEDED(res))
		{
 			res = pMetaInfo->SetPropertyULONG32(pName,value);
		}
	}
	
	HX_RELEASE(pMetaInfo);
	
	return res;
}
HX_RESULT CRMEditApp::Process(void)
{
	HX_RESULT res = HXR_OK;

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

	if(!m_pEdit)
	{
		res = HXR_NOT_INITIALIZED;
	}
	
	if(SUCCEEDED(res) && m_bHasInputFile && m_bHasOutputFile)
	{
#if USE_RMFILESINK  // set USE_RMFILESINK to 1 in order to activate the RMFileSink
		if(m_pFileSink)
		{
			HX_RELEASE(m_pFileSink);
			m_pFileSink = NULL;
		}
		
		m_pFileSink = new CRMFileSink(this);

		if(!m_pFileSink)
		{
			res = HXR_OUTOFMEMORY;
		}

		if(SUCCEEDED(res))
		{
			m_pFileSink->AddRef();
			res = m_pEdit->SetRMFileSink(m_pFileSink);
		}
#endif
		if(SUCCEEDED(res))
		{
			Log("\nProcessing...\n");

			res = m_pEdit->Process();

			if(SUCCEEDED(res))
			{			
				Log("Processing complete!\n");
				Log("\nResults:\n");
			}
		}
	}

	if(SUCCEEDED(res) && m_bListInputFile)
	{			
		res = DisplayEditInfo();
	}

	if(SUCCEEDED(res) && m_pDump)
	{
		Log("\nDumping input file...\n");
		res = m_pDump->Process();
		Log("\nDump complete!\n");
	}
	
	if(!SUCCEEDED(res))
	{
		HandleSDKError(res);
	}

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

	return res;
}

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

    fprintf(stdout, "\trmeditor -i <input> -o <output> [-t <title>] [-a <author>] [-c <copyright>]\n\t[-C <comment>] [-r <allow recording>] [-k <allow download>]\n\t[-s <start time>] [-e <end time>] [-l <log file>] [-d <dump file>] \n\t[-q <description>] [-n <keywords>] [-IN <allowing indexing>] [-AR <audience rating>]\n" );

	fprintf(stdout,"\nWhere:\n");
    fprintf(stdout,"\tinput\t\t\t- the path to the input file.\n");
    fprintf(stdout,"\toutput\t\t\t- the path to the output file that will contain the edited file.\n");
    fprintf(stdout,"\ttitle\t\t\t- title text.\n");
    fprintf(stdout,"\tauthor\t\t\t- author text.\n");
    fprintf(stdout,"\tcopyright\t\t- copyright text.\n");
    fprintf(stdout,"\tcomment\t\t\t- comment text.\n");
    fprintf(stdout,"\tallow recording\t\t- 1 = On, 0 = Off\n");
    fprintf(stdout,"\tallow download\t\t- 1 = On, 0 = Off\n");
	fprintf(stdout,"\tstart time\t\t- days:hours:minutes:seconds.milliseconds.\n");
    fprintf(stdout,"\tend time\t\t- days:hours:minutes:seconds.milliseconds.\n");
	fprintf(stdout,"\tlog file\t\t- the path to the log file.\n");
	fprintf(stdout,"\tdump file\t\t- the path to the dump file.\n");
	fprintf(stdout,"\tdescription\t\t- a description of the contents of the file.\n");
	fprintf(stdout,"\tkeywords\t\t- a list of keywords for the file.\n");
    fprintf(stdout,"\tallow indexing\t\t- 1 = On, 0 = Off\n");
    fprintf(stdout,"\taudience rating\t\t- 1 = General, 2 = Parental Guidance Recommended, 3 = Adult Supervision Required, 4 = Adults Only\n");
	fprintf(stdout,"\nTips:\n");
	fprintf(stdout,"\tEnter rmeditor -i <input> to view the contents of the input file.\n");
	fprintf(stdout,"\tEnter rmeditor -i <input1> -i <input2>... to paste input files.\n");
	fprintf(stdout,"\tUse 0 as the end time to specify the end of the file.\n");
	fprintf(stdout,"\tPlace \" \" around the path to your file if any directory or file name contains a space\n");
#ifdef _MACINTOSH
	fprintf(stdout,"\t\tFor example enter -i \"My Hard Drive:foo.rm\"\n");
#endif
}


HX_RESULT CRMEditApp::DisplayEditInfo(void)
{
	HX_RESULT res = HXR_OK;
	char* pTempBuf = NULL;
	BOOL bEnabled;
	
	if(!m_pEdit)
	{
		res = HXR_NOT_INITIALIZED;
	}
	
	if(SUCCEEDED(res))
	{
		pTempBuf = new char[MAX_STRING_SIZE];
		if(!pTempBuf)
		{
			res = HXR_OUTOFMEMORY;
		}
	}
	
	if(SUCCEEDED(res)) // print the title
	{
		res = m_pEdit->GetTitle(pTempBuf,MAX_STRING_SIZE);
		if(res == HXR_OK)
		{
			sprintf(gLogString,"Title: %s\n",pTempBuf);
			Log(gLogString);
		}
	}

	if(SUCCEEDED(res)) // print the author
	{
		res = m_pEdit->GetAuthor(pTempBuf,MAX_STRING_SIZE);
		if(res == HXR_OK)
		{
			sprintf(gLogString,"Author: %s\n",pTempBuf);
			Log(gLogString);
		}
	}

	if(SUCCEEDED(res)) // print the copyright
	{
		res = m_pEdit->GetCopyright(pTempBuf,MAX_STRING_SIZE);
		if(res == HXR_OK)
		{
			sprintf(gLogString,"Copyright: %s\n",pTempBuf);
			Log(gLogString);
		}
	}

	if(SUCCEEDED(res)) // print the comment
	{
		res = m_pEdit->GetComment(pTempBuf,MAX_STRING_SIZE);
		if(res == HXR_OK)
		{
			sprintf(gLogString,"Comment: %s\n",pTempBuf);
			Log(gLogString);
		}
	}
	
	// now print the property flags
	if(SUCCEEDED(res))
	{
		res = m_pEdit->GetSelectiveRecord(&bEnabled);
		if(res == HXR_OK)
		{
			sprintf(gLogString,"Allow Recording: %s\n",bEnabled?"On":"Off");
			Log(gLogString);
		}
	}

	if(SUCCEEDED(res))
	{
		res = m_pEdit->GetMobilePlayback(&bEnabled);
		if(res == HXR_OK)
		{
			sprintf(gLogString,"Allow Download: %s\n",bEnabled?"On":"Off");
			Log(gLogString);
		}
	}

#if 0
	if(SUCCEEDED(res))
	{
		res = m_pEdit->GetPerfectPlay(&bEnabled);
		if(res == HXR_OK)
		{
			fprintf(stdout,"Perfect Play: %s\n",bEnabled?"On":"Off");
		}
	}
#endif

	// Note: Earlier versions of the rmeditor SDK did not support the IID_IHXRMEdit2 interface.
	if(m_pEdit2)
	{
		BOOL bHasMedia;
		
		res = m_pEdit2->HasAudio(&bHasMedia);
		
		if(SUCCEEDED(res))
		{
			fprintf(stdout,"Audio: %s\n",bHasMedia?"Yes":"No");
		}
		
		res = m_pEdit2->HasVideo(&bHasMedia);
		
		if(SUCCEEDED(res))
		{
			fprintf(stdout,"Video: %s\n",bHasMedia?"Yes":"No");
			if(bHasMedia)
			{
				UINT16 height, width;
				res = m_pEdit2->GetVideoSize(&height,&width);
				if(SUCCEEDED(res))
				{
					fprintf(stdout,"\twidth: %hu\n",width);
					fprintf(stdout,"\theigth: %hu\n",height);
				}
			}
		}
		
		res = m_pEdit2->HasEvents(&bHasMedia);
		
		if(SUCCEEDED(res))
		{
			fprintf(stdout,"Events: %s\n",bHasMedia?"Yes":"No");
		}
		
		res = m_pEdit2->HasImageMaps(&bHasMedia);
		
		if(SUCCEEDED(res))
		{
			fprintf(stdout,"Image Maps: %s\n",bHasMedia?"Yes":"No");
		}
	}
	
	if(SUCCEEDED(res))
	{
		res = DisplayMetaInformation();
	}

	if(pTempBuf)
	{
		delete [] pTempBuf;
	}
	
	return res;
}

/************************************************************************
 *	Method:
 *	    DisplayMetaInformation
 *	Purpose:
 *	    prints to the standard console the contents of the Meta information
 *		in the input file reflecting any changes made to it by the user.
 *
 *	Parameters:
 *		none
 */
HX_RESULT CRMEditApp::DisplayMetaInformation(void)
{
	HX_RESULT res = HXR_OK;
    const char* pPropName = NULL;
    UINT32 propValue = 0;
    IHXBuffer* pPropBuffer = NULL;
	IHXValues* pMetaInfo = NULL;
 	
	if(!m_pEdit2)
	{
		return HXR_OK;
	}
	
	// get the IHXValues object that contains the meta information
	res = m_pEdit2->GetMetaInformation(&pMetaInfo);
	
	if(SUCCEEDED(res) && pMetaInfo)
	{
		pMetaInfo->AddRef();
		
	    // print all of the Integers name/value pairs
	    HX_RESULT rc = pMetaInfo->GetFirstPropertyULONG32(pPropName, propValue);
	    while(rc == HXR_OK)
	    {
			fprintf(stdout,"%s: %ld\n",pPropName,propValue);
			rc = pMetaInfo->GetNextPropertyULONG32(pPropName, propValue);
	    }
	    
	    // do all of the c string name/vlaue pairs
	    rc = pMetaInfo->GetFirstPropertyCString(pPropName, pPropBuffer);
	    while(rc == HXR_OK && pPropBuffer != NULL)
	    {
			fprintf(stdout,"%s: %s\n",pPropName,(char*)pPropBuffer->GetBuffer());
			HX_RELEASE(pPropBuffer);
			rc = pMetaInfo->GetNextPropertyCString(pPropName, pPropBuffer);
	    }
    }
    
    HX_RELEASE(pMetaInfo);

	return res;
}


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

	if(theRes != HXR_OK)
	{
		if(m_pEdit)
		{
			HX_RESULT res = m_pEdit->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 CRMEditApp::Log(const char* pLogString)
{
	if(pLogString)
	{
		fprintf(stdout,"%s",pLogString);

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


CRMFileSink::CRMFileSink(CRMEditApp* pOwnerApp)
{
	m_pOwnerApp = pOwnerApp;	// allows access to the owner's log function
	m_lRefCount = 0;
}

CRMFileSink::~CRMFileSink()
{

}

/****************************************************************************
 *  CRMFileSink::OnMediaPropertyHeader                        ref:  ihxtedit.h                 
 *
 *  The OnPacket() method is called everytime a Media Property header is about to be 
 *  written to a .rm file. You may modify the mime type string the data packet and encrypt
 *  the type specific data of the header. You must not modify any other fields of the Media
 *  Property header.
 *
 */

STDMETHODIMP CRMFileSink::OnMediaPropertyHeader(IHXValues* pValues)
{
	HX_RESULT res = HXR_OK;
	IHXBuffer* pBuffer = NULL;
	ULONG32 ulDataSize;

	// get pointer to RMEditor SDK
	IHXRMEdit* pEdit =	m_pOwnerApp->GetEditSDK();

	// get the header stream number
	UINT32 ulStreamNum;
	res = pValues->GetPropertyULONG32("HX_MEDIA_PROP_STREAM_NUMBER",ulStreamNum);
	if(SUCCEEDED(res))
	{
		m_pOwnerApp->Log("Media Property Header\n");
		sprintf(gLogString,"stream number: %lu\n",ulStreamNum);
		m_pOwnerApp->Log(gLogString);
	}

	if(SUCCEEDED(res)) 	// get the mimetype
	{
		char* pTempStr = NULL;
		pBuffer = 0;
		res = pValues->GetPropertyCString("HX_MEDIA_PROP_MIMETYPE",pBuffer);

		if(SUCCEEDED(res)) // here is how you extract the mimetype string
		{
			ulDataSize = pBuffer->GetSize();

			// allocate a buffer to hold the string
			pTempStr = new char[ulDataSize];
			memcpy(pTempStr,pBuffer->GetBuffer(),ulDataSize);

			// log the string
			sprintf(gLogString,"mimetype:%s\n",pTempStr);
			m_pOwnerApp->Log(gLogString);
		}

		// Here is how you would set the new mimetype string. In this example we
		// just set the same string. You will need to Set() your new mimetype string
		// into pBuffer.
		if(SUCCEEDED(res))
		{
			IHXBuffer* pMimeBuffer = NULL;
			
			// create a new IHXBuffer to hold your modified mimetype string
			res = pEdit->CreateIRMABuffer(&pMimeBuffer);

			if(SUCCEEDED(res))
			{
				pMimeBuffer->AddRef();
	 			pMimeBuffer->Set((UINT8*)pTempStr, strlen(pTempStr) + 1);
 				res = pValues->SetPropertyCString("HX_MEDIA_PROP_MIMETYPE",pMimeBuffer);
			}
			HX_RELEASE(pMimeBuffer);
		}
		if(pTempStr)
		{
			delete [] pTempStr;
			pTempStr = NULL;
		}
	}

	HX_RELEASE(pBuffer); // release the IHXBuffer

	// now get the type specific data. Please note that some media property headers do not contain any type specific data
	if(SUCCEEDED(res))
	{
		pBuffer = 0;
		HX_RESULT typeSpecificRes = pValues->GetPropertyBuffer("HX_MEDIA_PROP_TYPE_SPECIFIC_DATA",pBuffer);
		
		// check the return value from pValues->GetPropertyBuffer() to make sure the type specific data is present!!
		if(SUCCEEDED(typeSpecificRes))
		{
			ULONG32 ulDataSize = pBuffer->GetSize();
			sprintf(gLogString,"type_specific_data size: %lu\n",ulDataSize);
			m_pOwnerApp->Log(gLogString);

			// Here is how you would set the encrypted type specific data. In this example we
			// just set the same type specific data. You will need to Set() your new type specific data
			// into pBuffer.
			UINT8* pTempBuf = new UINT8[ulDataSize];

			// copy the typespecific data into the temporary buffer
			memcpy(pTempBuf,pBuffer->GetBuffer(),ulDataSize);

			IHXBuffer* pDataBuffer = NULL;
			
			// create a new IHXBuffer to hold your modified type specific data
			res = pEdit->CreateIRMABuffer(&pDataBuffer);

			if(SUCCEEDED(res))
			{
				pDataBuffer->AddRef();
				pDataBuffer->Set(pTempBuf, ulDataSize);
 				res = pValues->SetPropertyBuffer("HX_MEDIA_PROP_TYPE_SPECIFIC_DATA",pDataBuffer);
			}
			HX_RELEASE(pDataBuffer);
			delete [] pTempBuf;
		}
		HX_RELEASE(pBuffer);
	}

	m_pOwnerApp->Log("\n");

	return res;
}

/****************************************************************************
 *  CRMFileSink::OnPacket                                     ref:  ihxtedit.h
 *
 *  The OnPacket() method is called everytime a packet is about to be written to
 *  a .rm file. You may encrypt the data packet before it is written to file. You
 *  must not change any other fields in the IRMAPacket. 
 */

STDMETHODIMP CRMFileSink::OnPacket(IHXPacket* pMediaPacket, BOOL bIsKeyFrame)
{
	HX_RESULT res = HXR_OK;

	// get pointer to RMEditor SDK
	IHXRMEdit* pEdit =	m_pOwnerApp->GetEditSDK();

	// Note: See /include/rmapkts.h for the methods available in the IHXPacket interface.

	// get the IHXBuffer containing the packet data
	IHXBuffer* pBuffer = pMediaPacket->GetBuffer();

	if(pBuffer)
	{
		// allocate a buffer to hold the packet data
		UINT32 ulDataSize = pBuffer->GetSize();
		UINT8* pTempBuf = new UINT8[ulDataSize];
		
		// copy the packet data into the temporary buffer
		memcpy(pTempBuf,pBuffer->GetBuffer(),ulDataSize);

		IHXBuffer* pDataBuffer = NULL;
			
		// create a new IHXBuffer to hold your modified type specific data
		res = pEdit->CreateIRMABuffer(&pDataBuffer);

		if(SUCCEEDED(res))
		{
			pDataBuffer->AddRef();
			// modify the packet data here. In this example we will just send the same packet
			// data packet to the caller. We first need to set the packet data into the new
			// IRMABuffer.
			res = pDataBuffer->Set(pTempBuf,ulDataSize);
		}

		// We now need to set the IHXBuffer into the IRMAPacket.
		if(SUCCEEDED(res))
		{
			pMediaPacket->Set(pDataBuffer,
							  pMediaPacket->GetTime(),
							  pMediaPacket->GetStreamNumber(),
							  pMediaPacket->GetASMFlags(),
							  pMediaPacket->GetASMRuleNumber());
		}

		if(pTempBuf)
		{
			delete [] pTempBuf;
		}
		
		HX_RELEASE(pDataBuffer);
		HX_RELEASE(pBuffer);
	}


	return res;
}

// IUnknown COM Interface Methods

/****************************************************************************
 *  IUnknown::AddRef                                            ref:  hxcom.h
 *
 *  This routine increases the object reference count in a thread safe
 *  manner. The reference count is used to manage the lifetime of an object.
 *  This method must be explicitly called by the user whenever a new
 *  reference to an object is used.
 */
STDMETHODIMP_(ULONG32)
CRMFileSink::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}


/****************************************************************************
 *  IUnknown::Release                                           ref:  hxcom.h
 *
 *  This routine decreases the object reference count in a thread safe
 *  manner, and deletes the object if no more references to it exist. It must
 *  be called explicitly by the user whenever an object is no longer needed.
 */
STDMETHODIMP_(ULONG32)
CRMFileSink::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}


/****************************************************************************
 *  IUnknown::QueryInterface                                    ref:  hxcom.h
 *
 *  This routine indicates which interfaces this object supports. If a given
 *  interface is supported, the object's reference count is incremented, and
 *  a reference to that interface is returned. Otherwise a NULL object and
 *  error code are returned. This method is called by other objects to
 *  discover the functionality of this object.
 */
STDMETHODIMP
CRMFileSink::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = (IUnknown*)(IHXRMFileSink*)this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXRMFileSink))
    {
	AddRef();
	*ppvObj = (IHXRMFileSink*) this;
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}


