#ifndef cmUi_h
#define cmUi_h

#ifdef __cplusplus
extern "C" {
#endif
  /*
    cmUI implements a platform independent UI control manager 
    for multiple simultaneous applications. In this context
    an 'application' can be seen as a plug-in style program.

    The goal of the manager is to support the easy construction 
    of panels of graphic user interface controls.

    
     
    Operation:

    1) A cmUi manager is allocated by the master program. See cmUiAlloc(). 

    2) The master program then assigns a 'driver' to implmenent
    the commands issued by cmUi.  All commands can be 
    described using the cmUiDriverArg_t record.

    3) The master program then registers client applications with
    the cmUi manager by calling cmUiCreateApp().  

    4) Prior to calling any of the client application functions
    the master or client app. must call cmUiSetAppId(). This
    function sets the current application id and thereby slightly
    simplifies the client app interface by not requiring the
    app id be passed with every client app. function call.

    5) Once the current app. id is set with cmUiSetAppId() 
    the client app can make multiple calls to the cmUi manager
    to create or query controls.

    6) Calls to create controls result in callbacks to the 
    driver function which manages the actual windowing and
    initial event handling.

    7) Events generated by the driver based graphic controls 
    are routed back to the cmUi manager through calls to
    cmUiOnDriverEvent().

    8) The cmUiOnDriverEvent() internally caches the 
    state/value of the control based on the event information
    and calls the 'cbFunc' function registered with the
    cmUi manager by cmUiAlloc(). This call is intended to
    notify the client applications of changes in the state/value
    of a user interface control.
    
    Notes:
    1) Application id's must be unique among all applications.
    Control id's must be unique among all controls assigned
    to the same application. Both application and control id's
    are used internally as array indexes they should therefore
    be the low, positive, and densely packed integers.

    2) All communication between the cmUi and the driver,
    and all callbacks from the cmUi to the master app. 
    use the cmUiDriverArg_t structure 

    3) The panel tabs act like radio buttons.  When a tab is
    selected the clicked tab generates one event for each
    tab (cId=kPanelUiCId usrId=panelId) w/ ival=1 for the 
    selected tab and ival=0 for all other tabs.

    TODO:
    0) [DONE] Remove the 'cbArg' from the cmUiDriverArg_t record and 
    pass it as a separate paramenter in the cmUiDriverFunc_t calls.

    1) The controls should be based on a multilevel tree model
    not the simple two layer panel->controls framework used now.
    
    2) Given that access to the control values can be non-blocked
    and fast many controls won't need to report changes back to
    the client - the client can simply read the control value
    as necessary directly from the cmUi manager.  To decrease
    event processing overhead controls should therefore be 
    flagged as 'callback/no-callback'.  Some controls like
    buttons should default to turning the flag on, while other
    controls, like numbers, should default to turning it off.

    3) Implement support for the creation of arrays of controls.
    This can be implemented with a scheme similar to 'next rect'.
    cmUiSetupArray(uiH,panelId,baseCtlId,cnt).  
    
    4) Come up with a threading model. For example maybe 
    control creation should use a blocking scheme since it is
    generally too time consuming to do during real-time operation
    anyway.  If the control flow generated from driver event
    callbacks then allows value, but not structural changes,
    then non blocking will be necessary.

    5) The cmUiDriverArg_t structure is used for both
    commands to the driver and callbacks from the driver.
    Many of the fields are not use for event callbacks.
    Which fields are used under which circumstances should
    be documented. This would allow decreasing the size
    of the record during serialization.

    6) [DONE] Write a serialization/deserialization routines for cmUiDriverArg_t.
    unsigned cmUiDriverArgSerialBufByteCount(const cmUiDriverArg_t* a);
    cmUiRC_t cmUiDriverArgSerialize( const cmUiDriverArg_t* a, char* buf, bufByteCnt );
    cmUiRC_t cmUiDriverArgDeserialize( cmUiDriverArg_t* a, const char* buf, bufByteCnt );

    7) There is no duplex model for validating and then displaying the
    value of a control.

   */

  typedef cmHandle_t cmUiH_t;


  extern cmUiH_t cmUiNullHandle;

  //=============================================================================================
  //
  // Master program API
  //

  // Allocate the cmUi manager. If the driver function is not
  // yet available then set drvrFunc to NULL and use 
  // cmUiSetDriver() to set the driver function at a later
  // time.  cmUiAlloc() stores but does not call the driver
  // and so it is not needed when the cmUI manager is 
  // constructed - however it must be set prior to making
  // any other calls to the manager.
  cmUiRC_t cmUiAlloc( 
    cmCtx_t*         ctx, 
    cmUiH_t*         hp, 
    cmUiDriverFunc_t drvrFunc, // UI driver function 
    void*            drvrArg,
    cmUiDriverFunc_t cbFunc,   // client event callback function
    void*            cbArg );

  // Release all apps but assume that the driver has already
  // been released.
  cmUiRC_t cmUiFree(  cmUiH_t* hp );

  // Validate the cmUiH_t handle.
  bool     cmUiIsValid( cmUiH_t h );

  // Set the driver function. This function allows the driver
  // set in cmUiAlloc() to be replaced or set following the
  // call to cmUiAlloc().
  //
  // By setting the driver function to
  // NULL the application can prevent callback to the driver.
  // This is useful if the driver has been release prior
  // to the UI manager begining destroyed.
  void cmUiSetDriver( cmUiH_t h, cmUiDriverFunc_t drvrFunc, void* drvrArg );

  // Register a client application with the cmUi manager.
  // 'appId' must not have been used by any other previously registered.
  // application.
  // Automatically sets and restores the ambient appId.
  // In a plug-in context this function is generally called 
  // just prior a instantiating a plug-in object.
  cmUiRC_t cmUiCreateApp(  cmUiH_t uiH, unsigned appId, unsigned asSubIdx );

  // Return true if 'appId' is active.
  bool     cmUiAppIsActive( cmUiH_t uiH, unsigned appId );


  // Notify the cmUi manager that the resources associated
  // with a client application can be released.
  // Automatically sets and restores the ambient appId.
  cmUiRC_t cmUiDestroyApp( cmUiH_t uiH, unsigned appId );

  // Release all apps.
  // Automatically sets and restores the ambient appId.
  cmUiRC_t cmUiDestroyAllApps( cmUiH_t h );


  // The master program sets the ambient 'appId' prior to passing control
  // to a client which will make User API calls.  By making the
  // 'appId' an ambient parameter the User API calls are slightly
  // simplified because they do not have to include it in each of the
  // function calls.
  //cmUiRC_t cmUiSetAppId( cmUiH_t uiH, unsigned appId );
  //unsigned cmUiAppId(    cmUiH_t uiH );

  unsigned cmUiAppIdToAsSubIndex( cmUiH_t uiH, unsigned appId );

  // Return the count of app's.
  unsigned cmUiAppCount( cmUiH_t uiH );


  // Callbacks from the driver arrive via this function.
  cmUiRC_t cmUiOnDriverEvent( cmUiH_t uiH, const cmUiDriverArg_t* p );


  //=============================================================================================
  // 
  // Client Application API
  //

  // See above note on panel tabs acting like radio buttons.
  cmUiRC_t cmUiCreatePanel(   cmUiH_t uiH, unsigned appId, unsigned newPanelId, const cmChar_t* label, unsigned flags );

  cmUiRC_t cmUiCreateBtn(         cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags );
  cmUiRC_t cmUiCreateCheck(       cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, bool dflt );
  cmUiRC_t cmUiCreateLabel(       cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags );
  cmUiRC_t cmUiCreateString(      cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, const cmChar_t* text );  
  cmUiRC_t cmUiCreateConsole(     cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, const cmChar_t* text );
  cmUiRC_t cmUiCreateNumber(      cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, double min, double max, double incr, double dflt );
  cmUiRC_t cmUiCreateHSlider(     cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, double min, double max, double incr, double dflt );
  cmUiRC_t cmUiCreateVSlider(     cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, double min, double max, double incr, double dflt );
  cmUiRC_t cmUiCreateProgress(    cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, int    min, int    max, int    dflt );
  cmUiRC_t cmUiCreateHMeter(      cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, int    min, int    max, int    dflt ); 
  cmUiRC_t cmUiCreateVMeter(      cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, int    min, int    max, int    dflt ); 
  cmUiRC_t cmUiCreateFileBtn(     cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, const cmChar_t* dfltDir, const cmChar_t* patStr );
  cmUiRC_t cmUiCreateDirBtn(      cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, const cmChar_t* dfltDir );
  cmUiRC_t cmUiCreateMenuBtn(     cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags );
  cmUiRC_t cmUiCreateMenuBtnV(    cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* lavel, unsigned flags, const cmChar_t* label0, unsigned id0, va_list vl );
  cmUiRC_t cmUiCreateMenuBtnA(    cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* lavel, unsigned flags, const cmChar_t* label0, unsigned id0, ... );
  cmUiRC_t cmUiCreateMenuBtnJson( cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* lavel, unsigned flags, const cmJsonNode_t* root, const cmChar_t* memberLabel );
  cmUiRC_t cmUiCreateList(        cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, unsigned visibleRowCnt );
  cmUiRC_t cmUiCreateListV(       cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, unsigned visibleRowCnt, const cmChar_t* label0, unsigned id0, va_list vl );
  cmUiRC_t cmUiCreateListA(       cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, unsigned visibleRowCnt, const cmChar_t* label0, unsigned id0, ... );
  cmUiRC_t cmUiCreateListJson(    cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* label, unsigned flags, unsigned visibleRowCnt, const cmJsonNode_t* root, const cmChar_t* memberLabel );

  // If 'id' identifies a 'list' control use tabs as column separators.
  cmUiRC_t cmUiAppendListEle(    cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, const cmChar_t* text, unsigned eleId );

  // Remove all the elements of a list control.
  cmUiRC_t cmUiClearList(        cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id );

  // Enable/Disable a control
  cmUiRC_t cmUiEnableCtl( cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id, bool enableFl );
  
  // Enable/disable all controls on a panel except those included in the var args list.
  // Terminate the var args list with cmInvalidId.
  cmUiRC_t cmUiEnableAllExceptV( cmUiH_t uiH, unsigned appId, unsigned panelId, bool enableFl, va_list vl );
  cmUiRC_t cmUiEnableAllExcept( cmUiH_t uiH, unsigned appId, unsigned panelId, bool enableFl, ... );

  // If 'id' identifies a panel then all control belonging to the panel
  // will also be destroyed.
  cmUiRC_t cmUiDestroyCtl(    cmUiH_t uiH, unsigned appId, unsigned id );

  // Returns true if the control exists.
  // For panels set id=panelId.
  //bool     cmUiCtlExists(     cmUiH_t uiH, unsigned appId, unsigned panelId, unsigned id );

  // Destroy all the controls in a panel.
  cmUiRC_t cmUiClearPanel( cmUiH_t uiH, unsigned appId, unsigned panelId );

  cmUiRC_t cmUiSelectPanel( cmUiH_t uiH, const cmChar_t* label );

  //------------------------------------------------------------------------------------------
  // Location: 

  // 1) If a 'next rect' is set the control will be placed according to it.
  // 2) If cmUiSetBaseCol() was set then the control will be placed at the
  //    base col and base row.
  // 3) If cmUiPlaceRight() or cmUIPlaceBelow() have been called since the
  //    previous control was created then the new control will be placed
  //    accordingly.
  // 4) The control will be placed according to the 'fill rows' flag. 
  //    If not set (default) the new control  will be placed in the 
  //    base column below the previous control.  
  //    If 'fill rows' is set the new control will be placed on the 
  //    same row to the right of the previous control.
  //    If there is no previous control then it will be placed
  //    in the base column and base row.

  // Get/Set the fill directions. If the 'fill columns' flag is enabled
  // then the next control is placed below the previous control otherwise
  // the next control is placed to the right of the next control.
  bool    cmUiFillRows( cmUiH_t uiH, unsigned appId, unsigned panelId );
  bool    cmUiSetFillRows( cmUiH_t uiH, unsigned appId, unsigned panelId, bool enableFl );

  
  // Place the next control to the right/below of the previous ctl.
  // These flags override the current fill row/col setting.
  // Note that 'place right' and 'place below' are mutually
  // exclusive. Enabling one disables the other.
  void     cmUiPlaceRight( cmUiH_t uiH, unsigned appId, unsigned panelId );          
  void     cmUiPlaceBelow( cmUiH_t uiH, unsigned appId, unsigned panelId );

  // Place the next control at the base column below the previous ctl.
  void     cmUiNewLine(    cmUiH_t uiH, unsigned appId, unsigned panelId );

  // Set/Get current base col and return previous value. Place the next
  // control on the base row.
  int      cmUiBaseCol(       cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiSetBaseCol(    cmUiH_t uiH, unsigned appId, unsigned panelId, int x );   


  // Set/Get current base row and return previous value.
  int      cmUiBaseRow(       cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiSetBaseRow(    cmUiH_t uiH, unsigned appId, unsigned panelId, int y );   

  // Size:
  // 1) If a 'next rect' is set the control will be placed according
  //    to it.
  // 2) If a 'next w/h' is set the control will be placed according
  //    to it. The 'next w/h' setting is only active for the 
  //    next control created.
  // 3) The w/h of the new control will be set to the default w/h.
  //

  // Get/Set the default control width and height.
  // Set returns previous value.
  int      cmUiW(      cmUiH_t uiH, unsigned appId, unsigned panelId );         
  int      cmUiH(      cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiSetW(   cmUiH_t uiH, unsigned appId, unsigned panelId, int w );
  int      cmUiSetH(   cmUiH_t uiH, unsigned appId, unsigned panelId, int h );
  void     cmUiSetWH(  cmUiH_t uiH, unsigned appId, unsigned panelId, int w, int h );

  // Get/Set the control width and height for only the next control.
  // Set returns previous value.
  int      cmUiNextW(     cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiNextH(     cmUiH_t uiH, unsigned appId, unsigned panelId );
  void     cmUiSetNextW(  cmUiH_t uiH, unsigned appId, unsigned panelId, int w );
  void     cmUiSetNextH(  cmUiH_t uiH, unsigned appId, unsigned panelId, int h );
  void     cmUiSetNextWH( cmUiH_t uiH, unsigned appId, unsigned panelId, int w, int h );

  // Get/Set the default inter-control borders
  // Set returns previous value.
  int      cmUiHBorder( cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiVBorder( cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiSetHBorder( cmUiH_t uiH, unsigned appId, unsigned panelId, int w );
  int      cmUiSetVBorder( cmUiH_t uiH, unsigned appId, unsigned panelId, int h );

  // Get/Set the 'next' inter-control borders
  // Set returns previous value.
  int      cmUiNextHBorder( cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiNextVBorder( cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiSetNextHBorder( cmUiH_t uiH, unsigned appId, unsigned panelId, int w );
  int      cmUiSetNextVBorder( cmUiH_t uiH, unsigned appId, unsigned panelId, int h );

  // Place the next control at the following coordinates. The 
  // specified coordinates are only active during the next
  // cmUiCreateXXX() call. Setting the 'next rect' overrides all
  // other layout directives.
  cmUiRC_t cmUiNextRect( cmUiH_t uiH, unsigned appId, unsigned panelId, int x, int y, int w, int h );
  
  // Get the location/size of the previously created control.
  // All ref. args are optional.
  cmUiRC_t cmUiPrevRect( cmUiH_t uiH, unsigned appId, unsigned panelId, int* xRef, int* yRef, int* wRef, int* hRef );
  int      cmUiPrevL(    cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiPrevT(    cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiPrevR(    cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiPrevB(    cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiPrevW(    cmUiH_t uiH, unsigned appId, unsigned panelId );
  int      cmUiPrevH(    cmUiH_t uiH, unsigned appId, unsigned panelId );

  //------------------------------------------------------------------------------------------
  //
  // Get/set the value of UI control.  
  //

  // TODO:
  // +  Need functions for setting auxilliary values like
  //    min,max,etc..

  // Set the value associated with a control.   
  // Set the local value of the specified control and then 
  // send the value to the UI driver so that the UI reflects this value.
  cmUiRC_t  cmUiSetInt(    cmUiH_t uiH,  unsigned appId, unsigned id, int v );
  cmUiRC_t  cmUiSetUInt(   cmUiH_t uiH,  unsigned appId, unsigned id, unsigned v );
  cmUiRC_t  cmUiSetDouble( cmUiH_t uiH,  unsigned appId, unsigned id, double v );
  cmUiRC_t  cmUiSetString( cmUiH_t uiH,  unsigned appId, unsigned id, const cmChar_t* v );
  cmUiRC_t  cmUiSetVPrintf(cmUiH_t uiH,  unsigned appId, unsigned id, const cmChar_t* fmt, va_list vl );
  cmUiRC_t  cmUiSetPrintf( cmUiH_t uiH,  unsigned appId, unsigned id, const cmChar_t* fmt, ... );

  // Get the value associated with a control. These functions return
  // the control value cached in the local control, they do not need 
  // to call the driver and are therefore very fast.
  int             cmUiInt(    cmUiH_t uiH, unsigned appId,  unsigned id );
  unsigned        cmUiUInt(   cmUiH_t uiH, unsigned appId,  unsigned id );
  double          cmUiDouble( cmUiH_t uiH, unsigned appId,  unsigned id );
  const cmChar_t* cmUiString( cmUiH_t uiH, unsigned appId,  unsigned id );

  unsigned        cmUiListEleCount( cmUiH_t uiH, unsigned appId, unsigned id );
  unsigned        cmUiListEleId(    cmUiH_t uiH, unsigned appId, unsigned id, unsigned index );
  const cmChar_t* cmUiListEleLabel( cmUiH_t uiH, unsigned appId, unsigned id, unsigned index );
  unsigned        cmUiListEleLabelToIndex( cmUiH_t uiH, unsigned appId, unsigned id, const cmChar_t* label );

  // Query/set the current error state.
  cmUiRC_t   cmUiLastRC( cmUiH_t uiH );
  cmUiRC_t   cmUiSetRC( cmUiH_t uiH, cmUiRC_t rc );


#ifdef __cplusplus
}
#endif

#endif
