/*
  xlgt_05_sonoff_l1.ino - Sonoff L1 led support for Tasmota

  Copyright (C) 2020  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/>.
*/

#ifdef USE_LIGHT
#ifdef USE_SONOFF_L1
/*********************************************************************************************\
 * Sonoff L1
\*********************************************************************************************/

#define XLGT_05                           5

#define SONOFF_L1_BUFFER_SIZE           140

#define SONOFF_L1_MODE_COLORFUL           1  // [Color key] Colorful (static color)
#define SONOFF_L1_MODE_COLORFUL_GRADIENT  2  // [SMOOTH] Colorful Gradient
#define SONOFF_L1_MODE_COLORFUL_BREATH    3  // [FADE] Colorful Breath
#define SONOFF_L1_MODE_DIY_GRADIENT       4  // DIY Gradient (fade in and out) [Speed 1- 100, color]
#define SONOFF_L1_MODE_DIY_PULSE          5  // DIY Pulse  (faster fade in and out) [Speed 1- 100, color]
#define SONOFF_L1_MODE_DIY_BREATH         6  // DIY Breath (toggle on/off) [Speed 1- 100, color]
#define SONOFF_L1_MODE_DIY_STROBE         7  // DIY Strobe (faster toggle on/off) [Speed 1- 100, color]
#define SONOFF_L1_MODE_RGB_GRADIENT       8  // RGB Gradient
#define SONOFF_L1_MODE_RGB_PULSE          9  // [STROBE] RGB Pulse
#define SONOFF_L1_MODE_RGB_BREATH        10  // RGB Breath
#define SONOFF_L1_MODE_RGB_STROBE        11  // [FLASH] RGB strobe
#define SONOFF_L1_MODE_SYNC_TO_MUSIC     12  // Sync to music [Speed 1- 100, sensitivity 1 - 10]

struct SNFL1 {
  uint32_t unlock = 0;
  bool receive_ready = true;
} Snfl1;

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

void SnfL1Send(const char *buffer)
{
//  AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Send %s"), buffer);

  Serial.print(buffer);
  Serial.write(0x1B);
  Serial.flush();
}

void SnfL1SerialSendOk(void)
{
  char buffer[16];
  snprintf_P(buffer, sizeof(buffer), PSTR("AT+SEND=ok"));

  SnfL1Send(buffer);
}

bool SnfL1SerialInput(void)
{
  if (serial_in_byte != 0x1B) {
    if (serial_in_byte_counter >= 140) {
      serial_in_byte_counter = 0;
    }
    if (serial_in_byte_counter || (!serial_in_byte_counter && ('A' == serial_in_byte))) {  // A from AT
      serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
    }
  } else {
    serial_in_buffer[serial_in_byte_counter++] = 0x00;

    // AT+RESULT="sequence":"1554682835320"
    // AT+UPDATE="sequence":"34906","switch":"on","light_type":1,"colorR":0,"colorG":16,"colorB":0,"bright":6,"mode":1
    // AT+UPDATE="switch":"on","light_type":1,"colorR":255,"colorG":0,"colorB":0,"bright":6,"mode":1,"speed":100,"sensitive":10
//    AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd %s"), serial_in_buffer);

    if (!strncmp(serial_in_buffer +3, "RESULT", 6)) {
      Snfl1.receive_ready = true;
    }
    else if (!strncmp(serial_in_buffer +3, "UPDATE", 6)) {
      char cmnd_dimmer[20];
      char cmnd_color[20];
      char *end_str;
      char *string = serial_in_buffer +10;
      char *token = strtok_r(string, ",", &end_str);

      bool color_updated[3] = { false, false, false };
      uint8_t current_color[3];
      memcpy(current_color, Settings.light_color, 3);

      bool switch_state = false;
      bool is_power_change = false;
      bool is_color_change = false;
      bool is_brightness_change = false;

      while (token != nullptr) {
        char* end_token;
        char* token2 = strtok_r(token, ":", &end_token);
        char* token3 = strtok_r(nullptr, ":", &end_token);

        if (!strncmp(token2, "\"sequence\"", 10)) {

//          AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd sequence %s"), token3);

          token = nullptr;
        }

        else if (!strncmp(token2, "\"switch\"", 8)) {
          switch_state = !strncmp(token3, "\"on\"", 4) ? true : false;

//          AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd switch %d (%d)"), switch_state, Light.power);

          is_power_change = (switch_state != Light.power);
        }

        else if (!strncmp(token2, "\"color", 6)) {
          char color_channel_name = token2[6];
          int color_index;
          switch(color_channel_name)
          {
            case 'R': color_index = 0;
              break;
            case 'G': color_index = 1;
              break;
            case 'B': color_index = 2;
              break;
          }
          int color_value = atoi(token3);
          current_color[color_index] = color_value;
          color_updated[color_index] = true;

          bool all_color_channels_updated = color_updated[0] && color_updated[1] && color_updated[2];
          if (all_color_channels_updated) {

//            AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd color R%d G%d B%d (R%d G%d B%d)"),
//              current_color[0], current_color[1], current_color[2],
//              Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]);

            is_color_change = (Light.power && (memcmp(current_color, Settings.light_color, 3) != 0));
          }
          snprintf_P(cmnd_color, sizeof(cmnd_color), PSTR(D_CMND_COLOR "2 %02x%02x%02x"), current_color[0], current_color[1], current_color[2]);
        }

        else if (!strncmp(token2, "\"bright\"", 8)) {
          uint8_t dimmer = atoi(token3);

//          AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd dimmer %d (%d)"), dimmer, Settings.light_dimmer);

          is_brightness_change = (Light.power && (dimmer > 0) && (dimmer != Settings.light_dimmer));
          snprintf_P(cmnd_dimmer, sizeof(cmnd_dimmer), PSTR(D_CMND_DIMMER " %d"), dimmer);
        }

        token = strtok_r(nullptr, ",", &end_str);
      }

      if (is_power_change) {
        if (Settings.light_scheme > 0) {
          if (!switch_state) {  // If power off RC button pressed stop schemes
            char cmnd_scheme[20];
            snprintf_P(cmnd_scheme, sizeof(cmnd_scheme), PSTR(D_CMND_SCHEME " 0"));
            ExecuteCommand(cmnd_scheme, SRC_SWITCH);
          }
        } else {
          ExecuteCommandPower(1, switch_state, SRC_SWITCH);
        }
      }
      else if (is_brightness_change) {
        ExecuteCommand(cmnd_dimmer, SRC_SWITCH);
      }
      else if (Light.power && is_color_change) {
        if (0 == Settings.light_scheme) {  // Fix spurious color receptions when scheme > 0
          if (Settings.light_fade) {  // Disable fade as RC button colors overrule and are immediate supressing ghost colors
            char cmnd_fade[20];
            snprintf_P(cmnd_fade, sizeof(cmnd_fade), PSTR(D_CMND_FADE " 0"));
            ExecuteCommand(cmnd_fade, SRC_SWITCH);
          }
          ExecuteCommand(cmnd_color, SRC_SWITCH);
        }
      }
    }

    SnfL1SerialSendOk();

    return true;
  }
  serial_in_byte = 0;
  return false;
}

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

bool SnfL1SetChannels(void)
{
  if (Snfl1.receive_ready || TimeReached(Snfl1.unlock)) {

    uint8_t *scale_col = (uint8_t*)XdrvMailbox.topic;

    char buffer[140];
    snprintf_P(buffer, sizeof(buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"),
      LocalTime(), millis()%1000,
      Light.power ? "on" : "off",
      scale_col[0], scale_col[1], scale_col[2],
      light_state.getDimmer(),
      SONOFF_L1_MODE_COLORFUL);

    SnfL1Send(buffer);

    Snfl1.unlock = millis() + 500;  // Allow time for the RC
    Snfl1.receive_ready = false;
  }
  return true;
}

void SnfL1ModuleSelected(void)
{
  if (SONOFF_L1 == my_module_type) {
    if (PinUsed(GPIO_RXD) && PinUsed(GPIO_TXD)) {
      SetSerial(19200, TS_SERIAL_8N1);

      light_type = LT_RGB;
      light_flg = XLGT_05;
      AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: Sonoff L1 Found"));
    }
  }
}

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

bool Xlgt05(uint8_t function)
{
  bool result = false;

  switch (function) {
    case FUNC_SERIAL:
      result = SnfL1SerialInput();
      break;
    case FUNC_SET_CHANNELS:
      result = SnfL1SetChannels();
      break;
    case FUNC_MODULE_INIT:
      SnfL1ModuleSelected();
      break;
  }
  return result;
}

#endif  // USE_SONOFF_L1
#endif  // USE_LIGHT