#include "cmPrefix.h"
#include "cmGlobal.h"
#include "cmFloatTypes.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmFileSys.h"
#include "cmJson.h"
#include "cmThread.h"
#include "dsp/cmDspValue.h"
#include "cmMsgProtocol.h"
#include "cmAudDspIF.h"


cmAiH_t cmAiNullHandle = cmSTATIC_NULL_HANDLE;

typedef struct
{
  cmErr_t      err;
  cmAdIfParm_t parms;
  cmJsonH_t    jsH;
} cmAi_t;

cmAi_t* _cmAiHandleToPtr( cmAiH_t h )
{
  cmAi_t* p = (cmAi_t*)h.h;
  assert(p != NULL);
  return p;
}


// Dispatch a message to the client application.
// This function is called from within cmTsQueueDequeueMsg() which is called
// by cmAdIfDispatchMsgToHost().  
cmRC_t _cmAiDispatchMsgToClient(void* cbDataPtr, unsigned msgByteCnt, const void* msgDataPtr )
{
  cmAi_t*       p    = (cmAi_t*)cbDataPtr;
  cmDspUiHdr_t* m    = (cmDspUiHdr_t*)msgDataPtr;
  cmRC_t        rc   = cmOkRC;

  switch( m->uiId )
  {
    case kStatusSelAsId:
      {
        // handle a status mesage
        const char*               base        = (const char*)msgDataPtr;
        const cmAudioSysStatus_t* st          = (const cmAudioSysStatus_t*)(base + (2 * sizeof(unsigned)));
        const double*             iMeterArray = (const double*)(st + 1);
        const double*             oMeterArray = iMeterArray + st->iMeterCnt;
        rc = p->parms.dispatchRecd.statusFunc(p->parms.dispatchRecd.cbDataPtr, st, iMeterArray, oMeterArray );
      }
      break;

    case kSsInitSelAsId:
      {
        // handle an ssInit message
        const cmAudioSysSsInitMsg_t* sip       = (const cmAudioSysSsInitMsg_t*)msgDataPtr;
        const char*                  iDevLabel = (const char*)(sip+1);
        const char*                  oDevLabel = iDevLabel + strlen(iDevLabel) + 1;
        rc = p->parms.dispatchRecd.ssInitFunc(p->parms.dispatchRecd.cbDataPtr, sip, iDevLabel, oDevLabel );
      }
      break;

    case kUiSelAsId:
      {
        bool          jsFl = false;
        cmDsvRC_t     rc   = kOkDsvRC;

        // if the value associated with this msg is a mtx then set
        // its mtx data area pointer to just after the msg header.
        if( cmDsvIsJson(&m->value) )
        {
          rc = cmDsvDeserializeJson(&m->value,p->jsH);
          jsFl = true;
        }
        else
          rc = cmDsvDeserializeInPlace(&m->value,msgByteCnt-sizeof(cmDspUiHdr_t));

        if( rc != kOkDsvRC )
          cmErrMsg(&p->err,kDeserialFailAiRC,"Deserialize failed.");
        else
          rc = p->parms.dispatchRecd.uiFunc(p->parms.dispatchRecd.cbDataPtr,m);

        if( jsFl )
          cmJsonClearTree(p->jsH);
      }
      break;

    default:
      cmErrMsg(&p->err,kUnknownMsgTypeAiRC,"The message type %i is unknown.",m->uiId);
      break;
  }

  return rc;  
}

cmAiRC_t _cmAdIfReadCfgFile( cmAi_t* p, cmCtx_t* ctx )
{
  cmAiRC_t        rc            = kOkAiRC;
  const cmChar_t* sysJsFn       = NULL;
  cmJsonH_t       jsH           = cmJsonNullHandle;
  cmJsonNode_t*   audDspNodePtr = NULL;
  //const cmChar_t* errLabelPtr   = NULL;

  // form the audio dsp resource file name
  if((sysJsFn = cmFsMakeFn( cmFsPrefsDir(),cmAudDspSys_FILENAME,NULL,NULL)) == NULL )
  {
    rc = cmErrMsg(&p->err,kFileSysFailAiRC,"Unable to form the audio dsp system resource file name.");
    goto errLabel;
  }

  // open the audio dsp resource file
  if(cmJsonInitializeFromFile(&jsH,sysJsFn,ctx) != kOkJsRC )
  {
    rc = cmErrMsg(&p->err,kJsonFailAiRC,"Unable to open the audio dsp resource file: '%s'.",cmStringNullGuard(sysJsFn));
    goto errLabel;
  }

  // locate the aud_dsp container object
  if( cmJsonNodeMember( cmJsonRoot(jsH), "aud_dsp", &audDspNodePtr ) != kOkJsRC )
  {
    rc = cmErrMsg(&p->err,kJsonFailAiRC,"The audio DSP system resource file '%s' does not contain an 'aud_dsp' object.",cmStringNullGuard(sysJsFn));
    goto errLabel;
  }

  /*
  // locate the read the aud_dsp sub-elements
  if( cmJsonMemberValues( audDspNodePtr, &errLabelPtr, 
      "msgQueueByteCnt", kIntTId,   &p->msgQueueByteCnt,
      NULL ) != kOkJsRC )
  {
    rc = cmErrMsg(&p->err,kJsonFailAiRC,"Syntax error while parsing the top level fields in the audio DSP system resource file:%s.",cmStringNullGuard(sysJsFn));
    goto errLabel;
  }
  */

 errLabel:
  if( cmJsonFinalize(&jsH) != kOkJsRC )
    rc = cmErrMsg(&p->err,kJsonFailAiRC,"JSON finalization failed.");

  if( sysJsFn != NULL )
    cmFsFreeFn(sysJsFn);

  return rc;

}


cmAiRC_t _cmAdIfSendIntMsg(cmAiH_t h, unsigned selId, unsigned asSubIdx, unsigned flags, unsigned iv, double dv, const cmChar_t* str )
{  
  cmAi_t*      p = _cmAiHandleToPtr( h );
  cmDspValue_t v;

  if(str != NULL )
    cmDsvSetStrcz(&v,str);
  else
    if(iv == cmInvalidIdx )
      cmDsvSetDouble(&v,dv);
    else
      cmDsvSetUInt(&v,iv);
 
  if( cmMsgSend(&p->err,asSubIdx,kUiSelAsId,selId,flags,cmInvalidId,cmInvalidId,&v,p->parms.audDspFunc,p->parms.audDspFuncDataPtr) != kOkMsgRC )
    return cmErrMsg(&p->err,kSendFailAiRC,"The integer message sel id:%i value:%i transmission failed.",selId,iv);
    
  return kOkAiRC;

}

cmAiRC_t _cmAdIfFree( cmAi_t* p )
{
  cmAiRC_t rc = kOkAiRC;

  if( cmJsonFinalize(&p->jsH) != kOkJsRC )
  {
    rc = cmErrMsg(&p->err,kJsonFailAiRC,"JSON finalization failed.");
    goto errLabel;
  }

  cmMemFree(p);

 errLabel:
  return rc;
}


cmAiRC_t cmAdIfAllocate( cmCtx_t* ctx, cmAiH_t* hp, const cmAdIfParm_t* parms  )
{
  cmAiRC_t rc;
  if((rc = cmAdIfFree(hp)) != kOkAiRC )
    return rc;
  
  cmAi_t* p = cmMemAllocZ(cmAi_t,1);

  cmErrSetup(&p->err,&ctx->rpt,"Audio DSP Interface");

  p->parms = *parms;

  // read the system configuration file
  if((rc = _cmAdIfReadCfgFile(p, ctx )) != kOkAiRC )
    goto errLabel;


  // initialize a JSON tree for use in deserializing JSON messages
  if((rc = cmJsonInitialize( &p->jsH, ctx )) != kOkJsRC )
  {
    rc = cmErrMsg(&p->err,kJsonFailAiRC,"JSON initialization failed.");
    goto errLabel;
  }


  hp->h = p;

 errLabel:
  if( rc != kOkAiRC )
    _cmAdIfFree(p);

  return rc;
}

cmAiRC_t cmAdIfFree( cmAiH_t* hp )
{
  cmAiRC_t rc = kOkAiRC;

  if( hp==NULL || cmAdIfIsValid(*hp)==false )
    return kOkAiRC;

  cmAi_t* p = _cmAiHandleToPtr(*hp);

  if((rc = _cmAdIfFree(p)) != kOkAiRC )
    return rc;

  hp->h = NULL;
  return rc;
}

bool            cmAdIfIsValid( cmAiH_t h )
{ return h.h != NULL; }

cmAiRC_t       cmAdIfRecvAudDspMsg( cmAiH_t h, unsigned msgByteCnt, const void* msg )
{
  cmAi_t* p = _cmAiHandleToPtr(h);
  cmAiRC_t rc = kOkAiRC;

  _cmAiDispatchMsgToClient(p,msgByteCnt,msg);
  return rc;
}

cmAiRC_t        cmAdIfDeviceReport( cmAiH_t h )
{ return _cmAdIfSendIntMsg(h,kDevReportDuiId,cmInvalidIdx,0,cmInvalidIdx,0.0,NULL); }
 
cmAiRC_t        cmAdIfSetAudioSysCfg(   cmAiH_t h, unsigned asCfgIdx )
{ return _cmAdIfSendIntMsg(h,kSetAudioCfgDuiId,cmInvalidIdx,0,asCfgIdx,0.0,NULL); }

cmAiRC_t        cmAdIfSetAudioDevice(   cmAiH_t h, unsigned asSubIdx, bool inputFl, unsigned devIdx )
{ return _cmAdIfSendIntMsg(h,kSetAudioDevDuiId,asSubIdx,inputFl,devIdx,0.0,NULL); }

cmAiRC_t        cmAdIfSetSampleRate(  cmAiH_t h, unsigned asSubIdx, double srate )
{ return _cmAdIfSendIntMsg(h,kSetSampleRateDuiId,asSubIdx,0,cmInvalidIdx,srate,NULL); }

cmAiRC_t        cmAdIfLoadProgram(   cmAiH_t h, unsigned asSubIdx, unsigned pgmIdx )
{ return _cmAdIfSendIntMsg(h,kSetPgmDuiId,asSubIdx,0,pgmIdx,0.0,NULL); }

cmAiRC_t        cmAdIfPrintPgm(   cmAiH_t h,unsigned asSubIdx, const cmChar_t* fn )
{ return _cmAdIfSendIntMsg(h,kPrintPgmDuiId,asSubIdx,0,cmInvalidIdx,0.0,fn); }

  
cmAiRC_t        cmAdIfEnableAudio( cmAiH_t h, bool enableFl )
{ return _cmAdIfSendIntMsg(h,kEnableDuiId,cmInvalidIdx,enableFl,cmInvalidIdx,0.0,NULL); }

cmAiRC_t        cmAdIfEnableStatusNotify( cmAiH_t h, bool enableFl )
{ return _cmAdIfSendIntMsg(h,kSetNotifyEnableDuiId,cmInvalidIdx,enableFl,cmInvalidIdx,0.0,NULL); }

cmAiRC_t        cmAdIfSendMsgToAudioDSP( 
  cmAiH_t             h, 
  unsigned            asSubIdx,
  unsigned            msgTypeId,
  unsigned            selId,
  unsigned            flags,
  unsigned            instId,
  unsigned            instVarId,
  const cmDspValue_t* valPtr )
{
  cmAiRC_t rc = kOkAiRC;
  cmAi_t*  p  = _cmAiHandleToPtr(h);
  
  if( cmMsgSend( &p->err, asSubIdx, msgTypeId,selId,flags,instId,instVarId,valPtr, p->parms.audDspFunc, p->parms.audDspFuncDataPtr ) != kOkMsgRC )
    rc = cmErrMsg(&p->err, kSendFailAiRC, "A UI message intened for the the audio DSP system was not successfully delivered.");

  return rc;

}


cmAiRC_t        cmAdIfDispatchMsgToHost(  cmAiH_t h ) 
{ return _cmAdIfSendIntMsg(h,kClientMsgPollDuiId,cmInvalidIdx,0,cmInvalidIdx,0.0,NULL); }