#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Item.H>
#include <Fl/Fl_Menu_Bar.H>
#include <Fl/Fl_Button.H>
#include <Fl/Fl_Text_Buffer.H>
#include <Fl/Fl_Text_Display.H>
#include <Fl/Fl_Tabs.H>
#include <Fl/Fl_File_Chooser.H>

#include "Fl_Splitter.h"

#include "cmGlobal.h"
#include "cmFloatTypes.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmLinkedHeap.h"
#include "cmJson.h"
#include "cmThread.h"
#include "cmText.h"
#include "cmFileSys.h"
#include "cmPrefs.h"
#include "cmSymTbl.h"
#include "cmDspValue.h"
#include "cmMsgProtocol.h"
#include "cmAudDspIF.h"
#include "cmAudDspLocal.h"
#include "cmAudioFile.h"
#include "cmAudioFileMgr.h"
#include "cmTime.h"
#include "cmMidi.h"
#include "cmMidiFile.h"
#include "cmTimeLine.h"
#include "cmScore.h"
#include "cmdIf.h"
#include "tlCtl.h"
#include "kcApp.h"

enum
{
  kAppWndW = 1600,
  kAppWndH = 750,
  kAppWndX = 10,
  kAppWndY = 10
  
};
  
kcApp* kcAppPtr = NULL;


void print( void*, const cmChar_t* text)
{ puts(text); }

cmRC_t  print_queue_cb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr )
{
  print(NULL,(const char*)msgDataPtr);
  return cmOkRC;
}

cmRC_t handleSsInitMsg( void* cbDataPtr, const cmAudioSysSsInitMsg_t* r, const char* iDevLabel, const char* oDevLabel )
{ kcAppPtr->_handleSsInitMsg(r,iDevLabel,oDevLabel); return cmOkRC; }

cmRC_t handleStatusMsg( void* cbDataPtr, const cmAudioSysStatus_t* r, const double* iMeterArray, const double* oMeterArray )
{  kcAppPtr->_handleStatusMsg(r,iMeterArray,oMeterArray);  return cmOkRC;}

cmRC_t handleUiMsg(     void* cbDataPtr, const cmDspUiHdr_t* r )
{ kcAppPtr->_handleUiMsg(r);  return cmOkRC;}

cmPrRC_t kcInitPrefs( cmCtx_t* ctx, cmPrH_t* prH )
{
  cmPrRC_t rc = kOkPrRC;

  if((rc = cmPrefsInit(ctx, prH, NULL, NULL, NULL, NULL, NULL )) != kOkPrRC )
    return rc;

  unsigned flags = 0;
  cmPrefsCreateInt(*prH, 1, "appWndW", flags, kAppWndW );
  cmPrefsCreateInt(*prH, 2, "appWndH", flags, kAppWndH );
  cmPrefsCreateInt(*prH, 3, "appWndX", flags, kAppWndX );
  cmPrefsCreateInt(*prH, 4, "appWndY", flags, kAppWndY );

  return cmPrefsRC(*prH);
}

int main( int argc, char* argv[] )
{
  cmCtx_t     ctx;
  cmTsMp1cH_t printqH         = cmTsMp1cNullHandle;
  const char* appPrefDir      = "kc";
  const char* appTitle        = "KC Console";
  bool        memDebugFl      = false; //cmDEBUG_FL;
  unsigned    memPadByteCnt   = memDebugFl ? 8 : 0;
  unsigned    memAlignByteCnt = 16;
  unsigned    memFlags        = memDebugFl ? (kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl) : 0;
  cmAdlH_t    adlH            = cmAdlNullHandle;
  cmAiH_t     aiH             = cmAiNullHandle;
  cmPrH_t     prH             = cmPrNullHandle;
  cmAdIfDispatch_t r;

  cmCtxSetup(&ctx,appTitle,print,print,NULL,memPadByteCnt,memAlignByteCnt,memFlags);

  // initialize the memory mgr
  if(cmMdInitialize( memPadByteCnt, memAlignByteCnt, memFlags, &ctx.rpt ) != kOkMmRC )
  {
    cmErrMsg(&ctx.err,kMemFailKmRC,"Heap initialization failed.");
    goto errLabel;
  }

  // initialize the file system
  if( cmFsInitialize( &ctx, appPrefDir ) != kOkFsRC )
    cmErrMsg(&ctx.err,kFileSysFailKmRC,"File system initialization failed.");

  // initialize the text system
  if( cmTsInitialize(&ctx) != kOkTxRC )
    cmErrMsg(&ctx.err,kTextSysFailKmRC,"Text system initialization failed.");

  // iniitalize the preferences
  if( kcInitPrefs(&ctx,&prH) != kOkPrRC )
    cmErrMsg(&ctx.err,kPrefsFailKmRC,"Preference initializatkion failed.");

  // create the print queue
  if( cmTsMp1cCreate( &printqH, 8192, print_queue_cb, NULL, NULL ) != kOkThRC )
    cmErrMsg(&ctx.err,kQueueFailKmRC,"Print queue creation failed.");

  r.cbDataPtr     = NULL;
  r.ssInitFunc    = handleSsInitMsg;
  r.statusFunc    = handleStatusMsg;
  r.uiFunc        = handleUiMsg;

  // initialize the audio engine
  if(  cmAudDspLocalAllocate(&ctx,&adlH,&r) != kOkAdlRC )
      cmErrMsg(&ctx.err,kEngFailKmRC,"The audio DSP local server allocation failed.");
  else
    if(cmAdIfIsValid(aiH = cmAudDspLocalIF_Handle(adlH)) == false )
      cmErrMsg(&ctx.err,kEngFailKmRC,"The audio DSP interface handle is not valid.");

  if( cmErrLastRC(&ctx.err) == kOkKmRC )
  {
    int appWndW = cmPrefsIntDef(prH,"appWndW", kAppWndW);
    int appWndH = cmPrefsIntDef(prH,"appWndH", kAppWndH);
    int appWndX = cmPrefsIntDef(prH,"appWndX", kAppWndX);
    int appWndY = cmPrefsIntDef(prH,"appWndY", kAppWndY);
    
    kcAppPtr = new kcApp(&ctx, prH, printqH, appWndW, appWndH, appTitle, aiH, argc, argv);
    
    kcAppPtr->resize(appWndX,appWndY,appWndW,appWndH);

    cmAudDspLocalSendSetup(adlH);

    Fl::run();

    delete kcAppPtr;
    kcAppPtr=NULL;

    // reset the pgm context and print queue  console output to stdout
    cmRptSetup(&ctx.rpt,print,print,NULL);
    cmTsMp1cSetCbFunc(printqH, print_queue_cb, NULL );

  }

  // empty any pending text to stdout
  while( cmTsMp1cMsgWaiting(printqH) )
    if( cmTsMp1cDequeueMsg(printqH, NULL, 0) != kOkThRC )
      cmErrMsg(&ctx.err,kQueueFailKmRC,"Print dequeue failed.");


  // release the engine
  if( cmAudDspLocalFree(&adlH) != kOkAdlRC )
    cmErrMsg(&ctx.err,kEngFailKmRC,"Audio DSP release failed.");


  // destroy the print queue
  if( cmTsMp1cDestroy(&printqH) != kOkThRC )
    cmErrMsg(&ctx.err,kQueueFailKmRC,"Print queue destroy failed.");

  // write the preferences file
  if( cmPrefsIsDirty(prH) )
    if( cmPrefsWrite(prH,cmPrefsFileName(prH)) != kOkPrRC )
      cmErrMsg(&ctx.err,kPrefsFailKmRC,"Preferences save failed.");

  // finalize the preferences file
  if( cmPrefsFinalize(&prH) != kOkPrRC )
    cmErrMsg(&ctx.err,kPrefsFailKmRC,"Preference finalize failed.");

  // finalize the text system
  if( cmTsFinalize() != kOkTxRC )
    cmErrMsg(&ctx.err,kTextSysFailKmRC,"Text system finalization failed.");

  // finalize the file system
  if( cmFsFinalize() != kOkFsRC )
    cmErrMsg(&ctx.err,kFileSysFailKmRC,"File system finalize failed.");

  // report memory mgr errors
  if( cmMdIsValid() )
    cmMdReport( kIgnoreNormalMmFl );

  // finalize the memory manager
  if( cmMdFinalize() != kOkMmRC )
    cmErrMsg(&ctx.err,kMemFailKmRC,"Heap finalization failed.");

 errLabel:

  return 0;
}
