#ifndef _GUISLICE_EX_XKEYPAD_H_
#define _GUISLICE_EX_XKEYPAD_H_

#include "GUIslice.h"


// =======================================================================
// GUIslice library extension: XKeyPad control
// - Paul Conti, Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XKeyPad.h

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus



#if (GSLC_FEATURE_COMPOUND)
// ============================================================================
// Extended Element: KeyPad (Number Selector)
// - Demonstration of a compound element consisting of
//   a counter text field along with an increment and
//   decrement button.
// ============================================================================

// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_KEYPAD GSLC_TYPE_BASE_EXTEND + 20

#define XKEYPAD_VAL_LEN  12  // Maximum buffer length for input string

// Define the status for GSLC_CB_INPUT callback
#define XKEYPAD_CB_STATE_DONE 1
#define XKEYPAD_CB_STATE_CANCEL 2
#define XKEYPAD_CB_STATE_UPDATE 3


// Define global list of button ID types
// - A given keypad may not use all of these
enum {
  // --------------------------------------------
  // --- Special Buttons
  KEYPAD_ID_BACKSPACE,
  KEYPAD_ID_PERIOD,
  KEYPAD_ID_SPACE,
  KEYPAD_ID_DECIMAL,
  KEYPAD_ID_MINUS,
  KEYPAD_ID_ESC,
  KEYPAD_ID_ENTER,
  // --- Basic Buttons
  KEYPAD_ID_BASIC_START=100,
  // --------------------------------------------
  // --- Extra elements
  KEYPAD_ID_TXT=200,        // Value string text area
};

/// Function for KeyPad creation
typedef int16_t (*XKEYPAD_LOOKUP)(gslc_tsGui* pGui, int16_t nKeyId);


// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
//   structure via the pXData pointer

  /// Configuration for the KeyPad
  typedef struct {
    bool                bFloatEn;         ///< Enable floating point (ie. decimal point)
    bool                bSignEn;          ///< Enable negative numbers
	  bool                bRoundEn;         ///< Enable rounded corners
    int8_t              nButtonSzW;       ///< Button width (in pixels)
    int8_t              nButtonSzH;       ///< Button height (in pixels)
    char**              pacKeys;          ///< Array of character strings for KeyPad labels

    // From constructor
    int16_t             nFontId;          ///< Configured font for KeyPad labels
    int16_t             nOffsetX;         ///< Configured offset (X direction) for buttons from parent container
    int16_t             nOffsetY;         ///< Configured offset (Y direction) for buttons from parent container

    int8_t              nFrameMargin;     ///< Margin around text value field
    uint8_t             nMaxCols;         ///< Maximum number of columns to occupy
    uint8_t             nMaxRows;         ///< Maximum number of rows to occupy
  } gslc_tsXKeyPadCfg;

  /// Input callback data structure
  /// - This struct is returned in GSLC_CB_INPUT when the
  ///   KeyPad edits are complete, and is used to provide
  ///   the resulting edited value.
  typedef struct {
    char*               pStr;             ///< Final value of edited value field
    int16_t             nTargetId;        ///< Target element ID to receive the value
  } gslc_tsXKeyPadData;

  /// Extended data for KeyPad element
  typedef struct {

    // State
    uint8_t             nValStrPos;       ///< Current number of characters stored in edit value string
    char                acValStr[XKEYPAD_VAL_LEN];  ///< Storage for edit value string
    bool                bValPositive;     ///< Value is positive if true, negative if false
    bool                bValDecimalPt;    ///< Value string includes decimal point

    // Config
    char**              pacKeys;          ///< Array of character strings for KeyPad labels
    gslc_tsXKeyPadCfg   sConfig;          ///< Configuration options

    GSLC_CB_INPUT       pfuncCb;          ///< Callback function for KeyPad actions
    XKEYPAD_LOOKUP      pfuncLookup;      ///< Callback function for converting key into key label
    int16_t             nTargetId;        ///< Target element ID associated with keypad (GSLC_CB_INPUT)

    // Internal sub-element members
    gslc_tsCollect      sCollect;         ///< Collection management for sub-elements
    uint8_t             nSubElemMax;      ///< Maximum number of sub-elements to create within KeyPad container
    gslc_tsElemRef*     psElemRef;        ///< Ptr to storage for sub-element references
    gslc_tsElem*        psElem;           ///< Ptr to storage for sub-elements

  } gslc_tsXKeyPad;


// Callback function for KeyPad creation
typedef void (*XKEYPAD_CREATE)(gslc_tsGui* pGui,gslc_tsXKeyPad* pXData);


  ///
  /// Add a key to the KeyPad control
  ///
  /// \param[in]  pGui:        Pointer to GUI
  /// \param[in]  pXData:      Ptr to extended element data structure
  /// \param[in]  nKeyId:      ID to associated with the key
  /// \param[in]  bTxtField:   Is this the text value field?
  /// \param[in]  nRow:        Element placement position (row index, 0 at top)
  /// \param[in]  nCol:        Element placement position (column index, 0 at left)
  /// \param[in]  nRowSpan:    Number of columns to occupy by element (1 for normal size, 2 for double width)
  /// \param[in]  nColSpan:    Number of rows to occupy by element (1 for normal size, 2 for double height)
  /// \param[in]  cColFill:    Fill color for element
  /// \param[in]  cColGlow:    Fill color for element when glowing
  /// \param[in]  bVisible:    Initial key visibility state
  ///
  /// \return none
  ///
void XKeyPadAddKeyElem(gslc_tsGui* pGui, gslc_tsXKeyPad* pXData, int16_t nKeyId, bool bTxtField, int16_t nRow, int16_t nCol,
  int8_t nRowSpan, int8_t nColSpan, gslc_tsColor cColFill, gslc_tsColor cColGlow, bool bVisible);

  ///
  /// Create a KeyPad Element
  ///
  /// \param[in]  pGui:        Pointer to GUI
  /// \param[in]  nElemId:     Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
  /// \param[in]  nPage:       Page ID to attach element to
  /// \param[in]  pXData:      Ptr to extended element data structure
  /// \param[in]  nX0:         X KeyPad Starting Coordinate 
  /// \param[in]  nY0:         Y KeyPad Starting Coordinate 
  /// \param[in]  nFontId:     Font ID to use for drawing the element
  /// \param[in]  pConfig:     Ptr to Config options
  /// \param[in]  pfuncCreate: Ptr to callback function for creation
  /// \param[in]  pfuncLookup: Ptr to callback function for button lookups
  ///
  /// \return Pointer to Element or NULL if failure
  ///
  gslc_tsElemRef* gslc_ElemXKeyPadCreateBase(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
    gslc_tsXKeyPad* pXData, int16_t nX0, int16_t nY0, int8_t nFontId, gslc_tsXKeyPadCfg* pConfig,
    XKEYPAD_CREATE pfuncCreate, XKEYPAD_LOOKUP pfuncLookup);


  ///
  /// Set the current value for the editable text field
  ///
  /// \param[in]  pGui:        Pointer to GUI
  /// \param[in]  pElemRef:   Ptr to KeyPad Element reference
  /// \param[in]  pStrBuf:     String to copy into keypad
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadValSet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, const char* pStrBuf);

  ///
  /// Set target element ID for KeyPad return value
  /// - The Target ID is used in the GSLC_CB_INPUT callback so that the user
  ///   has the context needed to determine which field should be edited
  ///   with the contents of the KeyPad edit field
  /// - It is expected that the user will call this function when showing
  ///   the KeyPad popup dialog
  ///
  /// \param[in]  pGui:       Pointer to GUI
  /// \param[in]  pElemRef:   Ptr to KeyPad Element reference
  /// \param[in]  nId:        Element enum ID for target of KeyPad value
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadTargetIdSet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nId);


  ///
  /// Set the current output string buffer associated with NumericInput element
  ///
  /// \param[in]  pGui:        Pointer to GUI
  /// \param[in]  pElemRef:    Ptr to KeyPad Element reference
  /// \param[in]  pStrBuf:     String to copy into element
  /// \param[in]  nStrBufMax:  Maximum length of string buffer (pStrBuf)
  ///
  /// \return none
  ///
  bool gslc_ElemXKeyPadValGet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, char* pStrBuf, uint8_t nStrBufMax);

  ///
  /// Fetch the edited value string from the KeyPad
  ///
  /// \param[in]  pGui:       Pointer to GUI
  /// \param[in]  pvData :    Void ptr to callback data structure
  ///
  /// \return Pointer to edited character string
  ///
  char* gslc_ElemXKeyPadDataValGet(gslc_tsGui* pGui, void* pvData);


  ///
  /// Fetch the element target ID associated with this KeyPad
  ///
  /// \param[in]  pGui:       Pointer to GUI
  /// \param[in]  pvData :    Void ptr to callback data structure
  ///
  /// \return Target Element ID or GSLC_ID_NONE if unspecified
  ///
  int16_t gslc_ElemXKeyPadDataTargetIdGet(gslc_tsGui* pGui, void* pvData);

  ///
  /// Draw a KeyPad element on the screen
  /// - Called during redraw
  ///
  /// \param[in]  pvGui:       Void ptr to GUI (typecast to gslc_tsGui*)
  /// \param[in]  pvElemRef:   Void ptr to Element reference (typecast to gslc_tsElemRef*)
  /// \param[in]  eRedraw:     Redraw mode
  ///
  /// \return true if success, false otherwise
  ///
  bool gslc_ElemXKeyPadDraw(void* pvGui, void* pvElemRef, gslc_teRedrawType eRedraw);

  ///
  /// Handle a click event within the KeyPad
  /// - This is called internally by the KeyPad touch handler
  ///
  /// \param[in]  pvGui:       Void ptr to GUI (typecast to gslc_tsGui*)
  /// \param[in]  pvElemRef:   Void ptr to Element Ref (typecast to gslc_tsElemRef*)
  /// \param[in]  eTouch:      Touch event type
  /// \param[in]  nX:          Touch X coord
  /// \param[in]  nY:          Touch Y coord
  ///
  /// \return none
  ///
  bool gslc_ElemXKeyPadClick(void* pvGui, void *pvElemRef, gslc_teTouch eTouch, int16_t nX, int16_t nY);

  ///
  /// Handle touch (up,down,move) events to KeyPad element
  /// - Called from gslc_ElemSendEventTouch()
  ///
  /// \param[in]  pvGui:       Void ptr to GUI (typecast to gslc_tsGui*)
  /// \param[in]  pvElemRef:   Void ptr to Element ref (typecast to gslc_tsElemRef*)
  /// \param[in]  eTouch:      Touch event type
  /// \param[in]  nRelX:       Touch X coord relative to element
  /// \param[in]  nRelY:       Touch Y coord relative to element
  ///
  /// \return true if success, false otherwise
  ///
  bool gslc_ElemXKeyPadTouch(void* pvGui, void* pvElemRef, gslc_teTouch eTouch, int16_t nRelX, int16_t nRelY);

  ///
  /// Set the callback function associated with the KeyPad
  /// - This function will be called during updates and OK / Cancel
  ///
  /// \param[in]  pGui:        Pointer to GUI
  /// \param[in]  pElemRef:    Pointer to Element Reference for KeyPad
  /// \param[in]  pfuncCb:     Callback function pointer
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadValSetCb(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, GSLC_CB_INPUT pfuncCb);

  ///
  /// Update the KeyPad configuration to enable floating point numbers
  /// - Effectively disables/enables the decimal point button & handling
  ///
  /// \param[in]  pConfig:     Pointer to the XKeyPad config structure
  /// \param[in]  bEn:         Enable flag (true if floating point enabled)
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadCfgSetFloatEn(gslc_tsXKeyPadCfg* pConfig,bool bEn);

  ///
  /// Update the KeyPad configuration to enable negative numbers
  /// - Effectively disables/enables the sign button & handling
  ///
  /// \param[in]  pConfig:     Pointer to the XKeyPad config structure
  /// \param[in]  bEn:         Enable flag (true if negative numbers enabled)
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadCfgSetSignEn(gslc_tsXKeyPadCfg* pConfig,bool bEn);

  ///
  /// Update the KeyPad configuration to enable rounded button corners
  ///
  /// \param[in]  pConfig:     Pointer to the XKeyPad config structure
  /// \param[in]  bEn:         Enable rounded corners
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadCfgSetRoundEn(gslc_tsXKeyPadCfg* pConfig,bool bEn);

  ///
  /// Update the KeyPad configuration to define the KeyPad button sizing
  ///
  /// \param[in]  pConfig:     Pointer to the XKeyPad config structure
  /// \param[in]  nButtonSzW:  Width of buttons in pixels
  /// \param[in]  nButtonSzH:  Width of buttons in pixels
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadCfgSetButtonSz(gslc_tsXKeyPadCfg* pConfig, int8_t nButtonSzW, int8_t nButtonSzH);

  ///
  /// Update the KeyPad active configuration to enable negative numbers
  /// - Effectively disables/enables the sign button & handling
  ///
  /// \param[in]  pGui:        Pointer to GUI
  /// \param[in]  pElemRef:    Pointer to KeyPad element reference
  /// \param[in]  bEn:         Enable flag (true if negative numbers enabled)
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadSetFloatEn(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, bool bEn);

  ///
  /// Update the KeyPad active configuration to enable negative numbers
  /// - Effectively disables/enables the sign button & handling
  ///
  /// \param[in]  pGui:        Pointer to GUI
  /// \param[in]  pElemRef:    Pointer to KeyPad element reference
  /// \param[in]  bEn:         Enable flag (true if negative numbers enabled)
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadSetSignEn(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, bool bEn);

  ///
  /// Trigger a KeyPad popup and associate it with a text element
  ///
  /// \param[in]  pGui:        Pointer to GUI
  /// \param[in]  pKeyPadRef:  Pointer to KeyPad element reference
  /// \param[in]  nPgPopup:    Page enum that contains the popup to show
  /// \param[in]  pTxtRef:     Pointer to associated text field element reference
  ///
  /// \return none
  ///
  void gslc_ElemXKeyPadInputAsk(gslc_tsGui* pGui, gslc_tsElemRef* pKeyPadRef, int16_t nPgPopup, gslc_tsElemRef* pTxtRef);

  ///
  /// Complete a KeyPad popup by retrieving the input data and storing it in the text element
  ///
  /// \param[in]  pGui:        Pointer to GUI
  /// \param[in]  pTxtRef:     Pointer to associated text field element reference
  /// \param[in]  pvCbData:    Void pointer to callback function's pvData
  ///
  /// \return The text string that was fetched from the KeyPad
  ///
  char* gslc_ElemXKeyPadInputGet(gslc_tsGui* pGui, gslc_tsElemRef* pTxtRef, void* pvCbData);


  #endif // GSLC_FEATURE_COMPOUND

// ============================================================================

// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------

// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XKEYPAD_H_

