#include "_Plugin_Helper.h"
#ifdef USES_P096

// #######################################################################################################
// #################################### Plugin 096: eInk display #################################
// #######################################################################################################

# define PLUGIN_096
# define PLUGIN_ID_096         96
# define PLUGIN_NAME_096       "Display - eInk with Lolin ePaper screen"
# define PLUGIN_VALUENAME1_096 "CursorX"
# define PLUGIN_VALUENAME2_096 "CursorY"

// #define PLUGIN_096_MAX_DISPLAY 1 // Unused

/* README.MD


 ## INTRO

   This plugin allow to control a eInk screen (ILI3897) through HTTP API

 ## Environment
   Tested with Lolin d32 pro and Wemos ePaper 2.13 shield
   Tested with ESPEasy 2.4.2  -tag mega-201902225)

   ePaper Shield : https://www.wemos.cc/en/latest/d1_mini_shiled/epd_2_13.html
   Price : ~ 10€/$ (https://fr.aliexpress.com/item/32981318996.html)


 ## Dependencies
   Plugin lib_deps = Adafruit GFX, LOLIN_EPD

 ## API Documentation

   This plugin is controlled by HTTP API, ie : http://<espeasy_ip>/control?cmd=epd,tx,HelloWorld

 | command | details | description |
 |-----|-----|-----|
 | epd | `EPD,<epd_subcommand>,....` | Draw line, rect, circle, triangle and text |
 |epdcmd | `EPDCMD,<epdcmd_subcommand>` | Control the screen (on, off, clear,..) |

   EPD Subcommands:

 | EPD Subcommands | details | description |
 |-----|-----|-----|
 | txt | txt,<text> | Write simple text (use last position, color and size) |
 | txp | txp,<X>,<Y> | Set text position (move the cursor) |
 | txc | txc,<foreColor>,<backgroundColor> | Set text color (background is transparent if not provided |
 | txz | txz,<X>,<Y>,<text> | Write text at position (move the cursor + print) |
 | txs | txs,<SIZE> | Set text size |
 | txtfull | txtfull,<row>,<col>,<size=1>,<foreColor=white>,<backColor=black>,<text> | Write text with all options |
 | l | l,<x1>,<y1>,<2>,<y2>,<color> | Draw a simple line |
 | lh | lh,<y>,<width>,<color> | Draw an horizontal line (width = Line width in pixels (positive = right of first point, negative = point of
 |first corner). |
 | lv | lv,<x>,<height>,<color> | Draw a vertical line (height= Line height in pixels (positive = below first point, negative = above first
 |point).|
 | r | r,<x>,<y>,<width>,<height>,<color> | Draw a rectangle |
 | rf | rf,<x>,<y>,<width>,<height>,<bordercolor>,<innercolor> | Draw a filled rectangle |
 | c | c,<x>,<y>,<radius>,<color> | Draw a circle |
 | cf | cf,<x>,<y>,<radius>,<bordercolor>,<innercolor> | Draw a filled circle |
 | t | t,<x1>,<y1>,<x2>,<y2>,<x3>,<y3>,<color>| Draw a triangle |
 | tf | tf,<x1>,<y1>,<x2>,<y2>,<x3>,<y3>,<bordercolor>,<innercolor> | Draw a filled triangle |
 | rr | rr,<x>,<y>,<width>,<height>,<corner_radius>,<color> | Draw a round rectangle |
 | rrf | rrf,<x>,<y>,<width>,<height>,<corner_radius>,<bordercolor>,<innercolor> | Draw a filled round rectangle |
 | px | px,<x>,<y>,<color> | Print a single pixel |

   EPDCMD Subcommands:

 | EPD Subcommands | details | description |
 |-----|-----|-----|
 | clear | clear,<color> | Clear display |
 | deepsleep | deepsleep | Make screen go to sleep |
 | inv | inv,<value> | Invert the dispaly (value:0 normal display, 1 inverted display) |
 | rot | rot,<value> | Rotate display (value from 0 to 3 inclusive) |


   Examples:

        Write Text :
                http://<espeasy_ip>/control?cmd=epd,txtfull,0,0,HelloWorld

        Write Text another place:
                http://<espeasy_ip>/control?cmd=epd,txtfull,100,40,HelloWorld

        Write bigger Text :
                http://<espeasy_ip>/control?cmd=epd,txtfull,0,0,3,HelloWorld

        Write RED Text :
                http://<espeasy_ip>/control?cmd=epd,txtfull,0,0,3,HelloWorld

        Write RED Text (size is 1):
                http://<espeasy_ip>/control?cmd=epd,txtfull,0,0,1,RED,HelloWorld

        Write RED Text on YELLOW background (size is 1):
                http://<espeasy_ip>/control?cmd=epd,txtfull,0,0,1,RED,YELLOW,HelloWorld

        Clear whole display
                http://<espeasy_ip>/control?cmd=epdcmd,clear

        Deepsleep screen
                http://<espeasy_ip>/control?cmd=epdcmd,deepsleep

 */

// plugin dependency
# include "src/PluginStructs/P096_data_struct.h"

// #include <LOLIN_EPD.h>
// #include <Adafruit_GFX.h>
// #ifdef P096_USE_ADA_GRAPHICS
// #include "src/Helpers/AdafruitGFX_helper.h"
// #endif

# ifndef P096_USE_ADA_GRAPHICS

// // declare functions for using default value parameters
// void Plugin_096_printText(const char    *string,
//                           int            X,
//                           int            Y,
//                           unsigned int   textSize = 1,
//                           unsigned short color    = EPD_WHITE,
//                           unsigned short bkcolor  = EPD_BLACK);
# endif // ifndef P096_USE_ADA_GRAPHICS

// Define the default values for both ESP32/lolin32 and D1 Mini
# ifdef ESP32

// for D32 Pro with EPD connector
  #  define EPD_CS 14
  #  define EPD_CS_HSPI 26 // when connected to Hardware-SPI GPIO-14 is already used
  #  define EPD_DC 27
  #  define EPD_RST 33     // can set to -1 and share with microcontroller Reset!
  #  define EPD_BUSY -1    // can set to -1 to not use a pin (will wait a fixed delay)
# else // ifdef ESP32

// for D1 Mini with shield connection
  #  define EPD_CS  16  // D0
  #  define EPD_DC  15  // D8
  #  define EPD_RST -1  // can set to -1 and share with microcontroller Reset!
  #  define EPD_BUSY -1 // can set to -1 to not use a pin (will wait a fixed delay)
# endif // ifdef ESP32


// // The setting structure
// struct Plugin_096_EPD_SettingStruct
// {
//   Plugin_096_EPD_SettingStruct()
//     : address_epd_cs(EPD_CS), address_epd_dc(EPD_DC), address_epd_rst(EPD_RST), address_epd_busy(EPD_BUSY), rotation(0), width(250),
// height(
//       122)
//   {}

//   uint8_t address_epd_cs;
//   uint8_t address_epd_dc;
//   uint8_t address_epd_rst;
//   uint8_t address_epd_busy;
//   uint8_t rotation;
//   int     width;
//   int     height;
// } EPD_Settings;

// // The display pointer
// LOLIN_IL3897 *eInkScreen                = nullptr;
// uint8_t plugin_096_sequence_in_progress = false;
// # ifdef P096_USE_ADA_GRAPHICS
// AdafruitGFX_helper *gfxHelper = nullptr;
// # endif // ifdef P096_USE_ADA_GRAPHICS

boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string)
{
  boolean success = false;

  switch (function)
  {
    case PLUGIN_DEVICE_ADD:
    {
      auto& dev = Device[++deviceCount];
      dev.Number = PLUGIN_ID_096;
      dev.Type   = DEVICE_TYPE_SPI3;
      dev.VType  = Sensor_VType::SENSOR_TYPE_NONE;
      # if P096_USE_EXTENDED_SETTINGS
      dev.ValueCount    = 2;
      dev.TimerOption   = true;
      dev.TimerOptional = true;
      # endif // if P096_USE_EXTENDED_SETTINGS
      break;
    }

    case PLUGIN_GET_DEVICENAME:
    {
      string  = F(PLUGIN_NAME_096);
      success = true;
      break;
    }

    case PLUGIN_GET_DEVICEVALUENAMES:
    {
      strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_096));
      strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_096));
      success = true;
      break;
    }

    case PLUGIN_GET_DEVICEGPIONAMES:
    {
      event->String1 = formatGpioName_output(F("EPD CS"));
      event->String2 = formatGpioName_output(F("EPD DC"));
      event->String3 = formatGpioName_output(F("EPD RST"));
      break;
    }

    case PLUGIN_SET_DEFAULTS:
    {
      PIN(0) = EPD_CS;
      # ifdef ESP32

      if (Settings.InitSPI == 2) { // When using ESP32 H(ardware-)SPI
        PIN(0) = EPD_CS_HSPI;
      }
      # endif // ifdef ESP32
      PIN(1) = EPD_DC;
      PIN(2) = EPD_RST;
      PIN(3) = EPD_BUSY;
      # if P096_USE_EXTENDED_SETTINGS

      P096_CONFIG_COLORS = static_cast<uint16_t>(AdaGFXMonoRedGreyscaleColors::ADAGFXEPD_BLACK) | // Default to dark on white (paper) colors
                           (static_cast<uint16_t>(AdaGFXMonoRedGreyscaleColors::ADAGFXEPD_WHITE) << 16);

      uint32_t lSettings = 0;
      set4BitToUL(lSettings, P096_CONFIG_FLAG_COLORDEPTH, static_cast<uint8_t>(AdaGFXColorDepth::Monochrome)); // Bit 20..23 Color depth
      P096_CONFIG_FLAGS = lSettings;
      # else // if P096_USE_EXTENDED_SETTINGS
      P096_CONFIG_WIDTH  = 250;
      P096_CONFIG_HEIGHT = 122;
      # endif // if P096_USE_EXTENDED_SETTINGS
      break;
    }

    case PLUGIN_WEBFORM_SHOW_GPIO_DESCR:
    {
      string  = concat(F("EPD BUSY: "), formatGpioLabel(PIN(3), false));
      success = true;
      break;
    }

    case PLUGIN_WEBFORM_LOAD:
    {
      addFormPinSelect(PinSelectPurpose::Generic_output, formatGpioName_output(F("EPD BUSY")), F("_epd_busy"), PIN(3));

      # if P096_USE_EXTENDED_SETTINGS

      if (P096_CONFIG_VERSION < 2) {
        strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_096)); // Values introduced in V2 settings
        strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_096));
      }

      {
        const __FlashStringHelper *options4[] = {
          EPD_type_toString(EPD_type_e::EPD_IL3897),
          EPD_type_toString(EPD_type_e::EPD_UC8151D),
          EPD_type_toString(EPD_type_e::EPD_SSD1680),
          #  if P096_USE_WAVESHARE_2IN7
          EPD_type_toString(EPD_type_e::EPD_WS2IN7)
          #  endif // if P096_USE_WAVESHARE_2IN7
        };
        const int optionValues4[] = {
          static_cast<int>(EPD_type_e::EPD_IL3897),
          static_cast<int>(EPD_type_e::EPD_UC8151D),
          static_cast<int>(EPD_type_e::EPD_SSD1680),
          #  if P096_USE_WAVESHARE_2IN7
          static_cast<int>(EPD_type_e::EPD_WS2IN7)
          #  endif // if P096_USE_WAVESHARE_2IN7
        };
        constexpr size_t optionCount = NR_ELEMENTS(optionValues4);
        const FormSelectorOptions selector(
          optionCount,
          options4,
          optionValues4);
        selector.addFormSelector(F("eInk display model"),
                        F("_type"),
                        P096_CONFIG_FLAG_GET_DISPLAYTYPE);
      }

      addFormSubHeader(F("Layout"));
      # endif // if P096_USE_EXTENDED_SETTINGS

      # ifdef P096_USE_ADA_GRAPHICS
      AdaGFXFormRotation(F("_rotate"), P096_CONFIG_ROTATION);
      # else // ifdef P096_USE_ADA_GRAPHICS
      {
        const __FlashStringHelper *options2[] = { F("Normal"), F("+90&deg;"), F("+180&deg;"), F("+270&deg;") };
        int optionValues2[]                   = { 0, 1, 2, 3 };
        constexpr size_t optionCount          = NR_ELEMENTS(optionValues2);
        const FormSelectorOptions selector( optionCount, options2, optionValues2);
        selector.addFormSelector(F("Rotation"), F("_rotate"), P096_CONFIG_ROTATION);
      }
      # endif // ifdef P096_USE_ADA_GRAPHICS

      # if !P096_USE_EXTENDED_SETTINGS

      // Width and Height no longer needed as ythat's define by the Display Type
      uint16_t width_ = P096_CONFIG_WIDTH;

      if (width_ == 0) {
        width_ = 250; // default value
      }
      addFormNumericBox(F("Width (px)"), F("_width"), width_, 1, 65535);

      uint16_t height_ = P096_CONFIG_HEIGHT;

      if (height_ == 0) {
        height_ = 122; // default value
      }
      addFormNumericBox(F("Height (px)"), F("_height"), height_, 1, 65535);
      # endif // if !P096_USE_EXTENDED_SETTINGS

      # if P096_USE_EXTENDED_SETTINGS
      {
        const __FlashStringHelper *colorDepths[] = { // Be sure to use all options needed
          toString(AdaGFXColorDepth::Monochrome),
          toString(AdaGFXColorDepth::BlackWhiteRed),
          toString(AdaGFXColorDepth::BlackWhite2Greyscales),
          #  if ADAGFX_SUPPORT_7COLOR
          toString(AdaGFXColorDepth::SevenColor)
          #  endif // if ADAGFX_SUPPORT_7COLOR
        };
        const int colorDepthOptions[] = {
          static_cast<int>(AdaGFXColorDepth::Monochrome),
          static_cast<int>(AdaGFXColorDepth::BlackWhiteRed),
          static_cast<int>(AdaGFXColorDepth::BlackWhite2Greyscales),
          #  if ADAGFX_SUPPORT_7COLOR
          static_cast<int>(AdaGFXColorDepth::SevenColor)
          #  endif // if ADAGFX_SUPPORT_7COLOR
        };

        if (P096_CONFIG_FLAG_GET_COLORDEPTH == 0) {                                                                // Enum doesn't have 0
          uint32_t lSettings = 0;
          set4BitToUL(lSettings, P096_CONFIG_FLAG_COLORDEPTH, static_cast<uint8_t>(AdaGFXColorDepth::Monochrome)); // Bit 20..23 Color depth
          P096_CONFIG_FLAGS = lSettings;
        }
        constexpr size_t optionCount = NR_ELEMENTS(colorDepthOptions);
        const FormSelectorOptions selector(optionCount, colorDepths, colorDepthOptions);
        selector.addFormSelector(F("Greyscale levels"),F("_colorDepth"),
            P096_CONFIG_FLAG_GET_COLORDEPTH);
      }

      AdaGFXFormTextPrintMode(F("_mode"), P096_CONFIG_FLAG_GET_MODE);

      #  if ADAGFX_FONTS_INCLUDED
      AdaGFXFormDefaultFont(F("deffont"), P096_CONFIG_DEFAULT_FONT);
      #  endif // if ADAGFX_FONTS_INCLUDED

      AdaGFXFormFontScaling(F("_fontscale"), P096_CONFIG_FLAG_GET_FONTSCALE);

      {
        const __FlashStringHelper *commandTriggers[] = { // Be sure to use all options available in the enum (except MAX)!
          P096_CommandTrigger_toString(P096_CommandTrigger::epd),
          P096_CommandTrigger_toString(P096_CommandTrigger::eInk),
          P096_CommandTrigger_toString(P096_CommandTrigger::ePaper),
          P096_CommandTrigger_toString(P096_CommandTrigger::il3897),
          P096_CommandTrigger_toString(P096_CommandTrigger::uc8151d),
          P096_CommandTrigger_toString(P096_CommandTrigger::ssd1680),
          #  if P096_USE_WAVESHARE_2IN7
          P096_CommandTrigger_toString(P096_CommandTrigger::ws2in7)
          #  endif // if P096_USE_WAVESHARE_2IN7
        };
        const int commandTriggerOptions[] = {
          static_cast<int>(P096_CommandTrigger::epd),
          static_cast<int>(P096_CommandTrigger::eInk),
          static_cast<int>(P096_CommandTrigger::ePaper),
          static_cast<int>(P096_CommandTrigger::il3897),
          static_cast<int>(P096_CommandTrigger::uc8151d),
          static_cast<int>(P096_CommandTrigger::ssd1680),
          #  if P096_USE_WAVESHARE_2IN7
          static_cast<int>(P096_CommandTrigger::ws2in7)
          #  endif // if P096_USE_WAVESHARE_2IN7
        };
        constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions);
        const FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions);
        selector.addFormSelector(F("Write Command trigger"), F("_commandtrigger"),
                        P096_CONFIG_FLAG_GET_CMD_TRIGGER);
        addFormNote(F("Select the command that is used to handle commands for this display."));
      }

      AdaGFXFormTextColRowMode(F("_colrow"), bitRead(P096_CONFIG_FLAGS, P096_CONFIG_FLAG_USE_COL_ROW) == 1);

      AdaGFXFormOnePixelCompatibilityOption(F("_compat"), !bitRead(P096_CONFIG_FLAGS, P096_CONFIG_FLAG_COMPAT_P096)); // Inverse

      AdaGFXFormTextBackgroundFill(F("_backfill"), bitRead(P096_CONFIG_FLAGS, P096_CONFIG_FLAG_BACK_FILL) == 0);      // Inverse

      addFormSubHeader(F("Content"));

      if (P096_CONFIG_COLORS == 0) { // For migrating from older release task settings
        P096_CONFIG_COLORS = static_cast<uint16_t>(AdaGFXMonoRedGreyscaleColors::ADAGFXEPD_WHITE) |
                             (static_cast<uint16_t>(AdaGFXMonoRedGreyscaleColors::ADAGFXEPD_BLACK) << 16);
      }
      AdaGFXFormForeAndBackColors(F("_foregroundcolor"),
                                  P096_CONFIG_GET_COLOR_FOREGROUND,
                                  F("_backgroundcolor"),
                                  P096_CONFIG_GET_COLOR_BACKGROUND,
                                  static_cast<AdaGFXColorDepth>(P096_CONFIG_FLAG_GET_COLORDEPTH));

      String strings[P096_Nlines];
      LoadCustomTaskSettings(event->TaskIndex, strings, P096_Nlines, 0);

      uint16_t remain = DAT_TASKS_CUSTOM_SIZE + DAT_TASKS_CUSTOM_EXTENSION_SIZE;

      for (uint8_t varNr = 0; varNr < P096_Nlines; varNr++) {
        addFormTextBox(concat(F("Line "), (varNr + 1)), getPluginCustomArgName(varNr), strings[varNr], P096_Nchars);
        remain -= (strings[varNr].length() + 1);
      }
      addUnit(concat(F("Remaining: "),  remain));

      # endif // if P096_USE_EXTENDED_SETTINGS

      success = true;
      break;
    }

    case PLUGIN_WEBFORM_SAVE:
    {
      # if P096_USE_EXTENDED_SETTINGS
      P096_CONFIG_VERSION = 2; // mark config V2 as already saved (next time, will not use default values)
      # else // if P096_USE_EXTENDED_SETTINGS
      P096_CONFIG_VERSION = 1; // mark config as already saved (next time, will not use default values)
      # endif // if P096_USE_EXTENDED_SETTINGS

      // PIN(0)..(2) are already set
      PIN(3)               = getFormItemInt(F("_epd_busy"));
      P096_CONFIG_ROTATION = getFormItemInt(F("_rotate"));
      # if !P096_USE_EXTENDED_SETTINGS
      P096_CONFIG_WIDTH  = getFormItemInt(F("_width"));
      P096_CONFIG_HEIGHT = getFormItemInt(F("_height"));
      # endif // if !P096_USE_EXTENDED_SETTINGS
      # if ADAGFX_FONTS_INCLUDED
      P096_CONFIG_DEFAULT_FONT = getFormItemInt(F("deffont"));
      # endif // if ADAGFX_FONTS_INCLUDED

      # if P096_USE_EXTENDED_SETTINGS

      uint32_t lSettings = 0;

      bitWrite(lSettings, P096_CONFIG_FLAG_USE_COL_ROW, isFormItemChecked(F("_colrow")));         // Bit 3 Col/Row addressing
      bitWrite(lSettings, P096_CONFIG_FLAG_COMPAT_P096, !isFormItemChecked(F("_compat")));        // Bit 4 Compat_P096 (inv)
      bitWrite(lSettings, P096_CONFIG_FLAG_BACK_FILL,   !isFormItemChecked(F("_backfill")));      // Bit 5 Back fill text (inv)

      set4BitToUL(lSettings, P096_CONFIG_FLAG_CMD_TRIGGER, getFormItemInt(F("_commandtrigger"))); // Bit 8..11 Command trigger
      set4BitToUL(lSettings, P096_CONFIG_FLAG_FONTSCALE,   getFormItemInt(F("_fontscale")));      // Bit 12..15 Font scale
      set4BitToUL(lSettings, P096_CONFIG_FLAG_MODE,        getFormItemInt(F("_mode")));           // Bit 16..19 Text print mode
      set4BitToUL(lSettings, P096_CONFIG_FLAG_COLORDEPTH,  getFormItemInt(F("_colorDepth")));     // Bit 20..23 Color depth
      set4BitToUL(lSettings, P096_CONFIG_FLAG_DISPLAYTYPE, getFormItemInt(F("_type")));           // Bit 24..27 Hardwaretype

      P096_CONFIG_FLAGS = lSettings;

      String   color   = webArg(F("_foregroundcolor"));
      uint16_t fgcolor = static_cast<uint16_t>(AdaGFXMonoRedGreyscaleColors::ADAGFXEPD_BLACK);             // Default to white when empty

      if (!color.isEmpty()) {
        fgcolor = AdaGFXparseColor(color, static_cast<AdaGFXColorDepth>(P096_CONFIG_FLAG_GET_COLORDEPTH)); // Reduce to rgb565
      }
      color = webArg(F("_backgroundcolor"));
      uint16_t bgcolor = AdaGFXparseColor(color, static_cast<AdaGFXColorDepth>(P096_CONFIG_FLAG_GET_COLORDEPTH));

      P096_CONFIG_COLORS = fgcolor | (bgcolor << 16); // Store as a single setting

      String strings[P096_Nlines];

      for (uint8_t varNr = 0; varNr < P096_Nlines; varNr++) {
        strings[varNr] = webArg(getPluginCustomArgName(varNr));
      }

      const String error = SaveCustomTaskSettings(event->TaskIndex, strings, P096_Nlines, 0);

      if (error.length() > 0) {
        addHtmlError(error);
      }
      # endif // if P096_USE_EXTENDED_SETTINGS

      success = true;
      break;
    }

    case PLUGIN_GET_DISPLAY_PARAMETERS:
    {
      # if P096_USE_EXTENDED_SETTINGS
      uint16_t x, y;
      EPD_type_toResolution(static_cast<EPD_type_e>(P096_CONFIG_FLAG_GET_DISPLAYTYPE), x, y);
      event->Par1 = x;                                              // X-resolution in pixels
      event->Par2 = y;                                              // Y-resolution in pixels
      event->Par4 = P096_CONFIG_FLAG_GET_COLORDEPTH;                // Color depth
      # else // if P096_USE_EXTENDED_SETTINGS
      event->Par1 = P096_CONFIG_WIDTH;                              // X-resolution in pixels
      event->Par2 = P096_CONFIG_HEIGHT;                             // Y-resolution in pixels
      event->Par4 = static_cast<int>(AdaGFXColorDepth::Monochrome); // Color depth
      # endif // if P096_USE_EXTENDED_SETTINGS
      event->Par3 = P096_CONFIG_ROTATION;                           // Rotation (0..3: 0, 90, 180, 270 degrees)

      success = true;
      break;
    }

    case PLUGIN_INIT:
    {
      if (Settings.InitSPI != 0) {
        initPluginTaskData(event->TaskIndex,
                           # if P096_USE_EXTENDED_SETTINGS
                           new (std::nothrow) P096_data_struct(static_cast<EPD_type_e>(P096_CONFIG_FLAG_GET_DISPLAYTYPE),
                                                               P096_CONFIG_ROTATION,
                                                               P096_CONFIG_FLAG_GET_FONTSCALE,
                                                               static_cast<AdaGFXTextPrintMode>(P096_CONFIG_FLAG_GET_MODE),
                                                               P096_CommandTrigger_toString(static_cast<P096_CommandTrigger>(
                                                                                              P096_CONFIG_FLAG_GET_CMD_TRIGGER)),
                                                               P096_CONFIG_GET_COLOR_FOREGROUND,
                                                               P096_CONFIG_GET_COLOR_BACKGROUND,
                                                               static_cast<AdaGFXColorDepth>(P096_CONFIG_FLAG_GET_COLORDEPTH),
                                                               bitRead(P096_CONFIG_FLAGS, P096_CONFIG_FLAG_BACK_FILL) == 0)
                           # else // if P096_USE_EXTENDED_SETTINGS
                           new (std::nothrow) P096_data_struct(static_cast<EPD_type_e>(P096_CONFIG_FLAG_GET_DISPLAYTYPE),
                                                               P096_CONFIG_WIDTH,
                                                               P096_CONFIG_HEIGHT,
                                                               P096_CONFIG_ROTATION,
                                                               P096_CONFIG_FLAG_GET_FONTSCALE,
                                                               AdaGFXTextPrintMode::ContinueToNextLine,
                                                               F("epd"))
                           # endif // if P096_USE_EXTENDED_SETTINGS
                           );
        P096_data_struct *P096_data = static_cast<P096_data_struct *>(getPluginTaskData(event->TaskIndex));

        if (nullptr != P096_data) {
          success = P096_data->plugin_init(event); // Start the display
        }
      } else {
        addLog(LOG_LEVEL_ERROR, F("EPD  : SPI not enabled, init cancelled."));
      }

      break;
    }

    case PLUGIN_EXIT:
    {
      P096_data_struct *P096_data = static_cast<P096_data_struct *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != P096_data) {
        success = P096_data->plugin_exit(event); // Stop the display
      }
      break;
    }

    case PLUGIN_READ:
    {
      P096_data_struct *P096_data = static_cast<P096_data_struct *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != P096_data) {
        success = P096_data->plugin_read(event); // Read operation, redisplay the configured content
      }
      break;
    }

    case PLUGIN_WRITE:
    {
      P096_data_struct *P096_data = static_cast<P096_data_struct *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != P096_data) {
        success = P096_data->plugin_write(event, string); // Write operation, handle commands
      }
      break;
    }
  }

  return success;
}

# ifndef P096_USE_ADA_GRAPHICS

// // Print some text
// // param [in] string : The text to display
// // param [in] X : The left position (X)
// // param [in] Y : The top position (Y)
// // param [in] textSize : The text size (default 1)
// // param [in] color : The fore color (default ILI9341_WHITE)
// // param [in] bkcolor : The background color (default ILI9341_BLACK)
// void Plugin_096_printText(const char *string, int X, int Y, unsigned int textSize, unsigned short color, unsigned short bkcolor)
// {
//   eInkScreen->clearBuffer();
//   eInkScreen->clearDisplay();
//   eInkScreen->setCursor(X, Y);
//   eInkScreen->setTextColor(color, bkcolor);
//   eInkScreen->setTextSize(textSize);
//   String fixString = string;

//   Plugin_096_FixText(fixString);
//   eInkScreen->println(fixString);
//   eInkScreen->display();
// }

# endif // ifndef P096_USE_ADA_GRAPHICS

// Parse color string to color
// param [in] colorString : The color string (white, red, ...)
// return : color (default EPD_WHITE)
unsigned short Plugin_096_ParseColor(const String& colorString)
{
  // copy to local var and ensure lowercase
  // this optimise the next equlaity checks
  String s = colorString;

  s.toLowerCase();

  if (equals(s, F("black"))) {
    return EPD_BLACK;
  }

  if (equals(s, F("white"))) {
    return EPD_WHITE;
  }

  if (equals(s, F("inverse"))) {
    return EPD_INVERSE;
  }

  if (equals(s, F("red"))) {
    return EPD_RED;
  }

  if (equals(s, F("dark"))) {
    return EPD_DARK;
  }

  if (equals(s, F("light"))) {
    return EPD_LIGHT;
  }
  return EPD_WHITE;
}

# ifndef P096_USE_ADA_GRAPHICS

// Fix text with handling special characters (degrees and main monetary symbols)
// This is specific case for current AdafruitGfx standard fontused for eink screen
// param [in/out] s : The string to fix
void Plugin_096_FixText(String& s)
{
  const char degree[3]      = { 0xc2, 0xb0, 0 }; // Unicode degree symbol
  const char degree_eink[2] = { 0xf7, 0 };       // eink degree symbol

  s.replace(degree,     degree_eink);
  s.replace(F("{D}"),   degree_eink);
  s.replace(F("&deg;"), degree_eink);

  const char euro[4]      = { 0xe2, 0x82, 0xac, 0 }; // Unicode euro symbol
  const char euro_eink[2] = { 0xED, 0 };             // eink degree symbol

  s.replace(euro,        euro_eink);
  s.replace(F("{E}"),    euro_eink);
  s.replace(F("&euro;"), euro_eink);

  const char pound[3]      = { 0xc2, 0xa3, 0 }; // Unicode pound symbol
  const char pound_eink[2] = { 0x9C, 0 };       // eink pound symbol

  s.replace(pound,        pound_eink);
  s.replace(F("{P}"),     pound_eink);
  s.replace(F("&pound;"), pound_eink);

  const char yen[3]      = { 0xc2, 0xa5, 0 }; // Unicode yen symbol
  const char yen_eink[2] = { 0x9D, 0 };       // eink yen symbol

  s.replace(yen,        yen_eink);
  s.replace(F("{Y}"),   yen_eink);
  s.replace(F("&yen;"), yen_eink);

  const char cent[3]      = { 0xc2, 0xa2, 0 }; // Unicode yen symbol
  const char cent_eink[2] = { 0x9B, 0 };       // eink cent symbol

  s.replace(cent,        cent_eink);
  s.replace(F("{c}"),    cent_eink);
  s.replace(F("&cent;"), cent_eink);
}

// Split a string by delimiter
// param [in] s : The input string
// param [in] c : The delimiter
// param [out] op : The resulting string array
// param [in] limit : The maximum strings to find
// return : The string count
int Plugin_096_StringSplit(const String& s, char c, String op[], int limit)
{
  int    count = 0;
  char  *pch;
  String d = String(c);

  pch = strtok((char *)(s.c_str()), d.c_str());

  while (pch != NULL && count < limit)
  {
    op[count] = String(pch);
    count++;
    pch = strtok(NULL, ",");
  }
  return count;
}

# endif // ifndef P096_USE_ADA_GRAPHICS

#endif  // USES_P096
