// SPDX-License-Identifier: MIT
/*
 * Copyright (C) Mathieu Carbou
 */
#include <MycilaDimmerDFRobot.h>

// logging
#include <esp32-hal-log.h>

#define TAG "DFRobot"

bool Mycila::DFRobotDimmer::begin() {
  if (_enabled)
    return true;

  uint8_t resolution = getResolution();
  if (!resolution) {
    ESP_LOGE(TAG, "SKU not set!");
    return false;
  }

  // sanity checks
  if (_sku == SKU::DFR1071_GP8211S) {
    if (_channel > 0) {
      ESP_LOGW(TAG, "DFRobot DFR1071 (GP8211S) has only one channel: switching to channel 0");
      _channel = 0;
    }
  }

  if (_channel > 2) {
    ESP_LOGE(TAG, "Invalid channel %d", _channel);
    return false;
  }

  // discovery
  bool found = false;
  if (_deviceAddress) {
    ESP_LOGI(TAG, "Searching for DFRobot @ 0x%02x...", _deviceAddress);
    for (int i = 0; i < 3; i++) {
      uint8_t err = _test(_deviceAddress);
      if (err) {
        ESP_LOGD(TAG, "DFRobot @ 0x%02x: TwoWire communication error: %d", _deviceAddress, err);
        delay(10);
      } else {
        found = true;
        break;
      }
    }

  } else {
    ESP_LOGI(TAG, "Searching for DFRobot @ 0x58 up to 0x5F...");
    for (uint8_t addr = 0x58; !found && addr <= 0x5F; addr++) {
      if (_test(addr) == ESP_OK) {
        _deviceAddress = addr;
        found = true;
        break;
      }
    }
  }

  if (found) {
    ESP_LOGI(TAG, "Found DFRobot @ 0x%02x and channel %d", _deviceAddress, _channel);
  } else if (_deviceAddress) {
    ESP_LOGW(TAG, "DFRobot @ 0x%02x: Unable to communicate with device", _deviceAddress);
  } else {
    _deviceAddress = 0x58;
    ESP_LOGW(TAG, "DFRobot not found! Using default address 0x58");
  }

  // set output
  uint8_t err = _sendOutput(_deviceAddress, _output);
  if (err) {
    ESP_LOGE(TAG, "Disable DFRobot @ 0x%02x: Unable to set output voltage: TwoWire communication error: %d", _deviceAddress, err);
    return false;
  }

  _enabled = true;

  // restart with last saved value
  setDutyCycle(_dutyCycle);

  return true;
}

void Mycila::DFRobotDimmer::end() {
  if (!_enabled)
    return;
  _enabled = false;
  _online = false;
  ESP_LOGI(TAG, "Disable DFRobot @ 0x%02x", _deviceAddress);
  _apply();
}

uint8_t Mycila::DFRobotDimmer::_sendDutyCycle(uint8_t address, uint16_t duty) {
  duty = duty << (16 - getResolution());
  switch (_channel) {
    case 0: {
      uint8_t buffer[2] = {uint8_t(duty & 0xff), uint8_t(duty >> 8)};
      return _send(address, 0x02, buffer, 2) == 0;
    }
    case 1: {
      uint8_t buffer[2] = {uint8_t(duty & 0xff), uint8_t(duty >> 8)};
      return _send(address, 0x04, buffer, 2) == 0;
    }
    case 2: {
      uint8_t buffer[4] = {uint8_t(duty & 0xff), uint8_t(duty >> 8), uint8_t(duty & 0xff), uint8_t(duty >> 8)};
      return _send(address, 0x02, buffer, 4) == 0;
    }
    default:
      assert(false); // fail
      return ESP_FAIL;
  }
}

uint8_t Mycila::DFRobotDimmer::_sendOutput(uint8_t address, Output output) {
  switch (output) {
    case Output::RANGE_0_5V: {
      ESP_LOGI(TAG, "Set output range to 0-5V");
      uint8_t data = 0x00;
      return _send(address, 0x01, &data, 1);
    }
    case Output::RANGE_0_10V: {
      ESP_LOGI(TAG, "Set output range to 0-10V");
      uint8_t data = 0x11;
      return _send(address, 0x01, &data, 1);
    }
    default:
      assert(false); // fail
      return ESP_FAIL;
  }
}

uint8_t Mycila::DFRobotDimmer::_send(uint8_t address, uint8_t reg, uint8_t* buffer, size_t size) {
  _wire->beginTransmission(address);
  _wire->write(reg);
  for (uint16_t i = 0; i < size; i++) {
    _wire->write(buffer[i]);
  }
  return _wire->endTransmission();
}

uint8_t Mycila::DFRobotDimmer::_test(uint8_t address) {
  // return _sendDutyCycle(address, 0);
  _wire->beginTransmission(address);
  delayMicroseconds(100);
  return _wire->endTransmission();
}
