#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwThread.h"
#include "cwWebSock.h"
#include "cwObject.h"
#include "cwUi.h"
#include "cwUiTest.h"


namespace cw
{
  namespace ui
  {
    typedef struct ui_test_str
    {
      
      //const char*   uiCfgFn;   // Resource file name
      srv::handle_t wsUiSrvH;  // 

      std::atomic<bool> quitFl;

      // Application values
      bool     appCheckFl;
      unsigned appSelOptAppId;
      int      appInteger;
      float    appFloat;
      int      appProgress;
      char*    appString;
      bool     appCheck1Fl;
      bool     appCheck2Fl;
      float    appNumb;
      unsigned appSelId;

      unsigned myPanelUuId;
      unsigned logUuId;

      const object_t* listEleCfg;
      
    } ui_test_t;

    // Application Id's for UI elements
    enum
    {
      // Programatically created UI elements
     kDivId,
     kCheckId,
     kSelectId,
     kOption0Id,
     kOption1Id,
     kOption2Id,
     kOption3Id,
     kStringId,
     kIntegerId,
     kFloatId,
     kProgressId,
     kLogId,

     // Resource Based elements
     kQuitBtnId,
     kPanelDivId,
     kPanelBtn1Id,
     kPanelCheck1Id,
     kPanelBtn2Id,
     kPanelCheck2Id,
     kPanelFloaterId,
     kSelId,
     kOpt1Id,
     kOpt2Id,
     kOpt3Id,
     kListId
    };

    rc_t uiTestCreateUi( ui_test_t* p, unsigned wsSessId=kInvalidId )
    {
      rc_t     rc      = kOkRC;
      unsigned uuid    = kInvalidId;
      unsigned selUuId = kInvalidId;

      
      handle_t uiH = srv::uiHandle(p->wsUiSrvH);

      // Create a UI elements programatically.

      if((rc = createDiv( uiH, p->myPanelUuId, wsSessId, kInvalidId, "myDivId", kDivId, "divClass", "My Panel" )) != kOkRC )
        goto errLabel;

      if((rc = createCheck( uiH, uuid, wsSessId, p->myPanelUuId, "myCheckId", kCheckId, "checkClass", "Check Me", true )) != kOkRC )
        goto errLabel;
      
      if((rc = createSelect( uiH, selUuId, wsSessId, p->myPanelUuId, "mySelId", kSelectId, "selClass", "Select" )) != kOkRC )
        goto errLabel;

      if((rc = createOption( uiH, uuid, wsSessId, selUuId, "myOpt0Id", kOption0Id, "optClass", "Option 0" )) != kOkRC )
        goto errLabel;

      if((rc = createOption( uiH, uuid, wsSessId, selUuId, "myOpt1Id", kOption1Id, "optClass", "Option 1" )) != kOkRC )
        goto errLabel;
      
      if((rc = createOption( uiH, uuid, wsSessId, selUuId, "myOpt2Id", kOption2Id, "optClass", "Option 2" )) != kOkRC )
        goto errLabel;

      if((rc = createStr( uiH, uuid, wsSessId, p->myPanelUuId, "myStringId", kStringId, "stringClass", "String", "a string value" )) != kOkRC )
        goto errLabel;
      
      if((rc = createNumb( uiH, uuid, wsSessId, p->myPanelUuId, "myIntegerId", kIntegerId, "integerClass", "Integer", 0, 100, 1, 0, 10 )) != kOkRC )
        goto errLabel;

      if((rc = createNumb( uiH, uuid, wsSessId, p->myPanelUuId, "myFloatId", kFloatId, "floatClass", "Float", 0.53, 100.97, 1.0, 5, 10.0 )) != kOkRC )
        goto errLabel;
      
      if((rc = createProg(  uiH, uuid, wsSessId, p->myPanelUuId, "myProgressId", kProgressId, "progressClass", "Progress", 0, 10, 5 )) != kOkRC )
        goto errLabel;

      
      if((rc = createLog( uiH, p->logUuId, wsSessId, p->myPanelUuId, "myLogId", kLogId, "logClass", "My Log (click toggles auto-scroll)" )) != kOkRC )
        goto errLabel;

    errLabel:
      return rc;
    }

    void _print_state( ui_test_t* p )
    {
      printf("check:%i sel:%i int:%i flt:%f prog:%i str:%s chk1:%i chk2:%i numb:%f sel:%i\n",
        p->appCheckFl,
        p->appSelOptAppId,
        p->appInteger,
        p->appFloat,
        p->appProgress,
        p->appString,
        p->appCheck1Fl,
        p->appCheck2Fl,
        p->appNumb,
        p->appSelId);
    }


    rc_t _insert_list_ele( ui_test_t* p, unsigned wsSessId )
    {
      rc_t     rc   = kOkRC;

      if( p->listEleCfg != nullptr )
      {
        handle_t uiH       = srv::uiHandle(p->wsUiSrvH);
        unsigned listUuId  = findElementUuId( uiH, kListId );

        printf("list uuid:%i\n",listUuId);

        rc = createFromObject( uiH, p->listEleCfg, wsSessId, listUuId );        
      }
      
      return rc;
    }


    rc_t _insert_log_line( ui_test_t* p, unsigned wsSessId, const char* text )
    {
      return ui::setLogLine( srv::uiHandle(p->wsUiSrvH), wsSessId, p->logUuId, text );
    }

    rc_t _handleUiValueMsg( ui_test_t* p, unsigned wsSessId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* v )
    {
      rc_t rc = kOkRC;
      
      switch( appId )
      {
        case kQuitBtnId:
          p->quitFl.store(true);
          break;

        case kCheckId:
          printf("Check:%i\n", v->u.b);
          p->appCheckFl = v->u.b;
          _insert_log_line( p, wsSessId, "check!\n" );
          break;

        case kSelectId:
          printf("Selected: option appId:%i\n", v->u.i);
          p->appSelOptAppId = v->u.i;
          break;

        case kStringId:
          printf("String: %s\n",v->u.s);
          mem::release(p->appString);
          if( v->u.s != nullptr )
            p->appString = mem::duplStr(v->u.s);
          break;
          
        case kIntegerId:
          {
            printf("Integer: %i\n",v->u.i);
            p->appInteger = v->u.i;
          
            handle_t uiH = srv::uiHandle(p->wsUiSrvH);
            unsigned progUuId = findElementUuId(   uiH, p->myPanelUuId, kProgressId );
            sendValueInt(   uiH, wsSessId, progUuId, v->u.i );
          }
          break;
          
        case kFloatId:
          printf("Float: %f\n",v->u.f);
          p->appFloat = v->u.f;


        case kPanelBtn1Id:
          printf("Button 1\n");
          _print_state(p);
          break;
          
        case kPanelCheck1Id:
          printf("check 1: %i\n",v->u.b);
          p->appCheck1Fl = v->u.b;
          ui::report( srv::uiHandle(p->wsUiSrvH) );

          break;
          
        case kPanelBtn2Id:
          printf("Button 2\n");
          _print_state(p);
          break;
          
        case kPanelCheck2Id:
          printf("check 1: %i\n",v->u.b);
          p->appCheck1Fl = v->u.b;
          _insert_list_ele( p, wsSessId );
          break;
          
        case kPanelFloaterId:
          printf("numb: %f\n",v->u.f);
          p->appNumb = v->u.f;
          break;

        case kSelId:
          printf("sel: %i\n",v->u.i);
          p->appSelId = v->u.i;
          break;
      }

      return rc;
    }

    // 
    rc_t _handleUiEchoMsg( ui_test_t* p, unsigned wsSessId, unsigned parentAppId, unsigned  uuId, unsigned appId )
    {
      rc_t rc = kOkRC;
      
      switch( appId )
      {
        case kCheckId:
          sendValueBool( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appCheckFl );
          break;

        case kSelectId:
          sendValueInt( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appSelOptAppId );
          break;

        case kStringId:
          sendValueString( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appString );
          break;
          
        case kIntegerId:
          sendValueInt( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appInteger );
          break;

        case kFloatId:
          sendValueFloat( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appFloat );
          break;
          
        case kProgressId:
          sendValueInt( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appProgress );
          break;

        case kPanelCheck1Id:
          sendValueBool( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appCheck1Fl);
          break;
          
        case kPanelCheck2Id:
          sendValueBool( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appCheck2Fl);
          break;
          
        case kPanelFloaterId:
          sendValueFloat( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appNumb );
          break;

        case kSelId:
          sendValueInt( uiHandle( p->wsUiSrvH ), wsSessId, uuId, p->appSelId );
          break;
      }
      return rc;
    }
    


    // This function is called by the websocket with messages coming from a remote UI.
    rc_t _uiTestCallback( void* cbArg, unsigned wsSessId, opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* v )
    {
      ui_test_t* p = (ui_test_t*)cbArg;
      
      switch( opId )
      {
        case kConnectOpId:
          cwLogInfo("UI Test Connect: wsSessId:%i.",wsSessId);
          break;
          
        case kDisconnectOpId:
          cwLogInfo("UI Test Disconnect: wsSessId:%i.",wsSessId);          
          break;
          
        case kInitOpId:
          //_uiTestCreateUi(p,wsSessId);
          break;

        case kValueOpId:
          _handleUiValueMsg( p, wsSessId, parentAppId, uuId, appId, v );
          break;

        case kEchoOpId:
          _handleUiEchoMsg( p, wsSessId, parentAppId, uuId, appId );
          break;
          
        case kInvalidOpId:
          // fall through
        default:
          break;
        
      }
      return kOkRC; 
    }    
  }
}

cw::rc_t cw::ui::test( const object_t* cfg )
{
  rc_t           rc   = kOkRC;
  ui::ws::args_t args = {};
  
  // Application Id's for the resource based UI elements.
  appIdMap_t mapA[] =
    {
     { kRootAppId,  kQuitBtnId,      "myQuitBtnId" },
     { kRootAppId,  kPanelDivId,     "panelDivId" },
     { kPanelDivId, kPanelBtn1Id,    "myBtn1Id" },
     { kPanelDivId, kPanelCheck1Id,  "myCheck1Id" },
     { kPanelDivId, kPanelBtn2Id,    "myBtn2Id" },
     { kPanelDivId, kPanelCheck2Id,  "myCheck2Id" },
     { kPanelDivId, kPanelFloaterId, "myFloater" },
     { kPanelDivId, kSelId,          "mySel" },
     { kSelId,      kOpt1Id,         "myOpt1" },
     { kSelId,      kOpt2Id,         "myOpt2" },
     { kSelId,      kOpt3Id,         "myOpt3" },
     { kPanelDivId, kListId,         "myListId" }

    };

  unsigned mapN = sizeof(mapA)/sizeof(mapA[0]);
  ui_test_t*     app = mem::allocZ<ui_test_t>();

  if( cfg == nullptr )
  {
    cwLogError(kInvalidArgRC,"ui::test() was not passed a valid cfg. object.");
    goto errLabel;
  }
  
  if((rc = parseArgs(*cfg, args )) != kOkRC )
  {
    cwLogError(rc,"UI parse args failed in ui::test()");
    goto errLabel;
  }
  
  app->quitFl.store(false);

  // Initial values for the test applications
  app->appCheckFl     = true;
  app->appSelOptAppId = kOption1Id;
  app->appInteger     = 5;
  app->appFloat       = 2.56;
  app->appProgress    = 7;
  app->appString      = mem::duplStr("fooz");
  app->appCheck1Fl    = false;
  app->appCheck2Fl    = true;
  app->appNumb        = 1.23;
  app->appSelId       = kOpt3Id;

  if((rc = cfg->getv( "listEle", app->listEleCfg )) != kOkRC )
    rc = cwLogError( rc, "The list element cfg. was not found.");
  

  //app->uiCfgFn = "/home/kevin/src/cwtest/src/libcw/html/uiTest/ui.cfg";

  // create the UI server
  if((rc = srv::create(app->wsUiSrvH, args, app, _uiTestCallback, mapA, mapN, nullptr )) != kOkRC )
    return rc;

  if((rc = uiTestCreateUi( app )) != kOkRC )
    goto errLabel;

  //ui::report( srv::uiHandle(app->wsUiSrvH) );
  
  // start the UI server
  if((rc = srv::start(app->wsUiSrvH)) != kOkRC )
    goto errLabel;

  
  // readline loop
  while( !app->quitFl.load()  )
  {
    sleepMs(50);
  }

 errLabel:
  ui::ws::releaseArgs(args);
  
  rc_t rc1 = kOkRC;
  if( app->wsUiSrvH.isValid() )
    rc1 = srv::destroy(app->wsUiSrvH);

  mem::release(app->appString);
  mem::release(app);
  
  return rcSelect(rc,rc1);   
}