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

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.

********************************************************************/
/*************************************************************************/
/* module:          SyncML Command Dispatcher                            */
/*                                                                       */   
/* file:            mgrcmddispatcher.c                                   */
/* target system:   all                                                  */
/* target OS:       all                                                  */   
/*                                                                       */   
/* Description:                                                          */   
/* Core module for dispatching parsed commands and invoking callback     */
/* functions of the application                                          */
/*************************************************************************/



/*************************************************************************
 *  Definitions
 *************************************************************************/

#include <define.h>
/* Include Headers */
#include <smldef.h>
#include <smldtd.h>
#include <smlerr.h>

#include "libmem.h"
#include "xltdec.h"
#include "xltenc.h"
#include "mgr.h"



/* Used external functions */
#ifndef __SML_LITE__  /* these API calls are NOT included in the Toolkit lite version */
  extern Ret_t addInfo(InstanceInfoPtr_t pInfo);
  extern InstanceInfoPtr_t findInfo(InstanceID_t id);
  extern Ret_t removeInfo(InstanceID_t id);
#endif  
Ret_t smlLockReadBuffer(InstanceID_t id, MemPtr_t *pReadPosition, MemSize_t *usedSize);
Ret_t smlUnlockReadBuffer(InstanceID_t id, MemSize_t processedBytes);

/* Prototypes of exported SyncML API functions */
extern Ret_t smlProcessData(InstanceID_t id, SmlProcessMode_t mode);

/**
 * FUNCTION:  smlProcessMgmtTree
 *
 * Build the subtree from SyncML TNDS. 
 *
 * IN:              enc
 *                  The XML encoding
 *
 * IN:              pBufEnd
 *                  The SyncML buffer end boundry
 *
 * IN:              pBufPos
 *                  The SyncML buffer begin boundry
 *
 * OUT:             ppMgmtTree
 *                  The subtree
 *
 * RETURN:          Ret_t
 */
extern Ret_t smlProcessMgmtTree(int enc, char *pBufEnd, char *pBufPos, SmlSubTreePtr_t *ppMgmtTree);

/**
 * FUNCTION:  smlMarshalMgmtTree
 *
 * Build the SyncML TNDS from subtree. 
 *
 * IN:              enc
 *                  The XML encoding
 *
 * IN:              pBufEnd
 *                  The SyncML buffer end boundry
 *
 * IN OUT:          ppBufPos
 *                  The SyncML buffer begin boundry, also point to the message boundry
 *
 * IN:              pMgmtTree
 *                  The subtree
 *
 * RETURN:          Ret_t
 */
extern Ret_t smlMarshalMgmtTree(int enc, char *pBufEnd, char **ppBufPos, SmlSubTreePtr_t pMgmtTree);

/* Private function prototypes */
static Ret_t mgrProcessNextCommand(InstanceID_t id, InstanceInfoPtr_t pInstanceInfo);
static Ret_t mgrProcessStartMessage(InstanceID_t id, InstanceInfoPtr_t pInstanceInfo);
Ret_t mgrResetWorkspace (InstanceID_t id);



/*************************************************************************
 *  Exported SyncML API functions
 *************************************************************************/




/**
 * FUNCTION:  smlProcessData
 *
 * Start the parsing of the XML code in the workspace buffer, 
 * dispatches the interpreted command and calls the corresponding callback 
 * functions provided by the application.
 *
 * IN:              InstanceID_t
 *                  The SyncML instance id is used for referencing the 
 *                  workspace buffer from the XML content is parsed          
 *
 * IN:              ProcessMode_t
 *                  Mode of processing, Defines, if only the first or next 
 *                  XML command is parsed or if all commands are processed 
 *                  subsequently until the end of the entire workspace buffer 
 *                  is reached. The NEXT_COMMAND flag defines the blocking mode, 
 *                  the ALL_COMMANDS tag defines the non-blocking mode.  
 *
 * RETURN:          Ret_t
 */
SML_API Ret_t smlProcessData(InstanceID_t id, SmlProcessMode_t mode)
{

  
  /* --- Definitions --- */
  InstanceInfoPtr_t   pInstanceInfo;               // state info for the given instanceID
  Ret_t               rc;                          // Temporary return code saver


  /* --- Find that instance --- */
  #ifdef __SML_LITE__  /* Only ONE instance is supported in the Toolkit lite version */
    pInstanceInfo = mgrGetInstanceListAnchor();
  #else
    pInstanceInfo = (InstanceInfoPtr_t) findInfo(id);
  #endif

  if (pInstanceInfo==NULL) return SML_ERR_MGR_INVALID_INSTANCE_INFO;


  /* --- Are callback functions defined? --- */
  if (pInstanceInfo->callbacks==NULL) return SML_ERR_COMMAND_NOT_HANDLED;


  /* --- Is parsing already in progress? --- */
  if (pInstanceInfo->decoderState==NULL) 
    {
    /* No! Parse the Message header section first */    
    rc = mgrProcessStartMessage(id, pInstanceInfo);    

    /* 
	SCTSTK 12/06/2002 (mashok@in.ibm.com)
	If commands are being processed one at a time don't process the first command,
	this gives the start message handler a chance to stop the parsing loop if any
	errors are encountered.
	*/
	if (mode!=SML_ALL_COMMANDS) return rc;

	if (rc!=SML_ERR_OK) return rc;
    }
    
    
  /* --- Parse now the Message body section! --- */ 
  do
    {
    rc=mgrProcessNextCommand(id, pInstanceInfo);
    } while // keep processing while no error occurs,
            // AND the document end was not reached (decoderState has been invalidated),
            // AND the ALL_COMMAND mode is used
            ((rc==SML_ERR_OK)                                    
            &&((pInstanceInfo->decoderState)!=NULL)   
            &&(mode==SML_ALL_COMMANDS));                         

      if (rc != SML_ERR_OK)
       {
         // abort, unlock the buffer again without changing it's current position
         smlUnlockReadBuffer(id, (MemSize_t)0);  
 	     // Reset the decoder module (free the decoding object)
 	     xltDecReset(pInstanceInfo->decoderState);
         // this decoding job is over! reset Instance Info pointer
         pInstanceInfo->decoderState=NULL;     
 	     // Reset the Workspace (the remaining unparsed document fragment will be lost)
 	     mgrResetWorkspace(id);
       }

  return rc;
  
}

/**
 * FUNCTION:  smlProcessMgmtTree
 *
 * Build the subtree from SyncML TNDS. 
 *
 * IN:              enc
 *                  The XML encoding
 *
 * IN:              pBufEnd
 *                  The SyncML buffer end boundry
 *
 * IN:              pBufPos
 *                  The SyncML buffer begin boundry
 *
 * OUT:             ppMgmtTree
 *                  The subtree
 *
 * RETURN:          Ret_t
 */
SML_API Ret_t smlProcessMgmtTree(int enc, char *pBufEnd, char *pBufPos, SmlSubTreePtr_t *ppMgmtTree)
{
    Ret_t rcode = -1;
    char *pBufStart = pBufPos;
    XltDecoderPtr_t pDecoder = 0;
    rcode = xltDecDDF(!enc?SML_XML:SML_WBXML, (MemPtr_t)pBufEnd, (MemPtr_t*)&pBufStart, &pDecoder, ppMgmtTree);
    xltDecTerminate(pDecoder);
    return rcode;
}

/**
 * FUNCTION:  smlMarshalMgmtTree
 *
 * Build the SyncML TNDS from subtree. 
 *
 * IN:              enc
 *                  The XML encoding
 *
 * IN:              pBufEnd
 *                  The SyncML buffer end boundry
 *
 * IN OUT:          ppBufPos
 *                  The SyncML buffer begin boundry, also point to the message boundry
 *
 * IN:              pMgmtTree
 *                  The subtree
 *
 * RETURN:          Ret_t
 */
SML_API Ret_t smlMarshalMgmtTree(int enc, char *pBufEnd, char **ppBufPos, SmlSubTreePtr_t pMgmtTree)
{
  Ret_t               rc;
  XltEncoderPtr_t     pEncoder = 0;

  /* --- Call the encoder module --- */
  /*     (Saves the returned encoder state to the corresponding instanceInfo structure */
  //rc = xltEncDDFInit(enc==0?SML_XML:SML_WBXML,
  //                   pBufEnd, ppBufPos, 
  //                   &pEncoder);
  rc = xltEncDDFInit(!enc?SML_XML:SML_WBXML,
                     pBufEnd, ppBufPos, 
                     &pEncoder);
  if (rc != SML_ERR_OK) {
  	// Reset the encoder module (free the encoding object)
    xltEncReset(pEncoder);
    return rc;
    }

  rc = xltEncDDFAppend(pEncoder, pBufEnd, pMgmtTree, ppBufPos);
  if (rc != SML_ERR_OK) {
  	// Reset the encoder module (free the encoding object)
    xltEncReset(pEncoder);
    return rc;
    }

  rc = xltEncDDFTerminate(pEncoder, pBufEnd, ppBufPos);
  return rc;
}




/*************************************************************************
 *  Private Functions
 *************************************************************************/




/**
 * FUNCTION: 
 * Parses the header information at the beginning of an SyncML document.
 *
 * IN:              InstanceID
 *                  current InstanceID to pass to callback functions
 *
 * IN/OUT:          InstanceInfo
 *                  state information of the given InstanceID (decoder state will be changed)
 *
 * RETURN:          Return value of the Parser,            
 *                  SML_ERR_OK if next command was handled successfully
 */
static Ret_t mgrProcessStartMessage(InstanceID_t id, InstanceInfoPtr_t pInstanceInfo)
{


  /* --- Definitions --- */ 
  Ret_t               rc;                          // Temporary return code saver
  SmlSyncHdrPtr_t     pContent=NULL;               // data of the command to process
  MemPtr_t            pCurrentReadPosition ;        // current Position from which is read
  MemPtr_t            pBeginPosition;              // saves the first position which has been reading
  MemSize_t           usedSize ;                    // size of used memory to be read 
  

  /* --- Get Read Access to the workspace --- */
  rc = smlLockReadBuffer(id, &pCurrentReadPosition, &usedSize);

  if (rc!=SML_ERR_OK) {
    // abort, unlock the buffer again without changing it's current position
    smlUnlockReadBuffer(id, (MemSize_t)0);  
    return rc;
    }
    
  // Remember the position we have started reading    
  pBeginPosition=pCurrentReadPosition; 
  
  /* --- Start new decoding sequence and pass returned decoder status structure to instanceInfo --- */
  rc = xltDecInit(pInstanceInfo->instanceOptions->encoding, 
                  pCurrentReadPosition+usedSize-1, &pCurrentReadPosition, 
                  (XltDecoderPtr_t *)&(pInstanceInfo->decoderState), &pContent);

  if (rc!=SML_ERR_OK) {
    // abort, unlock the buffer again without changing it's current position
    smlUnlockReadBuffer(id, (MemSize_t)0);  
	// Reset the decoder module (free the decoding object)
	xltDecReset(pInstanceInfo->decoderState);
    // this decoding job is over! reset Instance Info pointer
    pInstanceInfo->decoderState=NULL;     
	// Reset the Workspace (the remaining unparsed document fragment will be lost)
	mgrResetWorkspace(id);
    return rc;
    }

  /* --- End Read Access to the workspace --- */
  rc = smlUnlockReadBuffer(id, (MemSize_t)pCurrentReadPosition-(MemSize_t)pBeginPosition);
  if (rc!=SML_ERR_OK) return rc;


  /* --- Perform callback to handle the beginning of a new message --- */
  if (pInstanceInfo->callbacks->startMessageFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
  rc=pInstanceInfo->callbacks->startMessageFunc(id, pInstanceInfo->userData, pContent); 

  if (rc != SML_ERR_OK)
   {
     // abort, unlock the buffer again without changing it's current position
     smlUnlockReadBuffer(id, (MemSize_t)0);  
   	 // Reset the decoder module (free the decoding object)
 	 xltDecReset(pInstanceInfo->decoderState);
     // this decoding job is over! reset Instance Info pointer
     pInstanceInfo->decoderState=NULL;     
 	 // Reset the Workspace (the remaining unparsed document fragment will be lost)
 	 mgrResetWorkspace(id);
    }
   

 
  return rc;
   
}



/**
 * FUNCTION: 
 * Parses the next Sync Command in the sync document.
 *
 * IN:              InstanceID
 *                  current InstanceID to pass to callback functions
 *
 * IN:              InstanceInfo
 *                  state information of the given InstanceID
 *
 * RETURN:          Return value of the Parser of the called application callback,            
 *                  SML_ERR_OK if next command was handled successfully
 */
static Ret_t mgrProcessNextCommand(InstanceID_t id, InstanceInfoPtr_t pInstanceInfo)
{

  /* --- Definitions --- */ 
  Ret_t               rc;                          // Temporary return code saver
  SmlProtoElement_t      cmdType;                     // ID of the command to process
  VoidPtr_t           pContent=NULL;               // data of the command to process
  MemPtr_t            pCurrentReadPosition;        // current Position from which is read
  MemPtr_t            pBeginPosition;              // saves the first position which has been reading
  MemSize_t           usedSize;                    // size of used memory to be read 
  Boolean_t           final;                       // flag indicates last message within a package
  

  /* --- Get Read Access to the workspace --- */
  rc = smlLockReadBuffer(id, &pCurrentReadPosition, &usedSize);
  
  if (rc!=SML_ERR_OK) {
    // abort, unlock the buffer again without changing it's current position
    smlUnlockReadBuffer(id, (MemSize_t)0);  
    return rc;
    }

  // Remember the position we have started reading
  pBeginPosition=pCurrentReadPosition;             
  

  /* --- Parse next Command --- */ 
  rc = xltDecNext(pInstanceInfo->decoderState, pCurrentReadPosition+usedSize, &pCurrentReadPosition, &cmdType, &pContent);

  if (rc!=SML_ERR_OK) {
    // abort, unlock the buffer again without changing it's current position
    smlUnlockReadBuffer(id, (MemSize_t)0);  
	// Reset the decoder module (free the decoding object)
	xltDecReset(pInstanceInfo->decoderState);
    // this decoding job is over! reset Instance Info pointer
    pInstanceInfo->decoderState=NULL;           
	// Reset the Workspace (the remaining unparsed document fragment will be lost)
	mgrResetWorkspace(id);
    return rc;
    }


  /* --- End Read Access to the workspace --- */
  rc = smlUnlockReadBuffer(id, (MemSize_t)pCurrentReadPosition-(MemSize_t)pBeginPosition);

  if (rc!=SML_ERR_OK) {
    // abort, unlock the buffer again without changing it's current position
    smlUnlockReadBuffer(id, (MemSize_t)0);  
    return rc;
    }


    
  /* --- Did we reach end of synchronization document? --- */
  if (((XltDecoderPtr_t)(pInstanceInfo->decoderState))->finished!=0) {
      final = ((XltDecoderPtr_t)(pInstanceInfo->decoderState))->final; // flag is returned to appl. with callback
      rc=xltDecTerminate(pInstanceInfo->decoderState);

      if (rc!=SML_ERR_OK) 
	  {
        // abort, unlock the buffer again without changing it's current position
        smlUnlockReadBuffer(id, (MemSize_t)0);  
        // Reset the decoder module (free the decoding object)
	    xltDecReset(pInstanceInfo->decoderState);
        // this decoding job is over! reset Instance Info pointer
        pInstanceInfo->decoderState=NULL;           
	    // Reset the Workspace (the remaining unparsed document fragment will be lost)
    	mgrResetWorkspace(id);
        return rc;
	  }

      // this decoding job is over! reset Instance Info pointer
	  // (the decoding object itself has been freed by the decoder)
      pInstanceInfo->decoderState=NULL;
      
      // Call the callback for handling an message ending
      if (pInstanceInfo->callbacks->endMessageFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      rc=pInstanceInfo->callbacks->endMessageFunc(id, pInstanceInfo->userData, final);

      return rc; 
      } 

     
  /* --- Dispatch parsed command (and call the applications command handler function)--- */
  switch (cmdType)
    {
    /* Handle ADD Command */
    case SML_PE_ADD:
      if (pInstanceInfo->callbacks->addCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->addCmdFunc (id, pInstanceInfo->userData, pContent); 
      break;

    /* Handle ALERT Command */
    case SML_PE_ALERT:
      if (pInstanceInfo->callbacks->alertCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->alertCmdFunc (id, pInstanceInfo->userData, pContent); 
      break;
        
    /* Handle DELETE Command */
    case SML_PE_DELETE:
      if (pInstanceInfo->callbacks->deleteCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->deleteCmdFunc (id, pInstanceInfo->userData, pContent);  
      break;

    /* Handle PUT Command */
    case SML_PE_PUT:
      if (pInstanceInfo->callbacks->putCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->putCmdFunc (id, pInstanceInfo->userData, pContent);   
      break;

    /* Handle GET Command */
    case SML_PE_GET:
      if (pInstanceInfo->callbacks->getCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->getCmdFunc (id, pInstanceInfo->userData, pContent);     
      break;
        
#ifdef MAP_RECEIVE      
    /* Handle MAP Command */
    case SML_PE_MAP:
      if (pInstanceInfo->callbacks->mapCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->mapCmdFunc (id, pInstanceInfo->userData, pContent);     
      break;
#endif
      
#ifdef RESULT_RECEIVE
    /* Handle RESULTS Command */
    case SML_PE_RESULTS:
      if (pInstanceInfo->callbacks->resultsCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->resultsCmdFunc (id, pInstanceInfo->userData, pContent); 
      break;
#endif

    /* Handle STATUS Command */
    case SML_PE_STATUS:
      if (pInstanceInfo->callbacks->statusCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->statusCmdFunc (id, pInstanceInfo->userData, pContent);  
      break;

    /* Handle START SYNC Command */
    case SML_PE_SYNC_START:
      if (pInstanceInfo->callbacks->startSyncFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->startSyncFunc (id, pInstanceInfo->userData, pContent);  
      break;

    /* Handle END SYNC Command */
    case SML_PE_SYNC_END:
      if (pInstanceInfo->callbacks->endSyncFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->endSyncFunc (id, pInstanceInfo->userData);              
      break;

    /* Handle REPLACE Command */
    case SML_PE_REPLACE:
      if (pInstanceInfo->callbacks->replaceCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->replaceCmdFunc (id, pInstanceInfo->userData, pContent);  
      break;

    /* Handle Final Flag */
    case SML_PE_FINAL:
      // if a FINAL Flag appears do nothing
      return SML_ERR_OK;
      break;


#ifdef SEARCH_RECEIVE  /* these API calls are NOT included in the Toolkit lite version */

    /* Handle SEARCH Command */
    case SML_PE_SEARCH:
      if (pInstanceInfo->callbacks->searchCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->searchCmdFunc (id, pInstanceInfo->userData, pContent);  
      break;
#endif

#ifdef SEQUENCE_RECEIVE
    /* Handle START SEQUENCE Command */
    case SML_PE_SEQUENCE_START:
      if (pInstanceInfo->callbacks->startSequenceFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->startSequenceFunc (id, pInstanceInfo->userData, pContent); 
      break;

    /* Handle END SEQUENCE Command */
    case SML_PE_SEQUENCE_END:
      if (pInstanceInfo->callbacks->endSequenceFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->endSequenceFunc (id, pInstanceInfo->userData);             
      break;
#endif

#ifdef ATOMIC_RECEIVE

    /* Handle START ATOMIC Command */
    case SML_PE_ATOMIC_START:
      if (pInstanceInfo->callbacks->startAtomicFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->startAtomicFunc (id, pInstanceInfo->userData, pContent);
      break;
        
    /* Handle END ATOMIC Command */
    case SML_PE_ATOMIC_END:
      if (pInstanceInfo->callbacks->endAtomicFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->endAtomicFunc (id, pInstanceInfo->userData);            
      break;
#endif
      
#ifdef COPY_RECEIVE

    /* Handle COPY Command */
    case SML_PE_COPY:
      if (pInstanceInfo->callbacks->copyCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->copyCmdFunc (id, pInstanceInfo->userData, pContent);    
      break;
#endif

#ifdef EXEC_RECEIVE

    /* Handle EXEC Command */
    case SML_PE_EXEC:
      if (pInstanceInfo->callbacks->execCmdFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
      return pInstanceInfo->callbacks->execCmdFunc (id, pInstanceInfo->userData, pContent);    
      break;
        

#endif
    
    /* Handle ERROR DETECTED  */
    //case SML_PE_ERROR:
    //  if (pInstanceInfo->callbacks->handleErrorFunc==NULL) return SML_ERR_COMMAND_NOT_HANDLED;
    //  return pInstanceInfo->callbacks->handleErrorFunc (id, pInstanceInfo->userData);        
    //  break;

    /* --- Invalid Command Element --- */
    default:
      return SML_ERR_XLT_INVAL_PROTO_ELEM;   
      break;
    } 
 
 
   
}
