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

Copyright (c) 2004 Open Mobile Alliance, Ltd.  All Rights Reserved.

This software is made available under the usage and distribution terms 
covered by the Common Public License 1.0 and by using this software you
are bound by the terms and conditions of the same.

EXCEPT AS EXPRESSLY SET FORTH IN THE COMMON PUBLIC LICENSE,
THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
   
Each recipient is solely responsible for determining the 
appropriateness of using and distributing this program and assumes 
all risks associated with its exercise of rights under the Common 
Public License, including but not limited to the risks and 
costs of program errors, compliance with applicable laws, 
damage to or loss of data, programs or equipment, and 
unavailability or interruption of operations.

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

#include "SctsMessage.h"
#ifdef __SCTS_USING_RTK__
extern "C" {
#include "wsm.h"
#include "mgr.h"
#include "smldef.h"
}

#endif

/*************************************************************
SctsMessage Class 
**************************************************************/




SctsMessage::SctsMessage(SctsSessionContext _sessionContext,SctsEncoding _sessionEncoding,SctsSyncHdr *_syncHdr,int maxSize,SctsSessionContext _wbxmlConverterContext,void *_uiCallbackData,void (*_uiCallback)(void *data,SctsCommand *command,int flag,bool build))
	: syncBody(_syncHdr),sessionContext(_sessionContext),sessionEncoding(_sessionEncoding),wbxmlConverterContext(_wbxmlConverterContext),uiCallbackData(_uiCallbackData),uiCallback(_uiCallback)
{
#ifdef __SCTS_USING_RTK__
	/* 
	Get the current free size of the workspace buffer, build the syncHdr, get the
	free size again and hence calculate the size of the syncHdr. Using this size
	calculate the remaining free size of the message (not the workspace buffer).
	*/
	long freeSizeBefore;
	long freeSizeAfter;
	SCTSASSERT(wsmGetFreeSize(sessionContext,&freeSizeBefore)==SML_ERR_OK);
#endif

	_syncHdr->Build(sessionContext);

#ifdef __SCTS_USING_RTK__
	SCTSASSERT(wsmGetFreeSize(sessionContext,&freeSizeAfter)==SML_ERR_OK);
#endif
	/* Ref#0019 : Set SyncHdr size */
	_syncHdr->size=freeSizeBefore-freeSizeAfter;

	freeSize=maxSize-(freeSizeBefore-freeSizeAfter);
	/* Account for size of closing tag */
	if (sessionEncoding==WBXML)
		freeSize-=4; /* 3 byte (0x01) in WBXML, +1 just in case */
	else
		freeSize-=29; /* 8 bytes '<Final/></SyncBody></SyncML>' in XML, +1 just in case */ 
	SCTSASSERT(freeSize>0);
	lastCommandNotBuilt=0;
	lastCommandFound=0;
	currentContainer=syncBody.GetRootItem();
	currentCommand=syncBody.GetRootItem();
	IsFinal(true);
}

SctsCommand *SctsMessage::FindNextCommand(SctsCommandType type)
{
	SctsTree<SctsCommand *>::Item *command;
	SctsTree<SctsCommand *>::Item *nextCommand;
	if (lastCommandFound)
	{
		if (type==SCTS_COMMAND_COUNT) type=lastCommandFound->data->GetType();
		command=lastCommandFound;
	}
	else
	{
		if (type==SCTS_COMMAND_COUNT) return 0;
		command=syncBody.GetRootItem();
		if (type==SCTS_HEADER) return command->data;
	}
	while (true)
	{
		if (!(nextCommand=syncBody.GetFirstChildItem(command)))
		{
			nextCommand=syncBody.GetNextSiblingItem(command);
			while (nextCommand && !nextCommand->data) 
			{
				nextCommand=syncBody.GetParentItem(command);
				nextCommand=syncBody.GetNextSiblingItem(nextCommand);
			}
			if (!nextCommand)
				break;
			if (nextCommand==syncBody.GetRootItem()) break;
		}
		if (nextCommand->data->GetType()==type)
		{
			lastCommandFound=nextCommand;
			return nextCommand->data;
		}
		command=nextCommand;
	}
	return 0;
}

SctsCommand *SctsMessage::FindCommand(SctsCommandType type)
{
	lastCommandFound=0;
	return FindNextCommand(type);
}

SctsCommand *SctsMessage::GetCurrentCommand()
{
	if (!currentCommand) return 0;
	return currentCommand->data;
}

void SctsMessage::DeleteCurrentCommand()
{
	SctsTree<SctsCommand *>::Item *nextCommand;
	if ((nextCommand=syncBody.GetFirstChildItem(currentCommand)) && nextCommand->data)
	{
		currentCommand=nextCommand;
		return;
	}
	nextCommand=syncBody.GetNextSiblingItem(currentCommand);
	if (!nextCommand->data)
	{
		syncBody.DeleteItem(currentCommand);
		currentCommand=syncBody.GetParentItem(nextCommand);
		if (currentCommand==syncBody.GetRootItem())
		{
			currentCommand=0;
			return;
		}
		nextCommand=syncBody.GetNextSiblingItem(currentCommand);
	}
	syncBody.DeleteItem(currentCommand);
	currentCommand=nextCommand;
}

extern "C" InstanceInfoPtr_t findInfo(InstanceID_t id);
bool SctsMessage::InsertCommand(SctsCommand *command,bool build,bool closeAll)
{
	bool isContainer=false;
	SctsTree<SctsCommand *>::Item *thisCommand;
	
#ifdef __SCTS_USING_RTK__
	/* Calculate the used size of the workspace buffer before building the command*/
	unsigned char *tempPtr;
	long usedSizeBefore;
	long usedSizeAfter;
	InstanceInfoPtr_t instanceInfo;
	struct
	{
	  SmlEncoding_t enc;
	  SmlPcdataExtension_t cur_ext;
	  SmlPcdataExtension_t last_ext;
	  Boolean_t final;
	  VoidPtr_t space_evaluation;
	  MemSize_t end_tag_size;
	} encoderState;
	SCTSASSERT(wsmGetUsedSize(sessionContext,&usedSizeBefore)==SML_ERR_OK);
	/* Save the encoder state if the encoding is WBXML */
	if (sessionEncoding==WBXML)
	{
		instanceInfo=findInfo(sessionContext);
		sctsMemcpy(&encoderState,instanceInfo->encoderState,sizeof(encoderState));
	}

#endif
	
	SCTSASSERT(currentContainer);	
	thisCommand=syncBody.InsertChildItem(currentContainer,command);
	/* Container closings are represented by a 0 */
	if (command)
	{
		SctsCommandType type;
		type=command->GetType();
		sctsStatusLog("Building ");
		sctsStatusLog(sctsCommandStrings[type]);
		sctsStatusLog("...\r\n");
		command->Build(sessionContext);
		if (type==SCTS_HEADER || type==SCTS_ATOMIC_START || type==SCTS_SEQUENCE_START || type==SCTS_SYNC_START)
		{
			/* Account for size of closing tag */
			if (sessionEncoding==WBXML)
			{
				/* 1 byte 0x01 in WBXML regardless of the type of tag except for SyncHdr
					Account for /SyncBody and /SyncML Tags
				*/
				if (type==SCTS_HEADER)
					freeSize-=4;
				else
					freeSize-=2;
			}
			else
			{
				switch (type)
				{
				case SCTS_ATOMIC_START:
					freeSize-=10; /* 10 bytes '</Atomic> in XML */
					break;
				case SCTS_SEQUENCE_START:
					freeSize-=12; /* 12 bytes '</Sequence> in XML */
					break;
				case SCTS_SYNC_START:
					freeSize-=8; /* 8 bytes '</Sync> in XML */
					break;
				case SCTS_HEADER:
					freeSize-=22; /* 22 bytes '</SyncBody> '</SyncML> in XML */
				}
			}
			isContainer=true;
		}
	}
	else
	{
		sctsStatusLog("Building ");
		sctsStatusLog(sctsCommandStrings[currentContainer->data->GetType()+1]);
		sctsStatusLog("...\r\n");
		currentContainer=syncBody.GetParentItem(currentContainer);
	}

	if (command)
	{
		/* Calculate the size of the command */
#ifdef __SCTS_USING_RTK__
		/* Calculate the used size of the workspace buffer after building the command */
		SCTSASSERT(wsmGetUsedSize(sessionContext,&usedSizeAfter)==SML_ERR_OK);
		command->size=usedSizeAfter-usedSizeBefore;
		freeSize-=(usedSizeAfter-usedSizeBefore);
		/* Delete paradox */
		if (command->GetType()==SCTS_DELETE)
		{
			if (sessionEncoding==XML)
				freeSize-=command->size;
			else
				freeSize-=command->size*3;
		}
		/* 'Unbuild' the command if required */
		if (lastCommandNotBuilt || !build || freeSize<0)
		{
			wsmLockH(sessionContext,SML_FIRST_FREE_ITEM,&tempPtr);
			wsmSetUsedSize(sessionContext,usedSizeBefore-usedSizeAfter);
			wsmUnlockH(sessionContext);
			/* Restore the encoder state if encoding is WBXML */
			if (sessionEncoding==WBXML)
			{
				sctsMemcpy(instanceInfo->encoderState,&encoderState,sizeof(encoderState));
			}
		}
		else
		{
			if (uiCallback && command->GetType()!=SCTS_STATUS) (*uiCallback)(uiCallbackData,command,0,true);
			if (wbxmlConverterContext && command->GetType()!=SCTS_STATUS) command->Build(wbxmlConverterContext);
			command->MakeStub();
			if (isContainer) currentContainer=thisCommand;
			return true;
		}
#endif

		/* Check if the message has reached the maximum size */
		if (freeSize<0)
		{
			sctsStatusLog("Not built, maximum message size reached...\r\n");
			IsFinal(false);
			thisCommand->data=0;
			syncBody.DeleteItem(thisCommand);
			if (closeAll)
			{
				while (currentContainer) InsertCommand(0,build);
			}
			freeSize+=(usedSizeAfter-usedSizeBefore);
			return false;
		}
		if (isContainer) currentContainer=thisCommand;
	}

	if (!lastCommandNotBuilt)
		lastCommandNotBuilt=thisCommand;
	
	/* Build any pending commands */
	if (build)
	{
		while (lastCommandNotBuilt)
		{
			if (!lastCommandNotBuilt->data)
			{
				syncBody.GetParentItem(lastCommandNotBuilt)->data->BuildClose(sessionContext);
				if (uiCallback) (*uiCallback)(uiCallbackData,0,syncBody.GetParentItem(lastCommandNotBuilt)->data->GetType()+1,true);
				if (wbxmlConverterContext) syncBody.GetParentItem(lastCommandNotBuilt)->data->BuildClose(wbxmlConverterContext);
			}
			else
			{
				lastCommandNotBuilt->data->Build(sessionContext);
				if (uiCallback && lastCommandNotBuilt->data->GetType()!=SCTS_STATUS) (*uiCallback)(uiCallbackData,lastCommandNotBuilt->data,0,true);
				if (wbxmlConverterContext && lastCommandNotBuilt->data->GetType()!=SCTS_STATUS) lastCommandNotBuilt->data->Build(wbxmlConverterContext);
				lastCommandNotBuilt->data->MakeStub();
			}
			if (syncBody.GetFirstChildItem(lastCommandNotBuilt))
			{
				lastCommandNotBuilt=syncBody.GetFirstChildItem(lastCommandNotBuilt);
				continue;
			}
			if (syncBody.GetNextSiblingItem(lastCommandNotBuilt))
			{
				lastCommandNotBuilt=syncBody.GetNextSiblingItem(lastCommandNotBuilt);
				continue;
			}
			lastCommandNotBuilt=syncBody.GetParentItem(lastCommandNotBuilt);
			lastCommandNotBuilt=syncBody.GetNextSiblingItem(lastCommandNotBuilt);
		}
	}
	return true;
}
			
SctsCommand *SctsMessage::GetHeader()
{
	return syncBody.GetRootItem()->data;
}

void SctsMessage::Rebuild()
{
	long freeSizeBefore;
	long freeSizeAfter;
	unsigned char *buffer;
	SctsTree<SctsCommand *>::Item *command;
	
	/* Close all containers and empty the workspace buffer */
	command=syncBody.GetRootItem();
	SCTSASSERT(syncBody.GetFirstChildItem(command)==lastCommandNotBuilt);
	
	/* Add the size of the closing tag to the size of the syncHdr and roll back */
#ifdef __SCTS_USING_RTK__
	SCTSASSERT(wsmGetFreeSize(sessionContext,&freeSizeBefore)==SML_ERR_OK);
#endif
	command->data->BuildClose(sessionContext);
#ifdef __SCTS_USING_RTK__
	SCTSASSERT(wsmGetFreeSize(sessionContext,&freeSizeAfter)==SML_ERR_OK);
	wsmLockH(sessionContext,SML_FIRST_FREE_ITEM,&buffer);
	wsmSetUsedSize(sessionContext,-(command->data->size+freeSizeBefore-freeSizeAfter));
	wsmUnlockH(sessionContext);
#endif
	
	/* Rebuild the SyncHdr */
#ifdef __SCTS_USING_RTK__
	/* 
	Get the current free size of the workspace buffer, build the syncHdr, get the
	free size again and hence calculate the size of the syncHdr. Using this size
	calculate the remaining free size of the message (not the workspace buffer).
	*/
	SCTSASSERT(wsmGetFreeSize(sessionContext,&freeSizeBefore)==SML_ERR_OK);
#endif

	command->data->Build(sessionContext);

#ifdef __SCTS_USING_RTK__
	SCTSASSERT(wsmGetFreeSize(sessionContext,&freeSizeAfter)==SML_ERR_OK);
#endif

	/* Adjust the free size */
	freeSize+=command->data->size-(freeSizeBefore-freeSizeAfter);
	command->data->size=freeSizeBefore-freeSizeAfter;

}

/* Delete all unbuilt commands */
void SctsMessage::DeleteAllUnbuilt()
{
	SctsTree<SctsCommand *>::Item *tempCommand;
	while (lastCommandNotBuilt)
	{
		tempCommand=lastCommandNotBuilt;
		lastCommandNotBuilt=syncBody.GetNextSiblingItem(lastCommandNotBuilt);
		syncBody.DeleteItem(tempCommand);
	}
	currentContainer=syncBody.GetRootItem();
	/* Close the message */
	IsFinal(false);
}

/* Build any unbuilt commands */
void SctsMessage::BuildAllUnbuilt()
{
	while (lastCommandNotBuilt)
	{
		if (!lastCommandNotBuilt->data)
		{
			syncBody.GetParentItem(lastCommandNotBuilt)->data->BuildClose(sessionContext);
			if (uiCallback) (*uiCallback)(uiCallbackData,0,syncBody.GetParentItem(lastCommandNotBuilt)->data->GetType()+1,true);
			if (wbxmlConverterContext) syncBody.GetParentItem(lastCommandNotBuilt)->data->BuildClose(wbxmlConverterContext);
		}
		else
		{
			lastCommandNotBuilt->data->Build(sessionContext);
			if (uiCallback && lastCommandNotBuilt->data->GetType()!=SCTS_STATUS) (*uiCallback)(uiCallbackData,lastCommandNotBuilt->data,0,true);
			if (wbxmlConverterContext && lastCommandNotBuilt->data->GetType()!=SCTS_STATUS) lastCommandNotBuilt->data->Build(wbxmlConverterContext);
			lastCommandNotBuilt->data->MakeStub();
		}
		if (syncBody.GetFirstChildItem(lastCommandNotBuilt))
		{
			lastCommandNotBuilt=syncBody.GetFirstChildItem(lastCommandNotBuilt);
			continue;
		}
		if (syncBody.GetNextSiblingItem(lastCommandNotBuilt))
		{
			lastCommandNotBuilt=syncBody.GetNextSiblingItem(lastCommandNotBuilt);
			continue;
		}
		lastCommandNotBuilt=syncBody.GetParentItem(lastCommandNotBuilt);
		lastCommandNotBuilt=syncBody.GetNextSiblingItem(lastCommandNotBuilt);
	}
}


			
	



