#include "decode.h"

namespace esphome {
namespace panasonic_heatpump {
constexpr const char* const PanasonicDecode::DisabledEnabled[];
constexpr const char* const PanasonicDecode::BlockedFree[];
constexpr const char* const PanasonicDecode::OffOn[];
constexpr const char* const PanasonicDecode::InactiveActive[];
constexpr const char* const PanasonicDecode::PumpFlowRateMode[];
constexpr const char* const PanasonicDecode::HolidayState[];
constexpr const char* const PanasonicDecode::OperationMode[];
constexpr const char* const PanasonicDecode::PowerfulMode[];
constexpr const char* const PanasonicDecode::QuietMode[];
constexpr const char* const PanasonicDecode::ThreeWayValve[];
constexpr const char* const PanasonicDecode::TwoWayValve[];
constexpr const char* const PanasonicDecode::MixingValve[];
constexpr const char* const PanasonicDecode::ZoneState[];
constexpr const char* const PanasonicDecode::WaterTempControl[];
constexpr const char* const PanasonicDecode::SolarMode[];
constexpr const char* const PanasonicDecode::ZoneSensorType[];
constexpr const char* const PanasonicDecode::LiquidType[];
constexpr const char* const PanasonicDecode::ExtPadHeaterType[];
constexpr const char* const PanasonicDecode::BivalentMode[];
constexpr const char* const PanasonicDecode::HeatingControl[];
constexpr const char* const PanasonicDecode::SmartDHW[];
constexpr const char* const PanasonicDecode::QuietModePriority[];
constexpr const char* const PanasonicDecode::ModelNames[];

constexpr const uint8_t KnownModels[NUMBER_OF_MODELS][10] = {
    {0xE2, 0xCF, 0x0B, 0x13, 0x33, 0x32, 0xD1, 0x0C, 0x16, 0x33},  // 0
    {0xE2, 0xCF, 0x0B, 0x14, 0x33, 0x42, 0xD1, 0x0B, 0x17, 0x33},  // 1
    {0xE2, 0xCF, 0x0D, 0x77, 0x09, 0x12, 0xD0, 0x0B, 0x05, 0x11},  // 2
    {0xE2, 0xCF, 0x0C, 0x88, 0x05, 0x12, 0xD0, 0x0B, 0x97, 0x05},  // 3
    {0xE2, 0xCF, 0x0D, 0x85, 0x05, 0x12, 0xD0, 0x0C, 0x94, 0x05},  // 4
    {0xE2, 0xCF, 0x0D, 0x86, 0x05, 0x12, 0xD0, 0x0C, 0x95, 0x05},  // 5
    {0xE2, 0xCF, 0x0D, 0x87, 0x05, 0x12, 0xD0, 0x0C, 0x96, 0x05},  // 6
    {0xE2, 0xCE, 0x0D, 0x71, 0x81, 0x72, 0xCE, 0x0C, 0x92, 0x81},  // 7
    {0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0B, 0x72, 0x66},  // 8
    {0xC2, 0xD3, 0x0B, 0x33, 0x65, 0xB2, 0xD3, 0x0B, 0x94, 0x65},  // 9
    {0xE2, 0xCF, 0x0B, 0x15, 0x33, 0x42, 0xD1, 0x0B, 0x18, 0x33},  // 10
    {0xE2, 0xCF, 0x0B, 0x41, 0x34, 0x82, 0xD1, 0x0B, 0x31, 0x35},  // 11
    {0x62, 0xD2, 0x0B, 0x45, 0x54, 0x42, 0xD2, 0x0B, 0x47, 0x55},  // 12
    {0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0D, 0x95, 0x05},  // 13
    {0xE2, 0xCF, 0x0B, 0x82, 0x05, 0x12, 0xD0, 0x0C, 0x91, 0x05},  // 14
    {0xE2, 0xCF, 0x0C, 0x55, 0x14, 0x12, 0xD0, 0x0B, 0x15, 0x08},  // 15
    {0xE2, 0xCF, 0x0C, 0x43, 0x00, 0x12, 0xD0, 0x0B, 0x15, 0x08},  // 16
    {0x62, 0xD2, 0x0B, 0x45, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55},  // 17
    {0x62, 0xD2, 0x0B, 0x43, 0x54, 0x42, 0xD2, 0x0C, 0x46, 0x55},  // 18
    {0xE2, 0xCF, 0x0C, 0x54, 0x14, 0x12, 0xD0, 0x0B, 0x14, 0x08},  // 19
    {0xC2, 0xD3, 0x0B, 0x34, 0x65, 0xB2, 0xD3, 0x0B, 0x95, 0x65},  // 20
    {0xC2, 0xD3, 0x0B, 0x35, 0x65, 0xB2, 0xD3, 0x0B, 0x96, 0x65},  // 21
    {0x62, 0xD2, 0x0B, 0x41, 0x54, 0x32, 0xD2, 0x0C, 0x45, 0x55},  // 22
    {0x32, 0xD4, 0x0B, 0x87, 0x84, 0x73, 0x90, 0x0C, 0x84, 0x84},  // 23
    {0x32, 0xD4, 0x0B, 0x88, 0x84, 0x73, 0x90, 0x0C, 0x85, 0x84},  // 24
    {0xE2, 0xCF, 0x0B, 0x75, 0x09, 0x12, 0xD0, 0x0C, 0x06, 0x11},  // 25
    {0x42, 0xD4, 0x0B, 0x83, 0x71, 0x42, 0xD2, 0x0C, 0x46, 0x55},  // 26
    {0xC2, 0xD3, 0x0C, 0x34, 0x65, 0xB2, 0xD3, 0x0B, 0x95, 0x65},  // 27
    {0xC2, 0xD3, 0x0C, 0x33, 0x65, 0xB2, 0xD3, 0x0B, 0x94, 0x65},  // 28
    {0xE2, 0xCF, 0x0B, 0x83, 0x05, 0x12, 0xD0, 0x0D, 0x92, 0x05},  // 29
    {0xE2, 0xCF, 0x0C, 0x78, 0x09, 0x12, 0xD0, 0x0B, 0x06, 0x11},  // 30
    {0xC2, 0xD3, 0x0C, 0x35, 0x65, 0xB2, 0xD3, 0x0B, 0x96, 0x65},  // 31
    {0x32, 0xD4, 0x0B, 0x99, 0x77, 0x62, 0x90, 0x0B, 0x01, 0x78},  // 32
    {0x42, 0xD4, 0x0B, 0x15, 0x76, 0x12, 0xD0, 0x0B, 0x10, 0x11},  // 33
    {0xE2, 0xD5, 0x0C, 0x29, 0x99, 0x83, 0x92, 0x0C, 0x28, 0x98},  // 34
    {0xE2, 0xCF, 0x0D, 0x85, 0x05, 0x12, 0xD0, 0x0E, 0x94, 0x05},  // 35
    {0xE2, 0xD5, 0x0D, 0x36, 0x99, 0x02, 0xD6, 0x0F, 0x67, 0x95},  // 36
    {0xE2, 0xD5, 0x0B, 0x08, 0x95, 0x02, 0xD6, 0x0E, 0x66, 0x95},  // 37
    {0xE2, 0xD5, 0x0B, 0x34, 0x99, 0x83, 0x92, 0x0C, 0x29, 0x98},  // 38
    {0xE2, 0xCF, 0x0C, 0x89, 0x05, 0x12, 0xD0, 0x0C, 0x98, 0x05},  // 39
    {0xE2, 0xD5, 0x0B, 0x08, 0x95, 0x02, 0xD6, 0x0E, 0x67, 0x95},  // 40
    {0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0C, 0x96, 0x05},  // 41
    {0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0E, 0x95, 0x05},  // 42
    {0x32, 0xD4, 0x0B, 0x89, 0x84, 0x73, 0x90, 0x0C, 0x86, 0x84},  // 43
    {0x32, 0xD4, 0x0B, 0x00, 0x78, 0x62, 0x90, 0x0B, 0x02, 0x78},  // 44
    {0xE2, 0xCF, 0x0B, 0x82, 0x05, 0x12, 0xD0, 0x0D, 0x91, 0x05},  // 45
    {0xE2, 0xD5, 0x0D, 0x99, 0x94, 0x02, 0xD6, 0x0D, 0x68, 0x95},  // 46
    {0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0C, 0x95, 0x05},  // 47
    {0xE2, 0xD5, 0x0B, 0x34, 0x99, 0x83, 0x92, 0x0C, 0x28, 0x98},  // 48
    {0xE2, 0xCF, 0x0D, 0x77, 0x09, 0x12, 0xD0, 0x0C, 0x05, 0x11},  // 49
    {0xE2, 0xCF, 0x0B, 0x44, 0x34, 0x12, 0xD0, 0x0C, 0x34, 0x35},  // 50
    {0xE2, 0xD5, 0x0C, 0x67, 0x00, 0x83, 0x92, 0x0C, 0x27, 0x98},  // 51
    {0xE2, 0xD5, 0x0B, 0x34, 0x99, 0x83, 0x92, 0x0C, 0x27, 0x98},  // 52
    {0x42, 0xD4, 0x0B, 0x83, 0x71, 0x32, 0xD2, 0x0C, 0x44, 0x55},  // 53
    {0xE2, 0xCF, 0x0C, 0x74, 0x09, 0x12, 0xD0, 0x0E, 0x94, 0x05},  // 54
    {0x12, 0xD7, 0x0D, 0x98, 0x11, 0x33, 0x94, 0x0C, 0x83, 0x10},  // 55
    {0xE2, 0xD5, 0x0B, 0x08, 0x95, 0x02, 0xD6, 0x0F, 0x67, 0x95},  // 56
    {0xE2, 0xD5, 0x0D, 0x36, 0x99, 0x02, 0xD6, 0x10, 0x66, 0x95},  // 57
    {0xE2, 0xD5, 0x0F, 0x99, 0x94, 0x02, 0xD6, 0x10, 0x68, 0x95},  // 58
    {0xE2, 0xD5, 0x0C, 0x67, 0x00, 0x83, 0x92, 0x0C, 0x28, 0x98},  // 59
    {0xE2, 0xCF, 0x0D, 0x86, 0x05, 0x12, 0xD0, 0x0E, 0x95, 0x05},  // 60
    {0x12, 0xD7, 0x0C, 0x98, 0x14, 0x35, 0x94, 0x0D, 0x83, 0x10},  // 61
    {0xE2, 0xD5, 0x0C, 0x08, 0x95, 0x02, 0xD6, 0x10, 0x67, 0x95},  // 62
    {0xC2, 0xD3, 0x0D, 0x35, 0x65, 0xB2, 0xD3, 0x0B, 0x96, 0x65},  // 63
    {0xE2, 0xD5, 0x0C, 0x29, 0x99, 0x83, 0x92, 0x0C, 0x29, 0x98},  // 64
    {0x62, 0xD2, 0x0B, 0x45, 0x54, 0x42, 0xD2, 0x0B, 0x72, 0x66},  // 65
    {0xE2, 0xCF, 0x0B, 0x82, 0x09, 0x12, 0xD0, 0x0B, 0x10, 0x11},  // 66
    {0x42, 0xD4, 0x0B, 0x83, 0x71, 0x32, 0xD2, 0x0C, 0x45, 0x55},  // 67
    {0x12, 0xD7, 0x0B, 0x47, 0x19, 0x37, 0x94, 0x0E, 0x83, 0x10},  // 68
    {0xE2, 0xD5, 0x0D, 0x00, 0x09, 0x43, 0xD6, 0x0E, 0x07, 0x09},  // 69
    {0xE2, 0xCF, 0x0B, 0x84, 0x05, 0x12, 0xD0, 0x0B, 0x93, 0x05},  // 70
    {0xE2, 0xD5, 0x0B, 0x35, 0x99, 0x83, 0x92, 0x0D, 0x29, 0x98},  // 71
    {0x12, 0xD7, 0x0B, 0x98, 0x14, 0x35, 0x94, 0x0D, 0x83, 0x10},  // 72
};

int PanasonicDecode::getBit1(uint8_t input) {
  return input >> 7;
}

int PanasonicDecode::getBit1and2(uint8_t input) {
  return (input >> 6) - 1;
}

int PanasonicDecode::getBit3and4(uint8_t input) {
  return ((input >> 4) & 0b11) - 1;
}

int PanasonicDecode::getBit5and6(uint8_t input) {
  return ((input >> 2) & 0b11) - 1;
}

int PanasonicDecode::getBit7and8(uint8_t input) {
  return (input & 0b11) - 1;
}

int PanasonicDecode::getBit3and4and5(uint8_t input) {
  return ((input >> 3) & 0b111) - 1;
}

int PanasonicDecode::getBit6and7and8(uint8_t input) {
  return (input & 0b111) - 1;
}

int PanasonicDecode::getByteMinus1(uint8_t input) {
  return (int)input - 1;
}

int PanasonicDecode::getByteMinus128(uint8_t input) {
  return (int)input - 128;
}

float PanasonicDecode::getByteMinus1Div2(uint8_t input) {
  return ((float)input - 1) / 2;
}

float PanasonicDecode::getByteMinus1Div5(uint8_t input) {
  return ((float)input - 1) / 5;
}

float PanasonicDecode::getByteMinus1Div50(uint8_t input) {
  return ((float)input - 1) / 50;
}

int PanasonicDecode::getByteMinus1Times10(uint8_t input) {
  return ((int)input - 1) * 10;
}

int PanasonicDecode::getByteMinus1Times50(uint8_t input) {
  return ((int)input - 1) * 50;
}

int PanasonicDecode::getByteMinus1Times200(uint8_t input) {
  return ((int)input - 1) * 200;
}

int PanasonicDecode::getHighNibbleMinus1(uint8_t input) {
  return (input >> 4) - 1;
}

int PanasonicDecode::getLowNibbleMinus1(uint8_t input) {
  return (input & 0b1111) - 1;
}

int PanasonicDecode::getWordMinus1(const std::vector<uint8_t>& data, uint8_t index) {
  return ((data[index + 1] << 8) + data[index]) - 1;
}

int PanasonicDecode::getUintt16(const std::vector<uint8_t>& data, uint8_t index) {
  return (static_cast<uint16_t>((data[index + 1] << 8) | data[index])) - 1;
}

// TOP4 //
int PanasonicDecode::getOperationMode(uint8_t input) {
  switch ((int)(input & 0b111111)) {
  case 0b100001:  // 0x21 = tank
    return 0;
  case 0b010010:  // 0x12 = heat
    return 1;
  case 0b100010:  // 0x22 = heat+tank
    return 2;
  case 0b010011:  // 0x13 = cool
    return 3;
  case 0b100011:  // 0x23 = cool+tank
    return 4;
  case 0b011000:  // 0x18 = auto
    return 5;
  case 0b101000:  // 0x28 = auto-tank
    return 6;
  case 0b011001:  // 0x19 = auto-heat
    return 7;
  case 0b101001:  // 0x29 = auto-heat+tank
    return 8;
  case 0b011010:  // 0x1A = auto-cool
    return 9;
  case 0b101010:  // 0x2A = auto-cool+tank
    return 10;
  default:  // unknown
    return -1;
  }
}

// TOP92 //
int PanasonicDecode::getModel(const std::vector<uint8_t>& data, uint8_t index) {
  // Ensure there are at least 10 bytes in data message after the start index
  if (index + 10 > data.size())
    return -1;
  // Get model bytes from data message
  auto model = std::vector<uint8_t>(data.begin() + index, data.begin() + index + 10);
  // Search model bytes in the list of known models and return the index
  for (size_t i = 0; i < NUMBER_OF_MODELS; i++) {
    if (std::equal(KnownModels[i], KnownModels[i] + 10, model.begin())) {
      return static_cast<int>(i);
    }
  }

  return -1;
}

// TOP1 //
// input1 = data[169]
// input2 = data[170]
float PanasonicDecode::getPumpFlow(const std::vector<uint8_t>& data, uint8_t index) {
  return (((float)data[index] - 1) / 256) + ((int)data[index + 1]);
}

// TOP5, TOP6 //
float PanasonicDecode::getFractional(uint8_t input, uint8_t shift) {
  return ((int)((input >> shift) & 0b111) - 1) * 0.25;
}

// TOP44 //
// errorType = data[113]
// errorNumber = data[114]
std::string PanasonicDecode::getErrorInfo(uint8_t errorType, uint8_t errorNumber) {
  int error_type = (int)(errorType);
  int error_number = ((int)(errorNumber)) - 17;
  char error_string[10];

  switch (error_type) {
  case 0xA1:  // H type error
    sprintf(error_string, "H%02X", error_number);
    break;
  case 0xB1:  // F type error
    sprintf(error_string, "F%02X", error_number);
    break;
  default:
    sprintf(error_string, "No error");
    break;
  }

  return error_string;
}

bool PanasonicDecode::getBinaryState(uint8_t input) {
  return input != 0;
}

std::string PanasonicDecode::getTextState(const char* const array[], int index) {
  int size = atoi(array[0]);

  if ((index < 0) || (index >= size)) {
    return "UNDEFINED";
  }

  return array[index + 1];
}
}  // namespace panasonic_heatpump
}  // namespace esphome
