/*
  xlgt_07_lsc_mcsl.ino - GPE Multi color smart light support for Tasmota

  Copyright (C) 2021  Theo Arends

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#define USE_LSC_MCSL_GUI

#ifdef USE_LIGHT
#ifdef USE_LSC_MCSL
/*********************************************************************************************\
 * Golden Power Electronics Integrated Switching Power Supply with Built-in Controller
 * GP-SW084-052 uses an ESP8285 (TYWE2S) and submicrocontroller controlling two wire RGB leds
 * https://www.gp-electronic.com/product/integrated-switching-power-supply-with-built-in-controller/eugs/european-vertical-6w8-function.html
 *
 * Template:
 * {"NAME":"LSC MC Lights","GPIO":[0,0,0,0,544,32,0,0,3840,0,3872,0,0,0],"FLAG":0,"BASE":18}
 *
 * Webbutton:
 * {"WebButton1":"On / Off",
 *  "WebButton2":"Bright",
 *  "WebButton3":"Slow",
 *  "WebButton4":"Star",
 *  "WebButton5":"Flower",
 *  "WebButton6":"Marquee",
 *  "WebButton7":"Fireworks",
 *  "WebButton8":"Meteor",
 *  "WebButton9":"Stream"}
 *
 * NL: Action LSC Multi color smart lights
 *
 * Button usage:
 * Single press = Power On / Off
 * Double press = Cycle Colors
 * Triple press = Cycle Effects
 *
 * HSBColor    Color
 * ----------  -----------
 *   1 .. 45   R   (Red)
 *  46 .. 90   RG  (Yellow)
 *  91 .. 135  G   (Green)
 * 136 .. 179  GB  (Light Blue)
 * 180 .. 224  B   (Blue)
 * 225 .. 269  RB  (Purple)
 * 270 .. 314  RGB (White)
 * 315 .. 360      (Alternating)
 *
 * Dimmer      Effect
 * ----------  ---------------------------
 *   0 .. 12   Bright (Steady On)
 *  13 .. 24   Gradually (Slow Fade)
 *  25 .. 37   Star (Sequential)
 *  38 .. 49   Flower (In Waves)
 *  50 .. 62   Marquee (Chasing / Flash)
 *  63 .. 74   Fireworks (Twinkle / Flash)
 *  75 .. 87   Meteor
 *  88 .. 100  Stream
 *
 * GPIO04 = Green led
 * GPIO05 = Button
 * GPIO12 = Data to submicrocontroller
 * GPIO14 = Reset to submicrocontroller (26ms active high)
\*********************************************************************************************/

#define XLGT_07               7

#define LSCMC_GPIO_DATA       12
#define LSCMC_GPIO_RESET      14

#define LSCMC_BIT_TIME        600   // us

const uint16_t kLscMcData[] PROGMEM = {
  //R     RG      G       GB      B       RB      RGB     RGBa
  0x0F4D, 0x0745, 0x08B5, 0x0340, 0x0CB0, 0x04B8, 0x0B48, 0x0143,    // Bright (Steady on)
  0x8FED, 0x87E5, 0x8815, 0x83E0, 0x8C10, 0x8418, 0x8BE8, 0x81E3,    // Gradually (Slow Fade)
  0x70ED, 0x78E5, 0x7715, 0x7CE0, 0x7510, 0x7B18, 0x74E8, 0x7EE3,    // Star (Sequential)
  0xCF92, 0xC79A, 0xC86A, 0xC39F, 0xCC6F, 0xC467, 0xCB97, 0xC19C,    // Flower (In Waves)
  0x3093, 0x389A, 0x376A, 0x3C9F, 0x336F, 0x3B67, 0x3497, 0x3E9C,    // Marquee (Chasing / Flash)
  0xB02D, 0xB825, 0xB7D5, 0xBC20, 0xB3D0, 0xBBD8, 0xB428, 0xBE23,    // Fireworks (Twinkle / Flash)
  0x4F2D, 0x4725, 0x48D5, 0x4320, 0x4CD0, 0x44D8, 0x4B28, 0x4123,    // Meteor
  0xEFAD, 0xE7A5, 0xE855, 0xE3A1, 0xEC50, 0xE458, 0xEBA8, 0xE1A3     // Stream
};

#include <Ticker.h>
Ticker LscMcStartDelay;

struct LSCMC {
  uint32_t last_send;
  uint8_t function;
  uint8_t color;
  uint8_t dimmer;
  uint8_t power;
  uint8_t scheme_offset;
  uint8_t pin;
} Lscmc;

/*********************************************************************************************/

void LscMcSend(void) {
  Lscmc.function &= 0x7;
  Lscmc.color &= 0x7;
  uint16_t fc = pgm_read_word(kLscMcData + (8 * Lscmc.function) + Lscmc.color);

  uint32_t data;
  if (Lscmc.power) {
    data = 0xC2FF0000 | fc;
  } else {
    data = Lscmc.last_send & 0xFF00FFFF;
    data ^= 0x0000FF00;
  }

  if (data == Lscmc.last_send) { return; }

  AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("LSC: Function %d, Color %d, State %d, Send %08X"),
    Lscmc.function, Lscmc.color, Lscmc.power, data);

  Lscmc.last_send = data;

  digitalWrite(Lscmc.pin, LOW);
  delayMicroseconds(LSCMC_BIT_TIME * 15);
  digitalWrite(Lscmc.pin, HIGH);
  delayMicroseconds(LSCMC_BIT_TIME * 7 + (LSCMC_BIT_TIME / 2));
  digitalWrite(Lscmc.pin, LOW);
  delayMicroseconds(LSCMC_BIT_TIME);
  digitalWrite(Lscmc.pin, HIGH);
  bool last_bit = 0;
  bool bit = 0;
  uint32_t bit_mask = 0x80000000;
  for (uint32_t i = 0; i < 32; i++) {
    bit = (data & bit_mask);
    if (bit != last_bit) {
      delayMicroseconds(LSCMC_BIT_TIME * 3);  // Switch bit state
    } else {
      delayMicroseconds(LSCMC_BIT_TIME);
    }
    digitalWrite(Lscmc.pin, LOW);
    delayMicroseconds(LSCMC_BIT_TIME);
    digitalWrite(Lscmc.pin, HIGH);
    last_bit = bit;
    bit_mask >>= 1;
  }
}

/*********************************************************************************************/

bool LscMcSetChannelsFromFunc(void) {
  // Use power for Off / On
  uint8_t power = Light.power;
  bool power_changed = (Lscmc.power != power);
  Lscmc.power = power;

  // Use dimmer for function
  uint8_t dimmer = light_state.getDimmer();
/*
  dimmer = changeUIntScale(dimmer, 0, 100, 0, 3);
  dimmer = changeUIntScale(dimmer, 0, 3, 0, 255);
  bool dimmer_changed = (Lscmc.dimmer != dimmer);
  Lscmc.dimmer= dimmer;
*/
  uint8_t function = changeUIntScale(dimmer, 0, 100, 0, 7);
  bool function_changed = (Lscmc.function != function);
  Lscmc.function = function;

  uint16_t hue;
  uint8_t sat;
  LightGetHSB(&hue, &sat, nullptr);
/*
  // Use saturation for function (won't work as if sat = 0 this function is not called)
  uint8_t function = changeUIntScale(sat, 0, 255, 0, 7);
  bool function_changed = (Lscmc.function != function);
  Lscmc.function = function;
*/
  // Use hue for color
  uint8_t color = changeUIntScale(hue, 1, 359, 0, 7);
  bool color_changed = (Lscmc.color != color);
  Lscmc.color = color;

//  AddLog(LOG_LEVEL_DEBUG, PSTR("LSC: Power %d, Hue %d = Color %d, Dimmer %d = Function %d"),
//    Lscmc.power, hue, Lscmc.color, dimmer, Lscmc.function);

  if (!power_changed && !function_changed && !color_changed) { return true; }

  static bool first_call = true;
  if (first_call) {
    LscMcStartDelay.once_ms(900, LscMcSend);  // Allow startup time for microcontroller
    first_call = false;
  } else {
    LscMcSend();
  }

  return true;
}

bool LscMcMultiButtonPressed(void) {
  if (XdrvMailbox.index != 0) { return false; }  // button_index

  char command[20];

  uint32_t press_counter = XdrvMailbox.payload;
  if (2 == press_counter) {         // Color rotate
    uint32_t color = Lscmc.color +1;
    if (color > 7) { color = 0; }
    snprintf_P(command, sizeof(command), PSTR(D_CMND_HSBCOLOR " %d"), (color * (360 / 8)) + ((360 / 8) / 2));
    ExecuteCommand(command, SRC_BUTTON);
  } else if (3 == press_counter) {  // Function rotate
    uint32_t function = Lscmc.function +1;
    if (function > 7) { function = 0; }
    snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER " %d"), (function * (100 / 8)) + ((100 / 8) / 2));
    ExecuteCommand(command, SRC_BUTTON);
  }
  return true;
}

void LscMcModuleSelected(void) {
  if (!ValidTemplate(PSTR("LSC MC Lights"))) { return; }
  if (!PinUsed(GPIO_OUTPUT_HI) || !PinUsed(GPIO_OUTPUT_LO)) { return; }

  Lscmc.pin = Pin(GPIO_OUTPUT_HI);

  uint32_t pin_reset = Pin(GPIO_OUTPUT_LO);
  digitalWrite(pin_reset, HIGH);
  delay(26);
  digitalWrite(pin_reset, LOW);

  Settings->flag.button_single = 0;           // SetOption13 - We need multi press detection
  Settings->flag3.slider_dimmer_stay_on = 1;  // SetOption77 - We need dimmer to keep power on at 0

  Lscmc.last_send = 0xC2FFEBA8;

  TasmotaGlobal.light_type = LT_RGB;
  TasmotaGlobal.light_driver = XLGT_07;
//  AddLog(LOG_LEVEL_DEBUG, PSTR("LGT: LSC Multi Color Found"));
}

#ifdef USE_WEBSERVER
#ifdef USE_LSC_MCSL_GUI

void LscMcAddFuctionButtons(void) {
  uint32_t rows = 1;
  uint32_t cols = 8;
  for (uint32_t i = 0; i < 8; i++) {
    if (strlen(GetWebButton(i +1))) {
      rows <<= 1;
      cols >>= 1;
      break;
    }
  }
  WSContentSend_P(HTTP_TABLE100);
  WSContentSend_P(PSTR("<tr>"));
  char number[4];
  uint32_t idx = 0;
  for (uint32_t i = 0; i < rows; i++) {
    if (idx > 0) { WSContentSend_P(PSTR("</tr><tr>")); }
    for (uint32_t j = 0; j < cols; j++) {
      idx++;
      WSContentSend_P(PSTR("<td style='width:%d%%'><button onclick='la(\"&lsc=%d\");'>%s</button></td>"),  // &lsc is related to WebGetArg("lsc", tmp, sizeof(tmp));
        100 / cols,
        idx -1,
        (strlen(GetWebButton(idx))) ? HtmlEscape(GetWebButton(idx)).c_str() : itoa(idx, number, 10));
    }
  }
  WSContentSend_P(PSTR("</tr></table>"));
}

void LscMcWebGetArg(void) {
  char tmp[8];                               // WebGetArg numbers only
  WebGetArg(PSTR("lsc"), tmp, sizeof(tmp));  // 0 - 7 functions
  if (strlen(tmp)) {
    uint32_t function = atoi(tmp);
    char command[20];
    snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER " %d"), (function * (100 / 8)) + ((100 / 8) / 2));
    ExecuteWebCommand(command);
  }
}

#endif  // USE_LSC_MCSL_GUI
#endif  // USE_WEBSERVER


/*********************************************************************************************\
 * Interface
\*********************************************************************************************/

bool Xlgt07(uint32_t function)
{
  bool result = false;

  switch (function) {
    case FUNC_SET_CHANNELS:
      result = LscMcSetChannelsFromFunc();
      break;
    case FUNC_BUTTON_MULTI_PRESSED:
      result = LscMcMultiButtonPressed();
      break;
#ifdef USE_WEBSERVER
#ifdef USE_LSC_MCSL_GUI
    case FUNC_WEB_ADD_MAIN_BUTTON:
      LscMcAddFuctionButtons();
      break;
    case FUNC_WEB_GET_ARG:
      LscMcWebGetArg();
      break;
#endif  // USE_LSC_MCSL_GUI
#endif  // USE_WEBSERVER
    case FUNC_MODULE_INIT:
      LscMcModuleSelected();
      break;
  }
  return result;
}

#endif  // USE_LSC_MCSL
#endif  // USE_LIGHT