#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Preferences.h>
#include <esp_mac.h>

// ========== ディスプレイとセンサの設定 ==========
#define SCREEN_WIDTH 128 
#define SCREEN_HEIGHT 64 
#define OLED_RESET     -1 
#define SCREEN_ADDRESS 0x3C 
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define ONE_WIRE_BUS 0 
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

#define LED_PIN 1   
#define SWITCH1_PIN 2   
#define BZ_PIN 20   
#define SWITCH2_PIN 21   
#define SWITCH3_PIN 18   

// ========== 新規構成のBLE UUID ==========
#define SERVICE_WEB_DATA_UUID         "a6a1bbcb-bb28-4109-8949-527aed7724ef" 
#define CHAR_WEB_RAW_DATA_UUID        "908e72ae-f437-4459-956b-4f05821885bf" 
#define CHAR_WEB_CALIB_DATA_UUID      "e255584a-6fd8-4e13-935f-15abec7d20a9" 

#define SERVICE_WEB_ADMIN_UUID        "ace891f9-871a-46c5-80c8-afbc1ae03027" 
#define CHAR_WEB_MODE_CTRL_UUID       "11b31d98-d27f-4d9c-87a7-502351fee507" 
#define CHAR_WEB_PARAM_UUID           "e16d00d7-acc7-46d7-b56e-93143f62abfe" 

BLECharacteristic* pCharWebRaw      = nullptr;
BLECharacteristic* pCharWebCalib    = nullptr;
BLECharacteristic* pCharWebParam    = nullptr;
BLECharacteristic* pCharWebMode     = nullptr;

bool deviceConnected = false;
Preferences preferences; 
char myDeviceName[20] = ""; 

// ========== 状態とパラメータ ==========
bool isCalibrated = false; 
bool isStreaming = false; 

// 補正式: T_calib = A * T_raw + B
float param_A1 = 1.0, param_B1 = 0.0;
float param_A2 = 1.0, param_B2 = 0.0;

// ========== BLEコールバック群 ==========

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) override { deviceConnected = true; }
    void onDisconnect(BLEServer* pServer) override {
      deviceConnected = false;
      isStreaming = false; // 切断時はストリーミング停止
      pServer->startAdvertising(); 
    }
};

class ParamWriteCallbacks: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pChar) override {
    String value = pChar->getValue().c_str();
    if (value.length() > 0) {
      int firstComma = value.indexOf(',');
      int secondComma = value.indexOf(',', firstComma + 1);
      int thirdComma = value.indexOf(',', secondComma + 1);

      if (firstComma > 0 && secondComma > 0 && thirdComma > 0) {
        param_A1 = value.substring(0, firstComma).toFloat();
        param_B1 = value.substring(firstComma + 1, secondComma).toFloat();
        param_A2 = value.substring(secondComma + 1, thirdComma).toFloat();
        param_B2 = value.substring(thirdComma + 1).toFloat();
        isCalibrated = true;

        preferences.begin("calib_data", false);
        preferences.putFloat("param_A1", param_A1);
        preferences.putFloat("param_B1", param_B1);
        preferences.putFloat("param_A2", param_A2);
        preferences.putFloat("param_B2", param_B2);
        preferences.putBool("is_calib", isCalibrated);
        preferences.end();
        
        Serial.printf("[Param Set] S1: %.3fx %+.3f, S2: %.3fx %+.3f\n", param_A1, param_B1, param_A2, param_B2);
      }
    }
  }
};

class ModeCtrlCallbacks: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pChar) override {
    String value = pChar->getValue().c_str();
    if (value.length() > 0) {
      value.toLowerCase();
      Serial.println("[Command] " + value);

      if (value == "start") {
        isStreaming = true;
      } else if (value == "stop") {
        isStreaming = false;
      } else if (value == "reset") {
        isCalibrated = false;
        param_A1 = 1.0; param_B1 = 0.0;
        param_A2 = 1.0; param_B2 = 0.0;
        preferences.begin("calib_data", false);
        preferences.clear();
        preferences.end();
        
        if(pCharWebParam) {
          pCharWebParam->setValue("1.0,0.0,1.0,0.0");
          pCharWebParam->notify();
        }
        Serial.println("Parameters Reset.");
      }
    }
  }
};

class RawDataCallbacks: public BLECharacteristicCallbacks {
  void onRead(BLECharacteristic *pChar) override {
    sensors.requestTemperatures();
    String val = String(sensors.getTempCByIndex(0), 2) + "," + String(sensors.getTempCByIndex(1), 2);
    pChar->setValue(val.c_str());
  }
};

void setup() {
  delay(100);
  Serial.begin(115200);

  pinMode(LED_PIN, OUTPUT);
  pinMode(SWITCH1_PIN, INPUT_PULLUP);
  pinMode(BZ_PIN, OUTPUT);
  pinMode(SWITCH2_PIN, INPUT_PULLUP);
  pinMode(SWITCH3_PIN, INPUT_PULLUP);

  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { for(;;); }
  
  preferences.begin("calib_data", true);
  param_A1 = preferences.getFloat("param_A1", 1.0);
  param_B1 = preferences.getFloat("param_B1", 0.0);
  param_A2 = preferences.getFloat("param_A2", 1.0);
  param_B2 = preferences.getFloat("param_B2", 0.0);
  isCalibrated = preferences.getBool("is_calib", false);
  preferences.end(); 

  display.clearDisplay();
  display.setTextSize(1);     
  display.setTextColor(SSD1306_WHITE); 
  display.setCursor(0, 0);     
  display.println("BLE Web Calibrator");
  display.display();          
  delay(1500);

  sensors.begin();
  sensors.setWaitForConversion(false);

  uint8_t mac[6];
  esp_read_mac(mac, ESP_MAC_WIFI_STA);
  snprintf(myDeviceName, sizeof(myDeviceName), "ESP32C6_%02X%02X", mac[4], mac[5]);
  
  BLEDevice::init(myDeviceName);
  BLEServer* pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks);
  
  BLEService* pServiceWeb1 = pServer->createService(SERVICE_WEB_DATA_UUID);
  pCharWebRaw = pServiceWeb1->createCharacteristic(CHAR_WEB_RAW_DATA_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
  pCharWebRaw->setCallbacks(new RawDataCallbacks());
  pCharWebCalib = pServiceWeb1->createCharacteristic(CHAR_WEB_CALIB_DATA_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
  pServiceWeb1->start();

  BLEService* pServiceWeb3 = pServer->createService(SERVICE_WEB_ADMIN_UUID);
  pCharWebMode = pServiceWeb3->createCharacteristic(CHAR_WEB_MODE_CTRL_UUID, BLECharacteristic::PROPERTY_WRITE);
  pCharWebMode->setCallbacks(new ModeCtrlCallbacks());
  
  pCharWebParam = pServiceWeb3->createCharacteristic(CHAR_WEB_PARAM_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY);
  pCharWebParam->setCallbacks(new ParamWriteCallbacks());
  String paramStr = String(param_A1, 4) + "," + String(param_B1, 4) + "," + String(param_A2, 4) + "," + String(param_B2, 4);
  pCharWebParam->setValue(paramStr.c_str());
  pServiceWeb3->start();

  BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_WEB_DATA_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->start();
  
  Serial.println("System Ready.");
}

void loop() {
  static bool ledState = false;       
  static bool lastSwitchState = HIGH; 
  unsigned long currentMillis = millis();
  
  // SW1: LEDトグル (チャタリング防止付き)
  bool currentSwitchState = digitalRead(SWITCH1_PIN);
  if (lastSwitchState == HIGH && currentSwitchState == LOW) {
    ledState = !ledState;             
    digitalWrite(LED_PIN, ledState);  
    delay(20); 
  }
  lastSwitchState = currentSwitchState; 
  
  // SW2: ブザー
  if (digitalRead(SWITCH2_PIN) == LOW) {
    tone(BZ_PIN,2000);
    delay(100);
  } else {
    noTone(BZ_PIN);
  }

  // SW3: 画面切り替え状態
  bool isSW3Pressed = (digitalRead(SWITCH3_PIN) == LOW);

  static unsigned long lastUpdate = 0;
  // ストリーミング or 定期画面更新
  if (currentMillis - lastUpdate >= 1000) {
    lastUpdate = currentMillis;
    sensors.requestTemperatures();
    
    // -127.00 は未接続を意味する
    float t1 = sensors.getTempCByIndex(0);
    float t2 = sensors.getTempCByIndex(1);

    if (deviceConnected && isStreaming) {
      if (pCharWebRaw) {
        String rawStr = String(t1, 2) + "," + String(t2, 2);
        pCharWebRaw->setValue(rawStr.c_str());
        pCharWebRaw->notify();
      }
      if (pCharWebCalib && isCalibrated) {
        float c1 = (t1 <= -100.0) ? t1 : (param_A1 * t1 + param_B1);
        float c2 = (t2 <= -100.0) ? t2 : (param_A2 * t2 + param_B2);
        String calibStr = String(c1, 2) + "," + String(c2, 2);
        pCharWebCalib->setValue(calibStr.c_str());
        pCharWebCalib->notify();
      }
    }

    // OLED画面更新
    display.clearDisplay();     
    display.setTextColor(SSD1306_WHITE);

    if (!isSW3Pressed) {
      // メイン画面（温度表示）
      display.setCursor(0, 0);     
      display.setTextSize(1);
      display.printf("ID: %s", myDeviceName); 
      display.drawFastHLine(0, 10, 128, SSD1306_WHITE); 
      
      // センサ1
      display.setTextSize(2);
      display.setCursor(0, 16);
      if (t1 <= -100.0) {
        display.print("T1: --.-");
      } else {
        float d1 = isCalibrated ? (param_A1 * t1 + param_B1) : t1;
        display.printf("%s:%.1f", isCalibrated ? "*1" : "T1", d1);
        display.write(0xF7); display.print("C");
      }

      // センサ2
      display.setCursor(0, 40);
      if (t2 <= -100.0) {
        display.print("T2: --.-");
      } else {
        float d2 = isCalibrated ? (param_A2 * t2 + param_B2) : t2;
        display.printf("%s:%.1f", isCalibrated ? "*2" : "T2", d2);
        display.write(0xF7); display.print("C");
      }
    } else {
      // パラメータ確認画面 (SW3押下時)
      display.setTextSize(1);
      display.setCursor(0, 0);
      display.print(isCalibrated ? "MODE: [CALIBRATED]" : "MODE: [RAW]");
      display.drawFastHLine(0, 10, 128, SSD1306_WHITE); 
      
      display.setCursor(0, 16);
      display.printf("S1: T=%.3fx%+.3f", param_A1, param_B1);
      display.setCursor(0, 30);
      display.printf("S2: T=%.3fx%+.3f", param_A2, param_B2);
    }
    
    display.display();
  }
}