/*
  xlgt_02_my92x1.ino - led 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/>.
*/

#ifdef USE_LIGHT
#ifdef USE_MY92X1
/*********************************************************************************************\
 * Sonoff B1 and AiLight inspired by OpenLight https://github.com/icamgo/noduino-sdk
\*********************************************************************************************/

#define XLGT_02             2

struct MY92X1 {
  int8_t pdi_pin = 0;
  int8_t pdcki_pin = 0;
  uint8_t model = 0;
} My92x1;

#ifdef ESP32
#include "rom/ets_sys.h"
#ifndef os_delay_us
#define os_delay_us ets_delay_us
#endif //os_delay_us
#else  // ESP8266
extern "C" {
  void os_delay_us(unsigned int);
}
#endif  // ESP8266

void LightDiPulse(uint8_t times)
{
  for (uint32_t i = 0; i < times; i++) {
    digitalWrite(My92x1.pdi_pin, HIGH);
    digitalWrite(My92x1.pdi_pin, LOW);
  }
}

void LightDckiPulse(uint8_t times)
{
  for (uint32_t i = 0; i < times; i++) {
    digitalWrite(My92x1.pdcki_pin, HIGH);
    digitalWrite(My92x1.pdcki_pin, LOW);
  }
}

void LightMy92x1Write(uint8_t data)
{
  for (uint32_t i = 0; i < 4; i++) {     // Send 8bit Data
    digitalWrite(My92x1.pdcki_pin, LOW);
    digitalWrite(My92x1.pdi_pin, (data & 0x80));
    digitalWrite(My92x1.pdcki_pin, HIGH);
    data = data << 1;
    digitalWrite(My92x1.pdi_pin, (data & 0x80));
    digitalWrite(My92x1.pdcki_pin, LOW);
    digitalWrite(My92x1.pdi_pin, LOW);
    data = data << 1;
  }
}

void LightMy92x1Init(void)
{
  uint8_t chips[3] = { 1, 2, 2 };

  LightDckiPulse(chips[My92x1.model] * 32);  // Clear all duty register
  os_delay_us(12);                      // TStop > 12us.
  // Send 12 DI pulse, after 6 pulse's falling edge store duty data, and 12
  // pulse's rising edge convert to command mode.
  LightDiPulse(12);
  os_delay_us(12);                      // Delay >12us, begin send CMD data
  for (uint32_t n = 0; n < chips[My92x1.model]; n++) {  // Send CMD data
    LightMy92x1Write(0x18);             // ONE_SHOT_DISABLE, REACTION_FAST, BIT_WIDTH_8, FREQUENCY_DIVIDE_1, SCATTER_APDM
  }
  os_delay_us(12);                      // TStart > 12us. Delay 12 us.
  // Send 16 DI pulse, at 14 pulse's falling edge store CMD data, and
  // at 16 pulse's falling edge convert to duty mode.
  LightDiPulse(16);
  os_delay_us(12);                      // TStop > 12us.
}

void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c)
{
  uint8_t channels[3] = { 4, 6, 6 };

  uint8_t duty[3][6] = {{ duty_r, duty_g, duty_b, duty_w, 0, 0 },             // Definition for RGBW channels
                        { duty_w, duty_c, 0, duty_g, duty_r, duty_b },        // Definition for RGBWC channels
                        { duty_r, duty_g, duty_b, duty_w, duty_w, duty_w }};  // Definition for RGBWWW channels as used in Lohas which uses up to 3 CW channels

  os_delay_us(12);                      // TStop > 12us.
  for (uint32_t channel = 0; channel < channels[My92x1.model]; channel++) {
    LightMy92x1Write(duty[My92x1.model][channel]);  // Send 8bit Data
  }
  os_delay_us(12);                      // TStart > 12us. Ready for send DI pulse.
  LightDiPulse(8);                      // Send 8 DI pulse. After 8 pulse falling edge, store old data.
  os_delay_us(12);                      // TStop > 12us.
}

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

bool My92x1SetChannels(void)
{
  uint8_t *cur_col = (uint8_t*)XdrvMailbox.data;

  LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]);

  return true;
}

void My92x1ModuleSelected(void)
{
  if (PinUsed(GPIO_DCKI) && PinUsed(GPIO_DI)) {
    My92x1.pdi_pin = Pin(GPIO_DI);
    My92x1.pdcki_pin = Pin(GPIO_DCKI);

    pinMode(My92x1.pdi_pin, OUTPUT);
    pinMode(My92x1.pdcki_pin, OUTPUT);
    digitalWrite(My92x1.pdi_pin, LOW);
    digitalWrite(My92x1.pdcki_pin, LOW);

    My92x1.model = 2;
    TasmotaGlobal.light_type = LT_RGBW;                 // RGBW (2 chips) as used in Lohas
#ifdef ESP8266
    if (AILIGHT == TasmotaGlobal.module_type) {         // RGBW (1 chip) as used in Ailight
      My92x1.model = 0;
//      TasmotaGlobal.light_type = LT_RGBW;
    }
    else if (SONOFF_B1 == TasmotaGlobal.module_type) {  // RGBWC (2 chips) as used in Sonoff B1
      My92x1.model = 1;
      TasmotaGlobal.light_type = LT_RGBWC;
    }
#endif  // ESP8266

    LightMy92x1Init();

    TasmotaGlobal.light_driver = XLGT_02;
    AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: MY29x1 Found"));
  }
}

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

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

  switch (function) {
    case FUNC_SET_CHANNELS:
      result = My92x1SetChannels();
      break;
    case FUNC_MODULE_INIT:
      My92x1ModuleSelected();
      break;
  }
  return result;
}

#endif  // USE_MY92X1
#endif  // USE_LIGHT