/**
 * \file cimditprofile.h
 * \brief Declaration of the CimditProfile class
 * \author GrumpyDeveloper (Sascha Nitsch)
 * \copyright 2022 Sascha Nitsch
 * Licensed under MIT license
 *
 */

#ifndef CIMDITPROFILE_H_
#define CIMDITPROFILE_H_

// our defines
#include "defines.h"

// system includes
#include <inttypes.h>

// library include
#include <HID-Project.h>

// own include
#include "cimdithal.h"

/**
 * \class CimditProfile
 * \brief Profile storage and action class
 */
class CimditProfile {
 public:
  /// constructor
  CimditProfile();

  /// \brief initialize
  void begin();

  /// a button has been pressed or release
  /// \param num which button number
  /// \param state current button state (true if pressed)
  void buttonAction(uint8_t num, bool state);

  /// an axis was moved
  /// \param num which axis number
  /// \param state current axis state (absolute value)
  void axisAction(uint8_t num, uint16_t state);

  /// an axis was moved
  /// \param num which axis number
  /// \param delta rotary delta value
  void rotaryAction(uint8_t num, int8_t delta);

  /// periodic tick function, for timeouts, turning off display a.s.o.
  void tick();

  /// print flash(profile) content to serial
  void printFlash();

  /// write flash(profile) content from serial
  void writeFlash();

  /// set user string
  /// \param timeout timout for string
  /// \param input string to print out
  void setUserString(uint8_t timeout, const char* input);

  /// display user submitted graphic
  void userDisplay();

  /// mapping types
  enum MappingType {
    /// no mapping
    MAPPING_NONE = 0,
    /// mapped as joystick button (only works for buttons)
    JOYSTICK_BUTTON,
    /// mapped as joystick axis (only works for analog values)
    JOYSTICK_AXIS,
    /// mapped as mouse button (only works for buttons)
    MOUSE_BUTTON,
    /// mapped as mouse relative/incremental X axis (works for analog values, rotary encoder and buttons)
    MOUSE_REL_X_AXIS,
    /// mapped as mouse relative/incremental Y axis (works for analog values, rotary encoder and buttons)
    MOUSE_REL_Y_AXIS,
    /// mapped as mouse relative/incremental wheel (works for rotary encoder and buttons)
    MOUSE_REL_WHEEL,
    /// select to next profile
    NEXT_PROFILE,
    /// select to previous profile
    PREV_PROFILE,
    /// confirm selection
    SWITCH_PROFILE,
    /// macro key pressed
    MACRO_PRESS,
    /// macro key released
    MACRO_RELEASE,
    /// mapping as keyboard button
    KEYBOARD_BUTTON,
  };

  /// enums for use in macros
  enum MACROCOMMANDS {
    /// null terminator
    MACRO_NULL = 0,
    /// end marker
    MACRO_END = 0,
    // time functions
    /// speed for typing (chars per second)
    MACRO_SPEED,         // 1
    /// wait a while (in ms)
    MACRO_DELAY,         // 2
    // press/release functions
    /// key press
    MACRO_KEY_PRESS,     // 3
    /// key release
    MACRO_KEY_RELEASE,   // 4
    /// joystick button press
    MACRO_JOY_PRESS,     // 5
    /// joystick button release
    MACRO_JOY_RELEASE,   // 6
    /// mouse press
    MACRO_MOUSE_PRESS,   // 7
    /// mouse release
    MACRO_MOUSE_RELEASE, // 8
    /// mouse wheel rotation
    MACRO_MOUSE_WHEEL,   // 9
    // typing functions
    /// type a series of keys (press, delay, release)<repeated>
    MACRO_TYPE,          // 10
    // axis mapping functions
    /// move mouse X according to given value
    MACRO_MOUSE_REL_X,   // 11
    /// move mouse Y according to given value
    MACRO_MOUSE_REL_Y,   // 12
    /// move mouse X according to an analog axis
    MACRO_MOUSE_REL_X_AXIS,   // 13
    /// move mouse Y according to an analog axis
    MACRO_MOUSE_REL_Y_AXIS,   // 14
  };

 private:
  /// get unsigned int 8 from EEPROM and advance one byte
  /// \return eeprom value
  uint8_t nextUInt8();

  /// get unsigned int 16 from EEPROM and advance two byte
  /// \return eeprom value
  uint16_t nextUInt16();

  /// get 0-terminated string from EEPROM
  /// \param destination destination pointer
  void nextString(unsigned char* destination);

  /// initialize profile structures
  void initProfiles();
  
  /// load profile
  /// \param profileNum number of profile to load
  void load(uint8_t);

  /// scan macros and optionally execute fiven Macro
  /// \param macroNum number of macro
  /// \param execute should macro be executed (true) or just scanned (false)
  void scanOrExecuteMacro(uint8_t macroNum, bool execute);

#ifdef EXTERNAL_EEPROM
  /**
   * \brief write to eeprom
   * \param addr addr to write to
   * \param val value to write
   */
  void eepromWrite(uint16_t addr, uint8_t val);
#endif

  /**
   * \brief show activation confirmation
   */
  void showActivate();
  
  /**
   * show currently active profile
   */
  void showActiveProfile();

  /**
   * show user string
   */
  void showUserString();

  /**
   * \brief print out text centered on display
   * \param text
   */
  void printTextCentered(const char* text);
  
  /// list of profile names
  char m_profiles[NUMBER_OF_PROFILES][16];

  /// number of profiles
  uint8_t m_numProfiles;

  /// active profile
  uint8_t m_activeProfile;

  /// selecting profile
  uint8_t m_selectingProfile;

  /// Mapping structure
  struct Map {
    /// number of (button/axis/rotary)
    uint8_t m_number;

    /// mapping type
    MappingType m_type;

    /// optional value
    int8_t m_value;
  };

  /// number of mapped buttons
  uint8_t m_numMappedButtons;

  /// actual button mappings
  Map m_mappedButtons[64];

  /// number of mapped axis
  uint8_t m_numMappedAxis;

  /// actual axis mappings
  Map m_mappedAxis[16];

  /// number of mapped rotary
  uint8_t m_numMappedRotary;

  /// actual rotary mappings
  Map m_mappedRotary[8];

  /// automatic mouse movement in X direction
  int16_t m_mouseMoveX;

  /// automatic mouse movement in Y direction
  int16_t m_mouseMoveY;

  /// current EEPROM position
  uint16_t m_eepromPosition;

  /// number of macros
  uint8_t m_numMacros;

  /// macro start position
  uint16_t *m_macroStart;

  /// different display states
  enum DISPLAY_STATE {
    /// almost blank display to protect oled
    DISPLAY_BLANK = 0,
    /// show current profile
    DISPLAY_PROFILE,
    /// show confirmation screen
    DISPLAY_CONFIRMATION,
    /// show user submitted (via USB) string
    DISPLAY_USER_STRING
  };

  /// active display state
  DISPLAY_STATE m_displayState;

  /// time when state started
  uint32_t m_stateStarted;

  /// time when state should be timed out
  uint32_t m_stateTimeout;

  /// time for next bar update
  uint32_t m_nextBarUpdate;

  /// screen saver last dot X position
  uint8_t m_dotX;

  /// screen saver last dot Y position
  uint8_t m_dotY;

  /// user submitted string to output
  char m_userString[43];

  /// timeout of submitted string
  uint32_t m_userTimeout;

  float m_relMouseX, m_relMouseY;
  uint16_t m_customImage;
};

#endif  // CIMDITPROFILE_H_
