/*******************************************************************************

Copyright (c) 2010, Perforce Software, Inc.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1.  Redistributions of source code must retain the above copyright
	notice, this list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright
	notice, this list of conditions and the following disclaimer in the
	documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*******************************************************************************/

/*******************************************************************************
 * Name		: P4BridgeClient.cpp
 *
 * Author	: dbb
 *
 * Description	:  P4BridgeClient is a class derived from the ClientUser in the 
 *  p4api. It provides the "UI" hooks for the p4api to return send output to the
 *  client app. It collects the output as a command is run so that the entire
 *  output can be retrieved when the command completes. Optionally, the client
 *  the client can register call back function to receive any or all of the 
 *  output as it is generated by the p4api.
 *
 ******************************************************************************/
#include "stdafx.h"
#include "P4BridgeClient.h"
#include "P4BridgeServer.h"
#include "P4Connection.h"
#include <strtable.h>
#include <strarray.h>

#include <diff.h>

class DiffObj : public Diff
{};

/*******************************************************************************
 *
 *  P4BridgeClient
 *
 *  Class derived from the ClientUser in the p4api. It provides the "UI" hooks 
 *      for the p4api to return send output to the  *  client app. It collects 
 *      the output as a command is run so that the entire  *  output can be 
 *      retrieved when the command completes. Optionally, the client the client 
 *      can register call back function to receive any or all of the output as 
 *      it is generated by the p4api.
 *
 ******************************************************************************/

/*******************************************************************************
 *
 *  Constructor
 *
 *  Initialize all the pointers to NULL. Space for storage will be created later
 *      when it is needed.
 *
 ******************************************************************************/

P4BridgeClient::P4BridgeClient(P4BridgeServer* pserver, P4Connection* pcon)
	: p4base(Type())
{
	pCon = pcon;
	pFirstError = NULL;
	pLastError = NULL;

	pFirstInfo = NULL;
	pLastInfo = NULL;
	info_results_count = 0;

	results_dictionary_head = NULL;
	results_dictionary_tail = NULL;
	results_dictionary_count = 0;

	data_set = NULL;

	objId = 0;

	pServer = pserver;
	progressCallbacks = {};
}


/*******************************************************************************
 *
 *  Destructor
 *
 *  Free any storage that is still being used.
 *
 ******************************************************************************/

P4BridgeClient::~P4BridgeClient(void)
{
	clear_results();
        delete data_set;
}

/*******************************************************************************
 *
 *  Message
 *
 *  ClientUser override function that is called by the p4api. If the P4 server
 *      is not Unicode enabled, the default implementation will process the 
 *      message by sending it's contents to HandleError() or OutputInfo(). For
 *      Unicode servers, this function must be overridden to provide the same
 *      functionality.
 *
 ******************************************************************************/

void P4BridgeClient::Message( Error *err )
{
	if (err->GetSeverity() >= E_WARN)
	{
		// This is an error
		HandleError( err );
		return; // no error
	}
	// not an error
	
	if (err->GetSeverity() == E_INFO)
	{
		// This is an info message
		ErrorId    *id = err->GetId(0);
		if (id == NULL)
			return;

		// Grab the  text
		StrBuf buf;

		if( pServer->unicodeServer())
			err->Fmt( buf, EF_PLAIN );
		else
			err->Fmt( buf, EF_PLAIN | EF_NOXLATE );

		int msgCode = ErrorOf( id->Subsystem(), id->SubCode(), 0, 0, 0 );

		HandleInfoMsg( id->code, (char)(err->GetGeneric() + '0'), buf.Text() );
		return; // no error
	}
}

/*******************************************************************************
 *
 *  CallTextResultsCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set)
 *
 ******************************************************************************/

void P4BridgeClient::CallTextResultsCallbackFn(const char *data)
{
	pServer->CallTextResultsCallbackFn(pCon->getId(), data);
}

/*******************************************************************************
 *
 *  OutputText
 *
 *  ClientUser override function that is called when a P4 command produces text
 *      output. Calls the callback function if set and adds the new text to the
 *      existing results if any.
 *
 ******************************************************************************/

void P4BridgeClient::OutputText( const char *data, int length )
{
	CallTextResultsCallbackFn( data );


	// length might not have been sent for null terminated string
	if (data && (length < 0)) 
		text_results.Append( data );
	else
		text_results.Append( data, length );
}

/*******************************************************************************
 *
 *  CallInfoResultsCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set) within a
 *      SEH __try block to catch any platform exception. SEH __try blocks must
 *      be contained in simple functions or you will get Compiler Error C2712,
 *      "cannot use __try in functions that require object unwinding"
 *
 ******************************************************************************/

void P4BridgeClient::CallInfoResultsCallbackFn( int msgId, char level, const char *data )
{
	pServer->CallInfoResultsCallbackFn( pCon->getId(), msgId, level, data );
}

/*******************************************************************************
 *
 *  OutputInfo
 *
 *  ClientUser override function that is called when a P4 command produces 
 *      information output (message severity level is E_INFO). Calls the callback 
 *      function if set and adds the new text to the existing results if any.
 *
 ******************************************************************************/

/*******************************************************************************
 *
 *  CallTaggedOutputCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set)
 *
 ******************************************************************************/

void P4BridgeClient::CallTaggedOutputCallbackFn( int objId, const char *pKey, const char * pVal )
{
	pServer->CallTaggedOutputCallbackFn( pCon->getId(), objId, pKey, pVal );
}

/*******************************************************************************
 *
 *  OutputStat
 *
 *  ClientUser override function that is called when a P4 command produces 
 *      tagged output. Calls the callback function if set and adds the new 
 *      StrDict to the existing results if any.
 *
 ******************************************************************************/

void P4BridgeClient::OutputStat( StrDict *dict )
{
	StrDictList * pNew = new StrDictList();

	if( results_dictionary_head == NULL )
	{
		// first item, so set as head and tail
		results_dictionary_head = pNew;
		results_dictionary_tail = pNew;
		// set the object id
		objId = 0;
		results_dictionary_count = 0;
	}
	else
	{
		// add item to tail item and move tail pointer
		results_dictionary_tail->Next(pNew);
		results_dictionary_tail = pNew;

		objId++;

		results_dictionary_count++;
	}

	StrRef var, val;
	for( int i = 0; dict->GetVar( i, var, val ); i++ )
	{
		// skip spec, specFormatted, and func tags
		const char* key = var.Text();
		if (strcmp(key, "spec") == 0 || strcmp(key, "specFormatted") == 0 || strcmp(key, "func") == 0)
			continue;

		char * pVal = new char[val.Length() + 2];
		memcpy((void*)pVal, (void*) val.Text(), val.Length());
		pVal[val.Length()] = '\0';
		pVal[val.Length()+1] = '\0'; // if Unicode
		pServer->CallTaggedOutputCallbackFn( pCon->getId(), objId, var.Text(), pVal );
		delete[] pVal;

		pNew->Data()->SetVar( var, val );
	}
	// flag the end of the object
	CallTaggedOutputCallbackFn( objId, NULL, NULL );
	return;
}

/*******************************************************************************
 *
 *  CallErrorCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set) 
 *
 ******************************************************************************/

void P4BridgeClient::CallErrorCallbackFn( int severity, int errorId, const char * errMsg )
{
	pServer->CallErrorCallbackFn(pCon->getId(), severity, errorId, errMsg);
}

/*******************************************************************************
 *
 *  HandleError
 *
 *  ClientUser override function that is called when a P4 command causes an 
 *      error. Calls the callback function if set and adds the new error message 
 *      to the existing errors for this command if any.
 *
 ******************************************************************************/

void P4BridgeClient::HandleError( Error *err )
{
	LOG_ENTRY();
	if (err->GetSeverity() == E_EMPTY)
		return; // no error

	StrBuf buf;
	err->Fmt( buf, EF_NEWLINE );

	// iterate through the ErrorIds in this Error
	for(int i = 0; ; ++i) 
	{
		ErrorId    *id = err->GetId(i);
		if (id == NULL)
			break;

		int eCode = ErrorOf( id->Subsystem(), id->SubCode(), 0, 0, 0 );
		HandleError( id->Severity(), id->code, buf.Text() );
	}
}

/*******************************************************************************
 *
 *  HandleError
 *
 *  Internal function that is called when an error that needs to be reported to 
 *      the client is generated in the bridge code. Calls the callback function 
 *      if set and adds the new error message to the existing errors for this 
 *      command if any.
 *
 ******************************************************************************/

void P4BridgeClient::HandleError( int severity, int	errorCode, const char *errMsg )
{
	P4ClientError * pNewError = new P4ClientError(severity, errorCode, errMsg );

	HandleError( pNewError );
}

void P4BridgeClient::HandleError( P4ClientError * pNewError )
{
	LOG_DEBUG(4, pNewError->Message.c_str());

	if( !pFirstError )
	{
		// first error so use it to start the list
		pFirstError = pNewError;
		pLastError = pNewError;
	}
	else
	{
		// Add it to the end of the list
		pLastError->Next = pNewError;
		pLastError = pNewError;
   }

	CallErrorCallbackFn( pNewError->Severity, pNewError->ErrorCode, pNewError->Message.c_str() );
}

/*******************************************************************************
 *
 *  HandleInfoMsg
 *
 *  Function that is called when an info message is received and reported to 
 *      the client. Calls the callback function if set and adds the new info 
 *      message to the existing info output for this command if any.
 *
 ******************************************************************************/

void P4BridgeClient::HandleInfoMsg( int msgCode, char level, const char *infMsg )
{
	P4ClientInfoMsg * pNewMsg = new P4ClientInfoMsg(msgCode, level, infMsg );

	HandleInfoMsg( pNewMsg );
}

void P4BridgeClient::HandleInfoMsg( P4ClientInfoMsg * pNewMsg )
{

	if( !pFirstInfo )
	{
		// first error so use it to start the list
		pFirstInfo = pNewMsg;
		pLastInfo = pNewMsg;
		info_results_count = 0;
	}
	else
	{
		// Add it to the end of the list
		pLastInfo->Next = pNewMsg;
		pLastInfo = pNewMsg;
		info_results_count++;
   }

	CallInfoResultsCallbackFn( pNewMsg->MsgCode, pNewMsg->Level, pNewMsg->Message.c_str() );
}

bool handleUrl = false;

void P4BridgeClient::HandleUrl(const StrPtr* url)
{
	handleUrl = true;
	ClientUser::HandleUrl(url);
}
/*******************************************************************************
 *
 *  OutputError
 *
 *  ClientUser override function that is called when an old P4 command causes an 
 *      error. Calls the callback function if set and adds the new error message 
 *      to the existing errors for this command if any.
 *
 ******************************************************************************/

void P4BridgeClient::OutputError( const char *err )// For broken servers
{
	LOG_ENTRY();
	HandleError( -1, 0, err );
}

void P4BridgeClient::Diff( FileSys *f1, FileSys *f2, int doPage, 
				char *diffFlags, Error *e )
{
	//
	// Duck binary files. Much the same as ClientUser::Diff, we just
	// put the output into Ruby space rather than stdout.
	//
	if( !f1->IsTextual() || !f2->IsTextual() )
	{
		if ( f1->Compare( f2, e ) )
			OutputText(  "(... files differ ...)", -1 );
		return;
	}

	// Time to diff the two text files. Need to ensure that the
	// files are in binary mode, so we have to create new FileSys
	// objects to do this.

	FileSys *f1_bin = FileSys::Create( FST_BINARY );
	FileSys *f2_bin = FileSys::Create( FST_BINARY );
	FileSys *t = FileSys::CreateGlobalTemp( f1->GetType() );

	f1_bin->Set( f1->Name() );
	f2_bin->Set( f2->Name() );

	{
		//
		// In its own block to make sure that the diff object is deleted
		// before we delete the FileSys objects.
		//
		DiffObj d;

		d.SetInput( f1_bin, f2_bin, diffFlags, e );
		if ( ! e->Test() ) d.SetOutput( t->Name(), e );
		if ( ! e->Test() ) d.DiffWithFlags( diffFlags );
		d.CloseOutput( e );

		// OK, now we have the diff output, read it in and add it to 
		// the output.
		if ( ! e->Test() ) t->Open( FOM_READ, e );
		if ( ! e->Test() ) 
		{
			StrBuf 	b;
			while( t->ReadLine( &b, e ) )
			{
				OutputText( b.Text(), b.Length() );
				OutputText( "\r\n", -1 );
			}
		}
	}

	delete t;
	delete f1_bin;
	delete f2_bin;

	if ( e->Test() ) HandleError( e );
}


/*******************************************************************************
 *
 *  CallBinaryResultsCallbackFn
 *
 *  Simple wrapper to call the callback function (if it has been set)
 *
 ******************************************************************************/

void P4BridgeClient::CallBinaryResultsCallbackFn(void * data, int length )
{
	pServer->CallBinaryResultsCallbackFn( pCon->getId(), data,  length );
}

/*******************************************************************************
 *
 *  OutputBinary
 *
 *  ClientUser override function that is called when a P4 command produces  
 *      binary data. Calls the callback function if set and adds the new data 
 *      to the existing binary data for this command if any.
 *
 ******************************************************************************/

void P4BridgeClient::OutputBinary( const char *data, int length )
{
	CallBinaryResultsCallbackFn((void *) data, length );

	Binary_results.insert(Binary_results.end(), data, data + length);
}

/*******************************************************************************
 *
 *  GetErrorResults
 *
 *  Gets the first error in the list of errors produced since the error list was
 *      last cleared. Returns null if there are no errors in the list. The 
 *      P4ClientError object returned contains a pointer to the next error in
 *      the list, so the entire list can be obtained.
 *
 ******************************************************************************/

P4ClientError* P4BridgeClient::GetErrorResults()
{
	return pFirstError;
}

/*******************************************************************************
 *
 *  GetInfoResultsCount
 *
 *  Gets the count of the number info results collected since the results were 
 *		last cleared. Returns -1 if there are no results available. The results 
 *		are returned as a single string.
 *
 ******************************************************************************/

int P4BridgeClient::GetInfoResultsCount()
{
	return info_results_count;
}

/*******************************************************************************
 *
 *  GetInfoResults
 *
 *  Gets the first info results collected since the results were last cleared. 
 *      Returns null if there are no results available. The results are 
 *      returned as a single string.
 *
 ******************************************************************************/

P4ClientInfoMsg * P4BridgeClient::GetInfoResults()
{
	return pFirstInfo;
}

/*******************************************************************************
 *
 *  GetTextResults
 *
 *  Gets the first text results collected since the results were last cleared. 
 *      Returns null if there are no results available. The results are 
 *      returned as a single string.
 *
 ******************************************************************************/

const char* P4BridgeClient::GetTextResults()
{
	return text_results.Text();
}

/*******************************************************************************
 *
 *  GetTaggedOutput
 *
 *  Gets the first tagged output collected since the results were last cleared. 
 *      Returns null if there are no results available. The StrDictListIterator 
 *      object returned can be used to iterate through all of the objects and
 *      their values.
 *
 ******************************************************************************/

StrDictListIterator* P4BridgeClient::GetTaggedOutput()
{
	if (results_dictionary_head)
	{
		return new StrDictListIterator(results_dictionary_head);
	}
	return NULL;
}

/*******************************************************************************
 *
 *  GetBinaryResults
 *
 *  Gets the first info results collected since the results were last cleared. 
 *      Returns null if there are no  results available.  The results are 
 *      returned as a single buffer.
 *
 ******************************************************************************/

const unsigned char* P4BridgeClient::GetBinaryResults()
{
	return Binary_results.data();
}

/*******************************************************************************
 *
 *  SetDataSet
 *
 *  Called to set the Input Data which will be used by a command. The data must 
 *      be set before the command is run
 *
 ******************************************************************************/

void P4BridgeClient::SetDataSet(const char * data)
{
	 if (!data_set)
		 data_set = new StrBuf();
	 data_set->Set(data);
}

/*******************************************************************************
 *
 *  GetDataSet
 *
 *  Get the Input Data which will be used by a command. 
 *
 ******************************************************************************/

StrPtr * P4BridgeClient::GetDataSet( void )
{
	 return data_set;
}


void P4BridgeClient::Prompt( const StrPtr &msg, StrBuf &rsp, 
				int noEcho, Error *e )
{
	pServer->Prompt(pCon->getId(), msg, rsp, noEcho, e);
}

/*******************************************************************************
 *
 *  InputData
 *
 *  ClientUser override function that is called when a P4 command requires
 *       additional text based data such as a workspace specification.
 *
 ******************************************************************************/

void P4BridgeClient::InputData( StrBuf *buf, Error *err )
{
	buf->Clear();
	if (data_set)
		buf->Set( data_set );
	buf->Terminate();
}

/*******************************************************************************
 *
 *  clear_results
 *
 *  Clear all the stored results in preparation for running a command.
 *
 ******************************************************************************/

#define DELETE_OBJECT(obj) if( obj != NULL ) { delete obj; obj = NULL; }
#define DELETE_ARRAY(obj)  if( obj != NULL ) { delete[] obj; obj = NULL; }

void P4BridgeClient::clear_results()
{
	LOG_ENTRY();
	// Cleanup the output buffers
	
	DELETE_OBJECT(pFirstError);	
	pFirstError = NULL;
	pLastError = NULL;

	DELETE_OBJECT(pFirstInfo);
	pFirstInfo = NULL;
	pLastInfo = NULL;

	DELETE_OBJECT( results_dictionary_head )
	results_dictionary_tail = NULL;
	text_results.Reset();
	Binary_results.clear();
}

int	P4BridgeClient::Resolve( ClientMerge *m, Error *e )
{
	return pServer->Resolve( pCon->getId(), m, e );
}

int	P4BridgeClient::Resolve( ClientResolveA *r, int preview, Error *e )
{
	return pServer->Resolve(pCon->getId(), r, preview, e );
}

/*******************************************************************************
 * 
 *  P4BridgeClient::CreateProgress
 *
 *  Creates a new P4BridgeClientProgress object, passing the registered
 *  progress callbacks and object ID.
 *
 ******************************************************************************/
ClientProgress* P4BridgeClient::CreateProgress(int type)
{
	// Only create progress if at least one callback is set
	if (!(progressCallbacks.Init || progressCallbacks.Description ||
		progressCallbacks.Total || progressCallbacks.Update ||
		progressCallbacks.Done))
	{
		return nullptr;
	}
    return new P4BridgeClientProgress(&progressCallbacks, type);
}

/*******************************************************************************
 *
 *  CreateProgress
 *
 *  Creates a progress object for file transfer operations, passing the
 *  registered progress callbacks and object ID. The totalSize parameter
 *  can be used if you want to track the total bytes for the operation.
 *
 ******************************************************************************/
ClientProgress* P4BridgeClient::CreateProgress(int type, P4INT64 totalSize)
{
    // You can use totalSize if you want to pass it to your progress class.
    // For now, we just ignore it, as the callbacks do not use it directly.
	  // Only create progress if at least one callback is set
    if (!(progressCallbacks.Init || progressCallbacks.Description ||
          progressCallbacks.Total || progressCallbacks.Update ||
          progressCallbacks.Done))
    {
        return nullptr;
    }
    return new P4BridgeClientProgress(&progressCallbacks, type);
}

/*******************************************************************************
*
*  ProgressIndicator
*
*  Returns 1 if progress callbacks are registered, 0 otherwise.
*
******************************************************************************/
int P4BridgeClient::ProgressIndicator()
{
    return (progressCallbacks.Init || progressCallbacks.Description ||
            progressCallbacks.Total || progressCallbacks.Update ||
            progressCallbacks.Done) ? 1 : 0;
}

/*******************************************************************************
*
*  SetProgressCallbacks
*
*  Registers the set of progress callbacks to be used for progress reporting.
*
******************************************************************************/
void P4BridgeClient::SetProgressCallbacks(const ProgressCallbackSet& callbacks)
{
    progressCallbacks = callbacks;
}


/*******************************************************************************
 *
 *  P4BridgeClientProgress Implementation
 *
 ******************************************************************************/

P4BridgeClientProgress::P4BridgeClientProgress(const ProgressCallbackSet* cbSet, int t)
    : callbacks(cbSet), type(t)
{
    // Invoke the Init callback if registered
    if (callbacks && callbacks->Init)
        callbacks->Init(type);
}

P4BridgeClientProgress::~P4BridgeClientProgress()
{
    // No cleanup needed
}

/*******************************************************************************
*  Description
*  Called when a progress description is available.
******************************************************************************/

void P4BridgeClientProgress::Description(const StrPtr* d, int u)
{
    if (callbacks && callbacks->Description)
        callbacks->Description(d ? d->Text() : "", u);
}

/*******************************************************************************
*  Total
*  Called when the total work amount is known.
******************************************************************************/

void P4BridgeClientProgress::Total(long t)
{
    if (callbacks && callbacks->Total)
        callbacks->Total(t);
}

/*******************************************************************************
*  Update
*  Called to report progress updates.
*****************************************************************************/

int P4BridgeClientProgress::Update(long update)
{
    if (callbacks && callbacks->Update)
        callbacks->Update(update);
    return 0;
}

/*******************************************************************************
*  Done
*  Called when the progress operation is complete.
******************************************************************************/

void P4BridgeClientProgress::Done(int f)
{
    if (callbacks && callbacks->Done)
        callbacks->Done(f);
}



/*******************************************************************************
 * 
 * StrDictList
 *
 * Class used to keep a list of StrDict objects when collecting the tagged
 *      output of a command
 *
 ******************************************************************************/

/*******************************************************************************
 * Constructor
 ******************************************************************************/

StrDictList::StrDictList() 
	: p4base(Type()) 
{ 
	pStrDict = new StrBufDict(); 
	pNext = NULL;
}

/*******************************************************************************
 * 
 * StrDictListIterator
 *
 * Class used to iterate a list of StrDict objects when retrieving the tagged
 *      output of a command. See comments in p4bridge-api.cpp for a detailed 
 *      usage explanation.
 *
 ******************************************************************************/

/*******************************************************************************
 * Constructor
 ******************************************************************************/

StrDictListIterator::StrDictListIterator()
	: p4base(Type())
{
	curEntry = NULL;
}

/*******************************************************************************
 * Constructor
 ******************************************************************************/

StrDictListIterator::StrDictListIterator(StrDictList* ndict)
 : p4base(Type())
{
	curEntry = NULL;
	Init(ndict);
}

/*******************************************************************************
 * Destructor
 *******************************************************************************
 * Note: this destructor uses an iterative while loop to delete the list
 *       instead of recusion because a long list (> ~2000) can cause stack
 *       overflow with a recursive destructor. 
 ******************************************************************************/
StrDictList::~StrDictList()
{ 
	if (pStrDict != NULL)
		delete pStrDict;
	  
	if (pNext == NULL)
	{
		return;
	}

	StrDictList* pCur = this->pNext;
	while (pCur != NULL)
	{
		pNext = pCur->pNext;
		pCur->pNext = NULL;
		delete pCur;
		pCur = this->pNext;
	}

};

/*******************************************************************************
 * Destructor
 ******************************************************************************/

StrDictListIterator::~StrDictListIterator()
{
	Reset();
}

/*******************************************************************************
 * 
 * Init
 *
 * Initialize the iterator to the beginning of the list before starting the 
 *      iteration.
 *
 ******************************************************************************/

int StrDictListIterator::Init(StrDictList* ndict)
{
	dict = ndict;

	Reset();

	return (curItem == NULL);
}

/*******************************************************************************
 * 
 * GetNextItem
 *
 * Get the next StrDict object in the list.
 *
 ******************************************************************************/

StrDictList* StrDictListIterator::GetNextItem()
{
	if (curItem == NULL)
	{
		curItem = dict;
	}
	else
	{
		curItem = curItem->Next();
	}
	// Start with the first entry of the new dictionary
	idx = 0;

	return curItem;
}

/*******************************************************************************
 * 
 * GetEntry
 *
 * Get the nth key:value pair for the current StrDict object.
 *
 ******************************************************************************/

KeyValuePair* StrDictListIterator::GetEntry(int idx)
{
	if (curItem == NULL)
	{
		return NULL;
	}
	if (curEntry != NULL)
	{
		delete curEntry;
		curEntry = NULL;
	}
	StrRef var, val;

	if (curItem->Data()->GetVar( idx, var, val ))
		curEntry = new KeyValuePair(var.Text(), var.Length(), val.Text(), val.Length());

	return curEntry;
}

/*******************************************************************************
 * 
 * GetNextEntry
 *
 * Get the next key:value pair for the current StrDict object.
 *
 ******************************************************************************/

KeyValuePair* StrDictListIterator::GetNextEntry()
{
	if (curItem == NULL)
	{
		return NULL;
	}
	return GetEntry(idx++);
}

/*******************************************************************************
 * 
 * Reset
 *
 * Reset the iterator.
 *
 ******************************************************************************/

void StrDictListIterator::Reset()
{
	curItem = NULL;
	idx = 0;

	if (curEntry != NULL)
	{
		delete curEntry;
		curEntry = NULL;
	}
}

/*******************************************************************************
 * KeyValuePair
 ******************************************************************************/

/*******************************************************************************
 * Constructor
 ******************************************************************************/

KeyValuePair::KeyValuePair(const char * k, int kSz, const char * v, int vSz)
	: p4base(Type()), keyLength(kSz), valLength(vSz)
{
	key = k;
	value = v;
}

/*******************************************************************************
 * Destructor
 ******************************************************************************/

KeyValuePair::~KeyValuePair()
{
}

/*******************************************************************************
 *
 *  P4ClientError
 *
 *  This simple class is used to return the data elements of an error message.
 *
 ******************************************************************************/

/*******************************************************************************
 * Constructor
 ******************************************************************************/

P4ClientError::P4ClientError(int severity, int	errorCode, const char * msg)
	: p4base(Type())
{
	Severity = severity;
	ErrorCode = errorCode;
	Message = msg;

	Next = NULL;
}

P4ClientError::P4ClientError(P4ClientError * err)
	: p4base(Type())
{
	Severity = err->Severity;
	ErrorCode = err->ErrorCode;
	Message = err->Message;

	Next = NULL;
}

/*******************************************************************************
 * Destructor
 *******************************************************************************
 * Note: this destructor uses an iterative while loop to delete the list
 *       instead of recursion because a long list (> ~2000) can cause stack
 *       overflow with a recursive destructor. 
 ******************************************************************************/
P4ClientError::~P4ClientError()
{ 
	if (Next == NULL)
	{
		return;
	}

	P4ClientError* pCur = this->Next;
	while (pCur != NULL)
	{
		this->Next = pCur->Next;
		pCur->Next = NULL;
		delete pCur;
		pCur = this->Next;
	}

};

/*******************************************************************************
 * Return the max severity in the error list
 ******************************************************************************/

int P4ClientError::MaxSeverity()
{
	P4ClientError *pNext = Next;
	int maxSeverity = Severity;

	while (pNext != NULL)
	{
		int nextSeverity = pNext->Severity;
		if (nextSeverity > maxSeverity)
		{
			maxSeverity =  nextSeverity;
		}
		pNext = pNext->Next;
	}
	return maxSeverity;
}

/*******************************************************************************
 *
 *  P4ClientInfoMsg
 *
 *  This simple class is used to return the data elements of an info message.
 *
 ******************************************************************************/

/*******************************************************************************
 * Constructor
 ******************************************************************************/

P4ClientInfoMsg::P4ClientInfoMsg(int msgCode, char level, const char * msg)
	: p4base(Type())
{
	Level = level;
	MsgCode = msgCode;
	Message = msg;

	Next = NULL;
}

P4ClientInfoMsg::P4ClientInfoMsg(P4ClientInfoMsg * info)
	: p4base(Type())
{
	Level = info->Level;
	MsgCode = info->MsgCode;
	Message = info->Message;

	Next = NULL;
}

/*******************************************************************************
 * Destructor
 *******************************************************************************
 * Note: this destructor uses an iterative while loop to delete the list
 *       instead of recursion because a long list (> ~2000) can cause stack
 *       overflow with a recursive destructor. 
 ******************************************************************************/
P4ClientInfoMsg::~P4ClientInfoMsg()
{ 
	if (Next == NULL)
	{
		return;
	}

	P4ClientInfoMsg* pCur = this->Next;
	while (pCur != NULL)
	{
		Next = pCur->Next;
		pCur->Next = NULL;
		delete pCur;
		pCur = this->Next;
	}

};

/*******************************************************************************
 *
 *  P4ClientMerge
 *
 *  This simple class is a wrapper for ClientMerge object.
 *
 ******************************************************************************/

	P4ClientMerge::P4ClientMerge(ClientMerge * merger)
	: p4base(Type())
	{
		lastError = NULL;
		Merger = merger;
	}

	P4ClientMerge::~P4ClientMerge()
	{
		if (lastError != NULL)
			delete lastError;
	}

	P4ClientError *P4ClientMerge::GetLastError()
	{
		if (lastError != NULL)
		{
			delete lastError;
			lastError = NULL;
		}
		if (err.GetSeverity() == E_EMPTY)
			return NULL; // no error

		StrBuf buf;
		err.Fmt( buf, EF_NEWLINE );

		lastError =  new P4ClientError(err.GetSeverity(), err.GetGeneric(), buf.Text());
		return lastError;
	}

	MergeStatus P4ClientMerge::AutoResolve( MergeForce forceMerge )
	{
		if (Merger == NULL)
			return CMS_QUIT;

		return Merger->AutoResolve(forceMerge);
	}
	
	MergeStatus P4ClientMerge::Resolve()
	{
		if (Merger == NULL)
			return CMS_QUIT;

		return Merger->Resolve(&err);
	}
	
	MergeStatus P4ClientMerge::DetectResolve()
	{
		if (Merger == NULL)
			return CMS_QUIT;

		return Merger->DetectResolve();
	}
	
	int	P4ClientMerge::IsAcceptable()
	{
		if (Merger == NULL)
			return false;

		return Merger->IsAcceptable();
	}

	StrPtr *P4ClientMerge::GetBaseFile()
	{
		if (Merger == NULL)
			return NULL;

		FileSys *f = Merger->GetBaseFile();
		if (f == NULL)
			return NULL;

		return f->Path();
	}

	StrPtr *P4ClientMerge::GetYourFile()
	{
		if (Merger == NULL)
			return NULL;

		FileSys *f = Merger->GetYourFile();
		if (f == NULL)
			return NULL;

		return f->Path();
	}

	StrPtr *P4ClientMerge::GetTheirFile()
	{
		if (Merger == NULL)
			return NULL;

		FileSys *f = Merger->GetTheirFile();
		if (f == NULL)
			return NULL;

		return f->Path();
	}

	StrPtr *P4ClientMerge::GetResultFile()
	{
		if (Merger == NULL)
			return NULL;

		FileSys *f = Merger->GetResultFile();
		if (f == NULL)
			return NULL;

		return f->Path();
	}

	int	P4ClientMerge::GetYourChunks()
	{
		if (Merger == NULL)
			return -1;

		return Merger->GetYourChunks();
	}
	int	P4ClientMerge::GetTheirChunks()
	{
		if (Merger == NULL)
			return -1;

		return Merger->GetTheirChunks();
	}
	int	P4ClientMerge::GetBothChunks()
	{
		if (Merger == NULL)
			return -1;

		return Merger->GetBothChunks();
	}
	int	P4ClientMerge::GetConflictChunks()
	{
		if (Merger == NULL)
			return -1;

		return Merger->GetConflictChunks();
	}

	const StrPtr *P4ClientMerge::GetMergeDigest()
	{
		if (Merger == NULL)
			return NULL;

		return Merger->GetMergeDigest();
	}

	const StrPtr *P4ClientMerge::GetYourDigest()
	{
		if (Merger == NULL)
			return NULL;

		return Merger->GetYourDigest();
	}

	const StrPtr *P4ClientMerge::GetTheirDigest()
	{
		if (Merger == NULL)
			return NULL;

		return Merger->GetTheirDigest();
	}

/*******************************************************************************
 *
 *  P4ClientResolve
 *
 *  This simple class is a wrapper for ClientResolve object.
 *
 ******************************************************************************/

	const Error P4ClientResolve::_INVALID_API_REFERENCE = Error();

	P4ClientResolve::P4ClientResolve(ClientResolveA * resolver, int isUnicode) : p4base(Type()) 
	{
		lastError = NULL;

		Resolver = resolver;

		if (resolver != NULL)
		{
			Error er;

			type.Clear();
			er.Clear();
			er = resolver->GetType();
			if( isUnicode )
				er.Fmt( type, EF_PLAIN );
			else
				er.Fmt( type, EF_PLAIN | EF_NOXLATE );

			mergeAction.Clear();
			er.Clear();
			er = resolver->GetMergeAction();
			if( isUnicode )
				er.Fmt( mergeAction, EF_PLAIN );
			else
				er.Fmt( mergeAction, EF_PLAIN | EF_NOXLATE );

			yoursAction.Clear();
			er.Clear();
			er = resolver->GetYoursAction();
			if( isUnicode )
				er.Fmt( yoursAction, EF_PLAIN );
			else
				er.Fmt( yoursAction, EF_PLAIN | EF_NOXLATE );
			
			theirAction.Clear();
			er.Clear();
			er = resolver->GetTheirAction();
			if( isUnicode )
				er.Fmt( theirAction, EF_PLAIN );
			else
				er.Fmt( theirAction, EF_PLAIN | EF_NOXLATE );

			mergePrompt.Clear();
			er.Clear();
			er = resolver->GetMergePrompt();
			if( isUnicode )
				er.Fmt( mergePrompt, EF_PLAIN );
			else
				er.Fmt( mergePrompt, EF_PLAIN | EF_NOXLATE );

			yoursPrompt.Clear();
			er.Clear();
			er = resolver->GetYoursPrompt();
			if( isUnicode )
				er.Fmt( yoursPrompt, EF_PLAIN );
			else
				er.Fmt( yoursPrompt, EF_PLAIN | EF_NOXLATE );

			theirPrompt.Clear();
			er.Clear();
			er = resolver->GetTheirPrompt();
			if( isUnicode )
				er.Fmt( theirPrompt, EF_PLAIN );
			else
				er.Fmt( theirPrompt, EF_PLAIN | EF_NOXLATE );

			mergeOpt.Clear();
			er.Clear();
			er = resolver->GetMergeOpt();
			if( isUnicode )
				er.Fmt( mergeOpt, EF_PLAIN );
			else
				er.Fmt( mergeOpt, EF_PLAIN | EF_NOXLATE );

			yoursOpt.Clear();
			er.Clear();
			er = resolver->GetYoursOpt();
			if( isUnicode )
				er.Fmt( yoursOpt, EF_PLAIN );
			else
				er.Fmt( yoursOpt, EF_PLAIN | EF_NOXLATE );

			theirOpt.Clear();
			er.Clear();
			er = resolver->GetTheirOpt();
			if( isUnicode )
				er.Fmt( theirOpt, EF_PLAIN );
			else
				er.Fmt( theirOpt, EF_PLAIN | EF_NOXLATE );

			skipOpt.Clear();
			er.Clear();
			er = resolver->GetSkipOpt();
			if( isUnicode )
				er.Fmt( skipOpt, EF_PLAIN );
			else
				er.Fmt( skipOpt, EF_PLAIN | EF_NOXLATE );

			helpOpt.Clear();
			er.Clear();
			er = resolver->GetHelpOpt();
			if( isUnicode )
				er.Fmt( helpOpt, EF_PLAIN );
			else
				er.Fmt( helpOpt, EF_PLAIN | EF_NOXLATE );

			autoOpt.Clear();
			er.Clear();
			er = resolver->GetAutoOpt();
			if( isUnicode )
				er.Fmt( autoOpt, EF_PLAIN );
			else
				er.Fmt( autoOpt, EF_PLAIN | EF_NOXLATE );

			prompt.Clear();
			er.Clear();
			er = resolver->GetPrompt();
			if( isUnicode )
				er.Fmt( prompt, EF_PLAIN );
			else
				er.Fmt( prompt, EF_PLAIN | EF_NOXLATE );

			typePrompt.Clear();
			er.Clear();
			er = resolver->GetTypePrompt();
			if( isUnicode )
				er.Fmt( typePrompt, EF_PLAIN );
			else
				er.Fmt( typePrompt, EF_PLAIN | EF_NOXLATE );

			usageError.Clear();
			er.Clear();
			er = resolver->GetUsageError();
			if( isUnicode )
				er.Fmt( usageError, EF_PLAIN );
			else
				er.Fmt( usageError, EF_PLAIN | EF_NOXLATE );

			help.Clear();
			er.Clear();
			er = resolver->GetHelp();
			if( isUnicode )
				er.Fmt( help, EF_PLAIN );
			else
				er.Fmt( help, EF_PLAIN | EF_NOXLATE );
		}
	}

	P4ClientResolve::~P4ClientResolve()
	{
		if (lastError != NULL)
			delete lastError;
	}

	P4ClientError *P4ClientResolve::GetLastError()
	{
		if (lastError != NULL)
		{
			delete lastError;
			lastError = NULL;
		}
		if (err.GetSeverity() == E_EMPTY)
			return NULL; // no error

		StrBuf buf;
		err.Fmt( buf, EF_NEWLINE );

		lastError =  new P4ClientError(err.GetSeverity(), err.GetGeneric(), buf.Text());
		return lastError;
	}

	MergeStatus P4ClientResolve::AutoResolve( MergeForce force ) const
	{
		if (Resolver == NULL)
		{
			return CMS_SKIP;
		}
		return Resolver->AutoResolve( force );
	}

	MergeStatus P4ClientResolve::Resolve( int preview )
	{
		if (Resolver == NULL)
		{
			return CMS_SKIP;
		}
		return Resolver->Resolve( preview, &err );
	}
