/*
    Reads the next action from the script file, verify correctness and return its individual elements
*/
McpBtsSpStatus _MCP_BTS_SP_GetNextAction(McpBtsSpContext *context, 
                                         McpU16 *actionDataActualLen, 
                                         McpBtsSpScriptActionType *actionType,
                                         McpU8 *actionData, 
                                         McpU16 actionDataMaxLen,
                                         McpBool *moreActions)
{
    McpBtsSpStatus spStatus;
    MCP_BTS_SP_ScriptActionHeader actionHeader;

    /* Read the action header */
    spStatus = _MCP_BTS_SP_ReadActionHeader(context, &actionHeader);

    if (spStatus != MCP_BTS_SP_STATUS_SUCCESS)
    {
        MCP_LOG_FATAL(("_MCP_BTS_SP_GetNextAction: Failed reading action header (%s)",
                       _MCP_BTS_SP_DebugStatusStr(spStatus)));
        return spStatus;
    }

    /*
        Parse action header. The format is:

        [ToDo] How is the binary data represented? Do we need to handle endianity?
    */
      *actionType = actionHeader.actionType;
    *actionDataActualLen = actionHeader.actionDataLen;

    /* Verify that actual action data len doesn't exceed the action data buffer max size */
    if (*actionDataActualLen > actionDataMaxLen)
    {       
        MCP_LOG_FATAL(("_MCP_BTS_SP_GetNextAction: Action Data is too long (%d), Max: %d.", 
                            *actionDataActualLen, actionDataMaxLen));
        return MCP_BTS_SP_STATUS_INVALID_SCRIPT;
    }

    /* Read the action data */

    spStatus = _MCP_BTS_SP_ReadScript(context, actionData, *actionDataActualLen);

    if (spStatus != MCP_BTS_SP_STATUS_SUCCESS)
    {
        MCP_LOG_FATAL(("_MCP_BTS_SP_GetNextAction: Failed reading action Data (%s)",
                       _MCP_BTS_SP_DebugStatusStr(spStatus)));
        return spStatus;
    }

    /* If we completed reading the entire script it means there are no more actions (we just read the last one) */
    if (context->scriptProcessingPos >= context->scriptSize)
    {
        *moreActions = MCP_FALSE;
    }
    else
    {
        *moreActions = MCP_TRUE;
    }

    return spStatus;
}
void MCP_BTS_SP_HciCmdCompleted(McpBtsSpContext *context, 
                                McpU8 *eventParms,
                                McpU8 eventParmsLen)
{
    MCP_UNUSED_PARAMETER(eventParms);
    MCP_UNUSED_PARAMETER(eventParmsLen);
    
/* 
    [ToDo] The eventParms currentl doens't include the HCI opcode, event status, etc. Only the HCI return parms 

    Need to see how to handle this in FMC_Transport
*/
#if 0   
    McpBtsSpStatus spStatus;

    /* Initialize to an invalid event status (in case there is no status available in the event parameters) */
    McpBtsSpHciEventStatus  eventStatus = (McpBtsSpHciEventStatus)0xFF;

    /* Extract event status from parms */
    if (eventParmsLen >= MCP_BTS_SP_HCI_EVENT_STATUS_OFFSET)
    {
        eventStatus = eventParms[MCP_BTS_SP_HCI_EVENT_STATUS_OFFSET];
    }
    
    if (eventType != MCP_BTS_SP_HCI_EVENT_COMMAND_COMPLETE)
    {
        MCP_LOG_FATAL(("MCP_BTS_SP_HciCmdCompleted: Unexpected HCI Event (%d), Status: %d",
                       eventType,
                       eventStatus));   
        _MCP_BTS_SP_CompleteExecution(context,
                                      MCP_BTS_SP_STATUS_EXECUTION_FAILED_UNEXPECTED_HCI_EVENT);
        return;
    }

    /* Now we know we have the expected command complete event */

    /* verify that the command completed successfully */
    if (eventStatus != MCP_BTS_SP_HCI_EVENT_STATUS_SUCCESS)
    {
        MCP_LOG_FATAL(("MCP_BTS_SP_HciCmdCompleted: Command completed unsuccessfully (%d)",
                       eventStatus));  
        _MCP_BTS_SP_CompleteExecution(context,
                                      MCP_BTS_SP_STATUS_EXECUTION_FAILED_COMMAND_FAILED);
        return;
    }
    
#endif

    /* [ToDo] - Pass the command complete status as the event data (instead of the 3rd argument) */
    _MCP_BTS_SP_ProcessScriptCommands(context, MCP_BTS_SP_PROCESSING_EVENT_COMMAND_COMPLETE, NULL);
}
/*
    This function is called to complete script execution from all possible execution paths.
*/
void _MCP_BTS_SP_CompleteExecution(McpBtsSpContext *context, McpBtsSpStatus executionStatus)
{
    if (executionStatus == MCP_BTS_SP_STATUS_SUCCESS)
    {
        MCP_LOG_INFO(("_MCP_BTS_SP_CompleteExecution: SCRIPT EXECUTION SUCCESSFULLY COMPLETED"));
    }
    else if (executionStatus == MCP_BTS_SP_STATUS_EXECUTION_ABORTED)
    {
        MCP_LOG_INFO(("_MCP_BTS_SP_CompleteExecution: SCRIPT EXECUTION ABORTED"));
    }
    else
    {
        MCP_LOG_FATAL(("_MCP_BTS_SP_CompleteExecution: SCRIPT EXECUTION FAILED (%s)", 
                            _MCP_BTS_SP_DebugStatusStr(executionStatus)));
    }

    /* Close script file ([ToDo] - Memory scripts ) */
    _MCP_BTS_SP_CloseScript(context);

    /* If execution completed asynchronously, notify the callback on execution completion status */
    if (context->asyncExecution == MCP_TRUE)
    {
        (context->cbData.execCompleteCb)(context, executionStatus);
    }
}
/*
    This function is called when transport on failed
*/
_CcmImStatus _CCM_IM_BtTranSm_HandlerTranOnFailed(_CcmIm_BtTranSm_Obj *thisObj, void *eventData)
{
    MCP_UNUSED_PARAMETER(eventData);

    MCP_LOG_FATAL(("_CCM_IM_BtTranSm_HandlerTranOnFailed: Tran On Failed"));
    
    return _CCM_IM_BtTranSm_PerformCompletion(thisObj, 
                                                _CCM_IM_BT_TRAN_SM_COMPLETED_EVENT_TRAN_ON_COMPLETED,
                                                _CCM_IM_STATUS_FAILED);
}
McpBtsSpStatus _MCP_BTS_SP_ProcessNextScriptAction(McpBtsSpContext *context, McpBool *moreActions)
{
    McpBtsSpStatus spStatus;
    McpBtsSpScriptActionDataLenType actionDataActualLen;
    McpBtsSpScriptActionType actionType;

    /* 
        [ToDo] - Check the need for the mopreCommand and its usage
        This condition should be true when the script contains no actions (just a header) => 
        the test may be moved elsewhere, and tested only once, after reading the header
    */
    if (context->scriptProcessingPos >= context->scriptSize)
    {
        *moreActions = MCP_FALSE;
        return MCP_BTS_SP_STATUS_SUCCESS;
    }

    /* Read next action from script and parse it */
    spStatus = _MCP_BTS_SP_GetNextAction(context, 
                                         &actionDataActualLen,
                                         &actionType,
                                         context->scriptActionData, 
                                         MCP_BTS_SP_MAX_SCRIPT_ACTION_DATA_LEN,
                                         moreActions);

    if (spStatus != MCP_BTS_SP_STATUS_SUCCESS)
    {
        MCP_LOG_FATAL(("_MCP_BTS_SP_ProcessNextScriptAction: _MCP_BTS_SP_GetNextAction Failed (%s)", 
                       _MCP_BTS_SP_DebugStatusStr(spStatus)));
        return spStatus;
    }

    /* Handle actions according to their type */
    /* [ToDo] Detect the case where 2 commands are sent without waiting for an event in-between */
    switch (actionType)
    {
        case MCP_BTS_SP_SCRIPT_ACTION_SEND_COMMAND:
        {
            /* Prepare Send HCI Command parameters */
            /* [ToDo] Define HCI command structure / define symbolic constants for offsets */
            McpU8   hciParmLen = context->scriptActionData[3];
            McpU16  hciOpcode;
            McpU8   *hciParms = &context->scriptActionData[4];
            /*[ToDo Zvi] We should use utils general function here */
            hciOpcode = (McpU16)( ((McpU16) *(&context->scriptActionData[1]+1) << 8) | ((McpU16) *&context->scriptActionData[1]) ); 
                        
            /* Send the HCI command via the client's supplied callback function */
            spStatus = (context->cbData.sendHciCmdCb)(  context,
                                                hciOpcode,
                                                hciParms,
                                                hciParmLen);
            
            /* 
                We return pending only when waiting for a command complete. 
                Otherwise, we should be called again by the caller of this function
            */
            if (spStatus == MCP_BTS_SP_STATUS_PENDING)
            {
                spStatus = MCP_BTS_SP_STATUS_SUCCESS;
            }
            else
            {
                /* [ToDo] - FATAL may be too severe as an uncoditional handling of errors */
                MCP_LOG_FATAL(("_MCP_BTS_SP_ProcessNextScriptAction: Failed sending HCI Command via the callback (%d)", 
                               _MCP_BTS_SP_DebugStatusStr(spStatus)));
            }
        }
        break;

        case MCP_BTS_SP_SCRIPT_ACTION_WAIT_FOR_COMMAND_COMPLETE:
            /* Nothing more to do, wait for client to call MCP_BTS_SP_HciCmdCompleted to continue processing */
            spStatus = MCP_BTS_SP_STATUS_PENDING;
            break;

        /* Invalid action for Software scripts */
        case MCP_BTS_SP_SCRIPT_ACTION_SERIAL_PORT_PARMS:
        {
            MCP_BTS_SP_ActionSerialPortParameters *pParams = (MCP_BTS_SP_ActionSerialPortParameters *)&context->scriptActionData[0];

            spStatus = (context->cbData.setTranParmsCb)(context, pParams->baudRate, pParams->flowControl);

            if (spStatus != MCP_BTS_SP_STATUS_SUCCESS)
            {
                /* [ToDo] - FATAL may be too severe as an uncoditional handling of errors */
                MCP_LOG_FATAL(("_MCP_BTS_SP_ProcessNextScriptAction: Setting Speed Callback Failed (%d)", 
                               _MCP_BTS_SP_DebugStatusStr(spStatus)));
            }
        }   
        break;
        
        /* Invalid action for Software scripts */
        case MCP_BTS_SP_SCRIPT_ACTION_RUN_SCRIPT:
            MCP_LOG_FATAL(("_MCP_BTS_SP_ProcessNextScriptAction: Run Script action Disallowed in BTS File"));
            spStatus = MCP_BTS_SP_STATUS_INVALID_SCRIPT;
            break;

        case MCP_BTS_SP_SCRIPT_ACTION_SLEEP:
        {
            /* Delay action - sleep for the specified amount of time */
            MCP_BTS_SP_ActionSleep *pParams = (MCP_BTS_SP_ActionSleep*)&context->scriptActionData[0];
            MCP_HAL_OS_Sleep(pParams->nDurationInMillisec);
        }
        break;
        
        case MCP_BTS_SP_SCRIPT_ACTION_REMARK:
            /* Nothing to do */
            spStatus = MCP_BTS_SP_STATUS_SUCCESS;
            break;

        /* Invalid action type detected */      
        default:
            MCP_LOG_FATAL(("_MCP_BTS_SP_ProcessNextScriptAction: Invalid Action (%d) in BTS File", actionType));
            spStatus = MCP_BTS_SP_STATUS_INVALID_SCRIPT;
            break;
            
    }

    return spStatus;
}
/*
    The main state machine of the script processor
*/
McpBtsSpStatus _MCP_BTS_SP_ProcessScriptCommands(McpBtsSpContext *context,
                                                 McpBtsSpProcessingEvent processingEvent,
                                                 void *eventData)
{
    McpBtsSpStatus spStatus = MCP_BTS_SP_STATUS_SUCCESS;
    McpBool keepProcessing;

    MCP_UNUSED_PARAMETER(eventData);
    
    MCP_LOG_INFO(("_MCP_BTS_SP_ProcessScriptCommands: Processing Event: %d", processingEvent));

    keepProcessing = MCP_TRUE;
    
    while ((keepProcessing == MCP_TRUE) && (context->abortRequested == MCP_FALSE))
    {
        keepProcessing = MCP_FALSE;

        MCP_LOG_INFO(("_MCP_BTS_SP_ProcessScriptCommands: State = %d", context->processingState));
        
        switch (context->processingState)
        {
            case MCP_BTS_SP_PROCESSING_STATE_NONE:

                if (processingEvent == MCP_BTS_SP_PROCESSING_EVENT_START)
                {
                    context->processingState = MCP_BTS_SP_PROCESSING_STATE_PROCESS_NEXT_ACTION;             
                }
                else
                {
                    MCP_LOG_FATAL(("_MCP_BTS_SP_ProcessScriptCommands: Unexpected Processing Event (%d)",
                                   processingEvent));

                    context->processingState = MCP_BTS_SP_PROCESSING_STATE_DONE;
                    spStatus = MCP_BTS_SP_STATUS_INTERNAL_ERROR;
                }

                keepProcessing = MCP_TRUE;
                
            break;

            case MCP_BTS_SP_PROCESSING_STATE_PROCESS_NEXT_ACTION:
            {
                McpBool moreActions;
                
                /* Process next script action as long as we do not have to wait for a command complete event */
                do {
                    spStatus = _MCP_BTS_SP_ProcessNextScriptAction(context, &moreActions);
                } while ((spStatus == MCP_BTS_SP_STATUS_SUCCESS) && (moreActions == MCP_TRUE));

                /* [ToDo] - Handle the case of the last actio in the script being a "Send Command" (illegal) */
                if (spStatus == MCP_BTS_SP_STATUS_PENDING)
                {
                    /* The first time we wait for a command complete event we will record that fact */
                    context->asyncExecution = MCP_TRUE;

                    /* */
                    if (moreActions == MCP_TRUE)
                    {
                        context->processingState = MCP_BTS_SP_PROCESSING_STATE_WAIT_FOR_COMMAND_COMPLETE;
                    }
                    else
                    {
                        context->processingState = MCP_BTS_SP_PROCESSING_STATE_WAIT_FOR_LAST_COMMAND_COMPLETE;
                    }
                }
                else
                {
                    /* 
                        We get here in 2 cases:
                        1. script processing error
                        2. Last script action was not a "Send Command" action

                        In that case we terminate. spStatus stores the completion status
                    */
                    context->processingState = MCP_BTS_SP_PROCESSING_STATE_DONE;
                    keepProcessing = MCP_TRUE;
                }
            }
            break;
            
            case MCP_BTS_SP_PROCESSING_STATE_WAIT_FOR_COMMAND_COMPLETE:

                if (processingEvent == MCP_BTS_SP_PROCESSING_EVENT_COMMAND_COMPLETE)
                {
                    context->processingState = MCP_BTS_SP_PROCESSING_STATE_PROCESS_NEXT_ACTION; 
                }
                else
                {
                    MCP_LOG_FATAL(("_MCP_BTS_SP_ProcessScriptCommands: Unexpected Processing Event (%d)",
                                   processingEvent));

                    context->processingState = MCP_BTS_SP_PROCESSING_STATE_DONE;
                    spStatus = MCP_BTS_SP_STATUS_INTERNAL_ERROR;
                }
                
                keepProcessing = MCP_TRUE;
                
            break;

            case MCP_BTS_SP_PROCESSING_STATE_WAIT_FOR_LAST_COMMAND_COMPLETE:
                
                if (processingEvent == MCP_BTS_SP_PROCESSING_EVENT_COMMAND_COMPLETE)
                {
                    spStatus = MCP_BTS_SP_STATUS_SUCCESS;
                    context->processingState = MCP_BTS_SP_PROCESSING_STATE_DONE;
                }
                else
                {
                    MCP_LOG_FATAL(("_MCP_BTS_SP_ProcessScriptCommands: Unexpected Processing Event (%d)",
                                   processingEvent));

                    context->processingState = MCP_BTS_SP_PROCESSING_STATE_DONE;
                    spStatus = MCP_BTS_SP_STATUS_INTERNAL_ERROR;
                }

                keepProcessing = MCP_TRUE;
                
            break;
            
            case MCP_BTS_SP_PROCESSING_STATE_DONE:

                MCP_LOG_INFO(("_MCP_BTS_SP_ProcessScriptCommands: Completed (%s)",
                              _MCP_BTS_SP_DebugStatusStr(spStatus)));
                
                _MCP_BTS_SP_CompleteExecution(context, spStatus);

            break;
            
            default:
                MCP_LOG_FATAL(("_MCP_BTS_SP_ProcessScriptCommands: Invalid Processing State (%s)",
                               context->processingState));

                context->processingState = MCP_BTS_SP_PROCESSING_STATE_DONE;
                spStatus = MCP_BTS_SP_STATUS_INTERNAL_ERROR;
                keepProcessing = MCP_TRUE;
        }
    }

    if (context->abortRequested == MCP_TRUE)
    {
        _MCP_BTS_SP_CompleteExecution(context, MCP_BTS_SP_STATUS_EXECUTION_ABORTED);
    }
    
    return spStatus;
}