#include "my_board.h"

#include <ArduinoJson.h>
#include <FS.h>
#include <LittleFS.h>

#include "global.h"
#include "JsonConstrain.h"
#include "system.h"

static const char* tag = "board";

BOARD_PINS* thisBoardPins;

// Basic validator for ESP32-S3 GPIOs; rejects common reserved/USB/strap pins
static bool isValidGpio(int pin) {
  if (pin < 0 || pin > 48) return false;
  switch (pin) {
    case 19: // USB D-
    case 20: // USB D+
    case 45: // strapping
    case 46: // strapping
      return false;
    default:
      return true;
  }
}

bool Load_Board_Pins(BOARD_PINS& boardPins, const String& path){
  // Default initialize to -1 to avoid stale values on partial loads
  memset(&boardPins, -1, sizeof(boardPins));
  thisBoardPins = &boardPins;
  File file = LittleFS.open(path);

  if (!file) {
    ESP_LOGE(tag, "Error opening %s...", path.c_str());
    return false;
  }

  JsonDocument doc;
  DeserializationError error = deserializeJson(doc, file);
  file.close();

  if (error) {
    ESP_LOGE(tag, "%s deserialize error!..", path.c_str());
    return false;
  }

  JsonObject boardJson = doc.as<JsonObject>();
  boardPins.rgb1 =      jsonConstrain<int>(tag, boardJson, "rgb1",     -1, 48, -1);
  boardPins.rgb2 =      jsonConstrain<int>(tag, boardJson, "rgb2",     -1, 48, -1);
  boardPins.btn[0] =    jsonConstrain<int>(tag, boardJson, "btn1",     -1, 48, -1);
  boardPins.btn[1] =    jsonConstrain<int>(tag, boardJson, "btn2",     -1, 48, -1);
  boardPins.btn[2] =    jsonConstrain<int>(tag, boardJson, "btn3",     -1, 48, -1);
  boardPins.buzzer =    jsonConstrain<int>(tag, boardJson, "buzzer",   -1, 48, -1);
  boardPins.touch[0] =  jsonConstrain<int>(tag, boardJson, "touch1",   -1, 48, -1);
  boardPins.touch[1] =  jsonConstrain<int>(tag, boardJson, "touch2",   -1, 48, -1);
  boardPins.touch[2] =  jsonConstrain<int>(tag, boardJson, "touch3",   -1, 48, -1);
  boardPins.touch[3] =  jsonConstrain<int>(tag, boardJson, "touch4",   -1, 48, -1);
  boardPins.touch[4] =  jsonConstrain<int>(tag, boardJson, "touch5",   -1, 48, -1);
  boardPins.shield =    jsonConstrain<int>(tag, boardJson, "shield",   -1, 48, -1);
  boardPins.relay[0] =  jsonConstrain<int>(tag, boardJson, "relay1",   -1, 48, -1);
  boardPins.relay[1] =  jsonConstrain<int>(tag, boardJson, "relay2",   -1, 48, -1);
  boardPins.relay[2] =  jsonConstrain<int>(tag, boardJson, "relay3",   -1, 48, -1);
  boardPins.relay[3] =  jsonConstrain<int>(tag, boardJson, "relay4",   -1, 48, -1);
  boardPins.stat[0] =   jsonConstrain<int>(tag, boardJson, "stat1",    -1, 48, -1);
  boardPins.stat[1] =   jsonConstrain<int>(tag, boardJson, "stat2",    -1, 48, -1);
  boardPins.adc1 =      jsonConstrain<int>(tag, boardJson, "adc1",     -1, 48, -1);
  boardPins.oled_dc =   jsonConstrain<int>(tag, boardJson, "oled_dc",  -1, 48, -1);
  boardPins.oled_rst =  jsonConstrain<int>(tag, boardJson, "oled_rst", -1, 48, -1);
  boardPins.oled_mosi = jsonConstrain<int>(tag, boardJson, "oled_mosi", -1, 48, -1);
  boardPins.oled_sck =  jsonConstrain<int>(tag, boardJson, "oled_sck",  -1, 48, -1);
  boardPins.oled_cs =   jsonConstrain<int>(tag, boardJson, "oled_cs",  -1, 48, -1);
  boardPins.ext[0] =    jsonConstrain<int>(tag, boardJson, "ext1",     -1, 48, -1);
  boardPins.ext[1] =    jsonConstrain<int>(tag, boardJson, "ext2",     -1, 48, -1);
  boardPins.rf433tx =   jsonConstrain<int>(tag, boardJson, "rf433tx",  -1, 48, -1);
  boardPins.rf433rx =   jsonConstrain<int>(tag, boardJson, "rf433rx",  -1, 48, -1);

  // Validate pins against reserved GPIOs
  auto clampPin = [](int v){ return isValidGpio(v) ? v : -1; };
  boardPins.rgb1 = clampPin(boardPins.rgb1);
  boardPins.rgb2 = clampPin(boardPins.rgb2);
  for (int i=0;i<3;i++) boardPins.btn[i] = clampPin(boardPins.btn[i]);
  boardPins.buzzer = clampPin(boardPins.buzzer);
  for (int i=0;i<5;i++) boardPins.touch[i] = clampPin(boardPins.touch[i]);
  boardPins.shield = clampPin(boardPins.shield);
  for (int i=0;i<4;i++) boardPins.relay[i] = clampPin(boardPins.relay[i]);
  for (int i=0;i<2;i++) boardPins.stat[i] = clampPin(boardPins.stat[i]);
  boardPins.adc1 = clampPin(boardPins.adc1);
  boardPins.oled_dc = clampPin(boardPins.oled_dc);
  boardPins.oled_rst = clampPin(boardPins.oled_rst);
  boardPins.oled_mosi = clampPin(boardPins.oled_mosi);
  boardPins.oled_sck = clampPin(boardPins.oled_sck);
  boardPins.oled_cs = clampPin(boardPins.oled_cs);
  for (int i=0;i<2;i++) boardPins.ext[i] = clampPin(boardPins.ext[i]);
  boardPins.rf433tx = clampPin(boardPins.rf433tx);
  boardPins.rf433rx = clampPin(boardPins.rf433rx);

  ESP_LOGI(tag, "loaded Pins from %s", path.c_str());
  return true;
}


void Init_Board_Basic(BOARD_PINS& boardPins)
{
  
  if(boardPins.stat[0] >= 0){ pinMode(boardPins.stat[0], OUTPUT); }
  if(boardPins.stat[1] >= 0){ pinMode(boardPins.stat[1], OUTPUT); }

  if(boardPins.btn[0] >= 0){ pinMode(boardPins.btn[0], INPUT_PULLUP); }
  if(boardPins.btn[1] >= 0){ pinMode(boardPins.btn[1], INPUT_PULLUP); }
  if(boardPins.btn[2] >= 0){ pinMode(boardPins.btn[2], INPUT); }

  if(boardPins.rgb1 >= 0){ pinMode(boardPins.rgb1, OUTPUT); }
  if(boardPins.rgb2 >= 0){ pinMode(boardPins.rgb2, OUTPUT); }

  if(boardPins.relay[0] >= 0){ pinMode(boardPins.relay[0], OUTPUT); }
  if(boardPins.relay[1] >= 0){ pinMode(boardPins.relay[1], OUTPUT); }
  if(boardPins.relay[2] >= 0){ pinMode(boardPins.relay[2], OUTPUT); }
  if(boardPins.relay[3] >= 0){ pinMode(boardPins.relay[3], OUTPUT); }

  ESP_LOGI(tag, "Board pins initialized...");
}




