#include "MultiColorLight.h"

MultiColorLight::MultiColorLight()
    : rgbLeds(ledAmount, ledPin) {

      };

void MultiColorLight::begin(void) {
  if (!PowerManager::waitForCurrentAllowance(
          PowerParameters::PowerConsumers::LED_RGB_TOP_LEFT,
          PowerParameters::CurrentConsumptions::CURRENT_LED_RGB_BASE,
          MULTI_COLOR_LIGHT_MAX_EXECUTION_DELAY_MS, NULL) &&
      PowerManager::waitForCurrentAllowance(
          PowerParameters::PowerConsumers::LED_RGB_TOP_RIGHT,
          PowerParameters::CurrentConsumptions::CURRENT_LED_RGB_BASE,
          MULTI_COLOR_LIGHT_MAX_EXECUTION_DELAY_MS, NULL) &&
      PowerManager::waitForCurrentAllowance(
          PowerParameters::PowerConsumers::LED_RGB_BOTTOM,
          PowerParameters::CurrentConsumptions::CURRENT_LED_RGB_BASE,
          MULTI_COLOR_LIGHT_MAX_EXECUTION_DELAY_MS, NULL)) {
    ESP_LOGE(TAG, "Could not get power for MultiColorLight");
    if(Serial){
      Serial.println("Could not get power for MultiColorLight");
    }
  }
  rgbLeds.begin();
  this->turnOffLed();
};

void MultiColorLight::setLed(uint8_t index, uint32_t color) {
  if (index > ledAmount - 1) {
    // TODO: logging
  }
  uint32_t normalizedColor = normalizeColor(color);
  float totalConsumption = modelCurrentConsumption(normalizedColor);
  switch (index) {
  case 0:
    if (!PowerManager::waitForCurrentAllowance(
            PowerParameters::PowerConsumers::LED_RGB_TOP_RIGHT,
            totalConsumption, MULTI_COLOR_LIGHT_MAX_EXECUTION_DELAY_MS, NULL)) {
      ESP_LOGW(TAG,
               "Power to set LED RGB TOP RIGHT to color 0x%.8X not granted in "
               "time. Skipping.",
               normalizedColor);
      return;
    }
    break;
  case 1:
    if (!PowerManager::waitForCurrentAllowance(
            PowerParameters::PowerConsumers::LED_RGB_TOP_LEFT, totalConsumption,
            MULTI_COLOR_LIGHT_MAX_EXECUTION_DELAY_MS, NULL)) {
      ESP_LOGW(TAG,
               "Power to set LED RGB TOP LEFT to color 0x%.8X not granted in "
               "time. Skipping.",
               normalizedColor);
      return;
    }
    break;
  case 2:
    if (!PowerManager::waitForCurrentAllowance(
            PowerParameters::PowerConsumers::LED_RGB_BOTTOM, totalConsumption,
            MULTI_COLOR_LIGHT_MAX_EXECUTION_DELAY_MS, NULL)) {
      ESP_LOGW(TAG,
               "Power to set LED RGB BOTTOM to color 0x%.8X not granted in "
               "time. Skipping.",
               normalizedColor);
      return;
    }
    break;
  }
  rgbLeds.setPixelColor(index, normalizedColor);
  rgbLeds.show();
};

void MultiColorLight::setLed(leds leds, uint32_t color) {
  switch (leds) {
  case TOP_LEFT:
    MultiColorLight::setLed(1, color);
    break;
  case TOP_RIGHT:
    MultiColorLight::setLed(0, color);
    break;
  case BOTTOM:
    MultiColorLight::setLed(2, color);
    break;
  case TOP:
    for (int index = 0; index < 2; index++) {
      MultiColorLight::setLed(index, color);
    }
    break;
  case ALL:
    for (int index = 0; index < ledAmount; index++) {
      MultiColorLight::setLed(index, color);
    }
    break;
  default:
    // TODO logging
    break;
  }
};

void MultiColorLight::setLed(leds leds, uint8_t red, uint8_t green,
                             uint8_t blue) {
  MultiColorLight::setLed(leds, MultiColorLight::color(red, green, blue));
};

void MultiColorLight::setTopLeds(uint32_t color) {
  MultiColorLight::setLed(TOP, color);
};

void MultiColorLight::setTopLeds(uint8_t red, uint8_t green, uint8_t blue) {
  MultiColorLight::setTopLeds(MultiColorLight::color(red, green, blue));
};

void MultiColorLight::blink(uint16_t amount, uint32_t color, leds leds,
                            uint32_t interval) {
  for (uint16_t index = 0; index < amount; index++) {
    MultiColorLight::setLed(leds, color);
    vTaskDelay(interval);
    MultiColorLight::turnOffLed(leds);
    vTaskDelay(interval);
  }
};

void MultiColorLight::turnOffLed(leds leds) {
  switch (leds) {
  case TOP_LEFT:
    MultiColorLight::setLed(1, 0);
    break;
  case TOP_RIGHT:
    MultiColorLight::setLed(0, 0);
    break;
  case BOTTOM:
    MultiColorLight::setLed(2, 0);
    break;
  case TOP:
    for (int index = 0; index < 2; index++) {
      MultiColorLight::setLed(index, 0);
    }
    break;
  case ALL:
    for (int index = 0; index < 3; index++) {
      MultiColorLight::setLed(index, 0);
    }
    break;
  default:
    // TODO logging
    break;
  }
};

uint32_t MultiColorLight::color(uint8_t r, uint8_t g, uint8_t b) {
  return rgbLeds.Color(r, g, b);
};

// PRIVATE
uint32_t MultiColorLight::normalizeColor(uint32_t color,
                                         uint8_t maxBrightness) {
  uint8_t red = (color & 0x00FF0000) >> 16;
  uint8_t green = (color & 0x0000FF00) >> 8;
  uint8_t blue = (color & 0x000000FF);
  if (red > maxBrightness) {
    red = maxBrightness;
  }
  if (green > maxBrightness - 70) {
    green = maxBrightness - 70;
  }
  if (blue > maxBrightness - 50) {
    blue = maxBrightness - 50;
  }
  return MultiColorLight::color(red, green, blue);
};

float MultiColorLight::modelCurrentConsumption(uint32_t color) {
  uint32_t normalizedColor = normalizeColor(color);
  uint16_t colorComponentRed = (normalizedColor & 0x00FF0000) >> 16;
  uint16_t colorComponentGreen = (normalizedColor & 0x0000FF00) >> 8;
  uint16_t colorComponentBlue = (normalizedColor & 0x000000FF);
  float redChannelConsumption =
      (colorComponentRed / 255.0) *
      PowerParameters::CurrentConsumptions::CURRENT_LED_RGB_CHAN_T_ON;
  float greenChannelConsumption =
      (colorComponentGreen / 255.0) *
      PowerParameters::CurrentConsumptions::CURRENT_LED_RGB_CHAN_T_ON;
  float blueChannelConsumption =
      (colorComponentBlue / 255.0) *
      PowerParameters::CurrentConsumptions::CURRENT_LED_RGB_CHAN_T_ON;
  return redChannelConsumption + greenChannelConsumption +
         blueChannelConsumption +
         PowerParameters::CurrentConsumptions::CURRENT_LED_RGB_BASE;
};

float MultiColorLight::modelCurrentConsumption(uint8_t red, uint8_t green,
                                               uint8_t blue) {
  return modelCurrentConsumption(MultiColorLight::color(red, green, blue));
};

float MultiColorLight::modelChargeConsumption(uint8_t index, uint32_t color,
                                                 uint16_t durationMs) {
  if (index > ledAmount - 1) {
    // TODO: logging
  }
  uint32_t normalizedColor = normalizeColor(color);
  float ledConsumption = modelCurrentConsumption(normalizedColor);
  return (ledConsumption * durationMs) / 10e6f;
};

float MultiColorLight::modelChargeConsumption(leds leds, uint32_t color,
                                                 uint16_t durationMs) {
  float ledsConsumption = 0;
  switch (leds) {
  case TOP_LEFT:
    ledsConsumption =
        MultiColorLight::modelChargeConsumption(1, color, durationMs);
    break;
  case TOP_RIGHT:
    ledsConsumption =
        MultiColorLight::modelChargeConsumption(0, color, durationMs);
    break;
  case BOTTOM:
    ledsConsumption =
        MultiColorLight::modelChargeConsumption(2, color, durationMs);
    break;
  case TOP:
    for (int index = 0; index < 2; index++) {
      ledsConsumption +=
          MultiColorLight::modelChargeConsumption(index, color, durationMs);
    }
    break;
  case ALL:
    for (int index = 0; index < ledAmount; index++) {
      ledsConsumption +=
          MultiColorLight::modelChargeConsumption(index, color, durationMs);
    }
    break;
  default:
    // TODO logging
    break;
  }
  return ledsConsumption;
};

float MultiColorLight::modelChargeConsumption(leds leds, uint8_t red,
                                                 uint8_t green, uint8_t blue,
                                                 uint16_t durationMs) {
  return MultiColorLight::modelChargeConsumption(
      leds, MultiColorLight::color(red, green, blue), durationMs);
};
