#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "DHT.h"
#include <Adafruit_NeoPixel.h>
#include <BH1750.h>
#include "HomeSpan.h" 
#include "DEV_LED.h"  

#define Version "0.3.3"



#define LED_PIN 5
#define LED_COUNT 9
const uint8_t ledMap[LED_COUNT] = { 3, 0, 1, 8, 2, 5, 4, 7, 6 };

//2DO:
//Pomiar światła bh1750 i fotorezystor
// obsługa nie zamontowania -wyświetlacza, bt1750, DHT22

#define NUMPIXELS 10  //docelowo 9-10

#define DHTTYPE DHT22

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C

//PINS
#define BTN_DIMM 34      //GPIO34    D34
#define BTN_RST 35       //GPIO35    D35
#define BTN_HYGRSTT 36   //GPIO36    VP
#define BTN_SPEED 39     //GPIO35    VN
#define PIN_SPEED_1 32   //GPIO32    D32
#define PIN_SPEED_2 14   //GPIO36    D14
#define DHTPIN 4         //GPIO04    D4
#define LED_WHT_TANK 25  //GPIO26    D26
#define LED_RED_TANK 26  //GPIO25    D25
#define NEOPIXEL 27      //GPIO04    D27
#define IN_PHOTOTRA 12
#define WTR_LVL 33       //GPIO33    D33
// SDA D21
// SCL D22
#define DIMM_MAX_VALUE 3  // docelowo 4, tylko na testy

//NEO PIXEL ARDESES:
#define NEOPXL_MULTIPLIER 25
// ARDESES:
#define ADR_NEOPXL_SPEED_1 1
#define ADR_NEOPXL_SPEED_2 0
#define ADR_NEOPXL_FILTER 3
#define ADR_NEOPXL_L1 2
#define ADR_NEOPXL_L2 5
#define ADR_NEOPXL_L3 4
#define ADR_NEOPXL_L4 7
#define ADR_NEOPXL_L5 6
#define ADR_NEOPXL_WATERLVL 8
#define ADR_NEOPXL_TANK

#define NON_ACTIVE_LED_DIVIDER 8

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIXEL, NEO_GRB + NEO_KHZ800);
DHT dht(DHTPIN, DHTTYPE);
BH1750 lightMeter;



//MAIN VALUES VARIABLES
// homekit
int RedHomeKit = 0;
int GreenHomeKit = 0;
int BlueHomeKit = 0;
bool turnOnFlag = true;

int8_t resetVal = 0;
int8_t fanSpeedVal = 1;
int8_t hygrostatVal = 55;
int8_t dimmStep = 1;
int8_t dimmVal = 1;
int8_t waterLvlVal = 0;
int8_t photoVal = 0;
float temp = 0.0;
float hum = 0.0;
float lux = 0.0;

//NEOPIXELS VARIABLES
int8_t neoPixelSwitch = 1;
int8_t neoPixelRed = 10;
int8_t neoPixelGreen = 10;
int8_t neoPixelBlue = 10;

int8_t neoPixelSpeed_1 = 1;
int8_t neoPixelSpeed_2 = 1;
int8_t neoPixelFilter = 1;
int8_t neoPixelLvl_1 = 1;
int8_t neoPixelLvl_2 = 1;
int8_t neoPixelLvl_3 = 1;
int8_t neoPixelLvl_4 = 1;
int8_t neoPixelLvl_5 = 1;
int8_t neoPixelWaterLvlR = 1;
int8_t neoPixelWaterLvlG = 1;
int8_t neoPixelWaterLvlB = 1;
int8_t neoPixelTank = 1;




void IRAM_ATTR dimmButtonFcn() {
  dimmStep++;
  if (dimmStep > DIMM_MAX_VALUE) {
    dimmStep = 0;
  }
}
void IRAM_ATTR speedButtonFcn() {
  fanSpeedVal++;
  if (fanSpeedVal >= 3) {
    fanSpeedVal = 1;
  }
}
void IRAM_ATTR hygrostatButtonFcn() {
  hygrostatVal = hygrostatVal + 5;
  if (hygrostatVal > 65) {
    hygrostatVal = 45;
  }
}

void setFanSpeed(uint8_t spdInpt) {
  if (spdInpt == 1) {
    digitalWrite(PIN_SPEED_2, LOW);
    delay(300);
    digitalWrite(PIN_SPEED_1, HIGH);
  } else if (spdInpt == 2) {
    digitalWrite(PIN_SPEED_1, LOW);
    delay(300);
    digitalWrite(PIN_SPEED_2, HIGH);
  } else {
    digitalWrite(PIN_SPEED_1, LOW);
    delay(300);
    digitalWrite(PIN_SPEED_2, LOW);
  }
}

void rainbowCycle(uint8_t wait) {
  uint16_t i, j;
  for (j = 0; j < 256 * 3; j++) {
    for (i = 0; i < LED_COUNT; i++) {
      uint8_t physIndex = ledMap[i];  // mapowanie pozycji logicznej na fizyczną
      pixels.setPixelColor(physIndex, Wheel((i * 256 / LED_COUNT + j) & 255));
    }
    pixels.show();
    delay(wait);
  }
}
uint32_t Wheel(byte pos) {
  pos = 255 - pos;
  uint8_t r, g, b;
  if (pos < 85) {
    r = 255 - pos * 3;
    g = 0;
    b = pos * 3;
  } else if (pos < 170) {
    pos -= 85;
    r = 0;
    g = pos * 3;
    b = 255 - pos * 3;
  } else {
    pos -= 170;
    r = pos * 3;
    g = 255 - pos * 3;
    b = 0;
  }
  r = r * 30 / 100;  // Ogranicz jasność do 30%
  g = g * 30 / 100;
  b = b * 30 / 100;
  return pixels.Color(r, g, b);
}

void setup() {
  Serial.begin(115200);
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
  }

  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println("Version:");
  display.setCursor(60, 0);
  display.println(Version);
  display.display();

  // delay(500);
  pinMode(BTN_RST, INPUT);
  pinMode(BTN_DIMM, INPUT);
  pinMode(BTN_SPEED, INPUT);
  pinMode(BTN_HYGRSTT, INPUT);
  pinMode(PIN_SPEED_1, OUTPUT);
  pinMode(PIN_SPEED_2, OUTPUT);
  pinMode(LED_RED_TANK, OUTPUT);
  pinMode(LED_WHT_TANK, OUTPUT);
  pinMode(IN_PHOTOTRA, INPUT_PULLDOWN);
  pinMode(WTR_LVL, INPUT);

  attachInterrupt(digitalPinToInterrupt(BTN_DIMM), dimmButtonFcn, FALLING);
  attachInterrupt(digitalPinToInterrupt(BTN_SPEED), speedButtonFcn, FALLING);
  attachInterrupt(digitalPinToInterrupt(BTN_HYGRSTT), hygrostatButtonFcn, FALLING);
  Wire.begin();
  lightMeter.begin();
  dht.begin();
  pixels.begin();
  if (dimmStep == 0) {
    dimmVal = 0;
  } else if (dimmStep == 1) {
    dimmVal = 1;
  } else if (dimmStep == 2) {
    dimmVal = 6;
  } else if (dimmStep == 3) {
    dimmVal = 12;
  } else {
    dimmVal = 24;
  }
// ##### HOME SPAN
 homeSpan.setPairingCode("11122333");
  homeSpan.setQRID("111-22-333");
// konfiguracja WIFI przez port szerefowy "W<returm"
// 1.Płytka ESP32 Dev Module
// 2 Partycja: Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)
// 3. konfiguracja WIFI przez port szerefowy komenda "W", U - unpair
  homeSpan.begin(Category::Bridges,"HomeSpan Bridge");

  new SpanAccessory();  
    new Service::AccessoryInformation();
      new Characteristic::Identify();

  new SpanAccessory();                                                          
    new Service::AccessoryInformation();    
      new Characteristic::Identify();               
      new Characteristic::Name("RGB LED"); 
    new DEV_RgbLED();     
// ##### HOME SPAN END
} //setup end

void loop() {
  neoPixelRed = int(RedHomeKit/10);
  neoPixelGreen = int(GreenHomeKit/10);
  neoPixelBlue = int(BlueHomeKit/10);
  // ###################################################
  // WYŚWIETLACZ
  display.clearDisplay();
  display.setCursor(90, 0);
  display.println(Version);

  display.setCursor(0, 0);
  display.println("RST:");
  display.setCursor(25, 0);
  display.println(resetVal);

  display.setCursor(35, 0);
  display.println("L:");
  display.setCursor(47, 0);
  display.println(lux);

  display.setCursor(0, 10);
  display.println("WtrLvl:");
  display.setCursor(50, 10);
  display.println(waterLvlVal);
  display.setCursor(70, 10);
  display.println("FanSpd:");
  display.setCursor(115, 10);
  display.println(fanSpeedVal);

  //    display.setCursor(0, 10);
  //    display.println("FanSpd:");
  //    display.setCursor(50, 10);
  //    display.println(fanSpeedVal);
  //
  //    display.setCursor(70, 10);
  //    display.println("Hygst:");
  //    display.setCursor(115, 10);
  //    display.println(hygrostatVal);

  display.setCursor(0, 20);
  display.println("Hum:");
  display.setCursor(28, 20);
  display.println(hum);

  // display.setCursor(0, 20);
  // display.println("Ph:");
  // display.setCursor(28, 20);
  // display.println(photoVal);

  display.setCursor(70, 20);
  display.println("Dimm:");
  display.setCursor(115, 20);
  display.println(dimmStep);

  display.display();

  display.setCursor(70, 20);
  display.println("Dimm:");
  display.setCursor(115, 20);
  display.println(dimmStep);

  display.display();

  // DATA HAVEREST
  lux = lightMeter.readLightLevel();
  waterLvlVal = touchRead(WTR_LVL);
  hum = (dht.readHumidity());
  photoVal = analogRead(IN_PHOTOTRA);
  //    #################################################################

  //    #################################################################
  // LOGIKA
  if (digitalRead(BTN_RST) == HIGH) {
    unsigned long startTime = millis();
    while (digitalRead(BTN_RST) == HIGH && millis() - startTime < 4000) {
      display.clearDisplay();
      display.setCursor(20, 0);
      display.println("Resetowanie... 4s");
      display.display();
      delay(100);
    }
    if (digitalRead(BTN_RST) == HIGH) {
      resetVal = 1;
      display.clearDisplay();
      display.setCursor(0, 0);
      display.println("Zresetowano");
      display.display();
      delay(500);
    }
  }

  // jeśli nieosiągnieto bliskiej wilgotności - zaświec diody
  // jeśli osiągnięto bliską wilgotność na 2 biegu zaświeć jedną i przciemnij drugą
  // jeśli osiągnięto wilgotność przyciemnij diody zależnie od biegu
  if (hum > hygrostatVal) {
    setFanSpeed(0);
    neoPixelSpeed_1 = 1;
    neoPixelSpeed_2 = 0;

    if (fanSpeedVal == 2) {
      neoPixelSpeed_2 = 1;
    }
  } else if (hum >= (hygrostatVal - 5) and fanSpeedVal == 2) {
    setFanSpeed(1);  //ustaw predkość 1 zbliża sie do docelowej wartości
    neoPixelSpeed_1 = NON_ACTIVE_LED_DIVIDER;
    neoPixelSpeed_2 = 1;
  } else if (hum < hygrostatVal) {
    setFanSpeed(fanSpeedVal);
    neoPixelSpeed_1 = NON_ACTIVE_LED_DIVIDER;
    if (fanSpeedVal == 1) {
      neoPixelSpeed_2 = 0;
    } else {
      neoPixelSpeed_2 = NON_ACTIVE_LED_DIVIDER;
    }
  }

  if (dimmStep == 0) {
    dimmVal = 0;
  } else if (dimmStep == 1) {
    dimmVal = 1;
  } else if (dimmStep == 2) {
    dimmVal = 6;
  } else if (dimmStep == 3) {
    dimmVal = 12;
  } else {
    dimmVal = 24;
  }

// Ustawianie LED docelowych wartości
  if (hygrostatVal >= 65) {
    neoPixelLvl_1 = 1;
    neoPixelLvl_2 = 1;
    neoPixelLvl_3 = 1;
    neoPixelLvl_4 = 1;
    neoPixelLvl_5 = 1;

  } else if (hygrostatVal == 60) {
    neoPixelLvl_1 = 1;
    neoPixelLvl_2 = 1;
    neoPixelLvl_3 = 1;
    neoPixelLvl_4 = 1;
    neoPixelLvl_5 = 0;
  } else if (hygrostatVal == 55) {
    neoPixelLvl_1 = 1;
    neoPixelLvl_2 = 1;
    neoPixelLvl_3 = 1;
    neoPixelLvl_4 = 0;
    neoPixelLvl_5 = 0;
  } else if (hygrostatVal == 50) {
    neoPixelLvl_1 = 1;
    neoPixelLvl_2 = 1;
    neoPixelLvl_3 = 0;
    neoPixelLvl_4 = 0;
    neoPixelLvl_5 = 0;
  } else if (hygrostatVal == 45) {
    neoPixelLvl_1 = 1;
    neoPixelLvl_2 = 0;
    neoPixelLvl_3 = 0;
    neoPixelLvl_4 = 0;
    neoPixelLvl_5 = 0;
  } else if (hygrostatVal <= 40) {
    neoPixelLvl_1 = 0;
    neoPixelLvl_2 = 0;
    neoPixelLvl_3 = 0;
    neoPixelLvl_4 = 0;
    neoPixelLvl_5 = 0;
  } else {
    neoPixelLvl_1 = 1;
    neoPixelLvl_2 = 0;
    neoPixelLvl_3 = 1;
    neoPixelLvl_4 = 0;
    neoPixelLvl_5 = 1;
  }

// Ustawianie LED osiągniętych wartości
  if (hum >= 65 && hygrostatVal >= 65 ) {
    neoPixelLvl_5 = 8;
  }
  if (hum >= 60 && hygrostatVal >= 60) {
      neoPixelLvl_4 = 8;
  }
  if (hum >= 55 && hygrostatVal >= 55) {
      neoPixelLvl_3 = 8;
  }
  if (hum >= 50 &&  hygrostatVal >= 50) {
      neoPixelLvl_2 = 8;
  }
  if (hum >= 45 && hygrostatVal >= 45) {
      neoPixelLvl_1 = 8;
  }

  // wartości zależne od sposobu zasilania PC/Powerbank/Ładowarka

  //46-47 - sonda nie dotyka wody
  //26 - ledwo na dnie
  // 25 jest troche wody
  // 24-25 połowa zbiornika
  // 23-24 pełen zbiornik

  if (waterLvlVal >= 38) {  // Empty TANK
    neoPixelWaterLvlR = 10;
    neoPixelWaterLvlG = 0;
    neoPixelWaterLvlB = 0;
    analogWrite(LED_WHT_TANK, dimmVal * 8);
    analogWrite(LED_RED_TANK, 0);
    // tu set fan
  }
  if (waterLvlVal < 40 && waterLvlVal >= 25) {  //a bit of water TANK
    neoPixelWaterLvlR = 10;
    neoPixelWaterLvlG = 10;
    neoPixelWaterLvlB = 0;
    analogWrite(LED_WHT_TANK, 0);
    analogWrite(LED_RED_TANK, dimmVal * 8);
  }
  if (waterLvlVal <= 24) {  // FULL TANK
    neoPixelWaterLvlR = neoPixelRed;
    neoPixelWaterLvlG = neoPixelGreen;
    neoPixelWaterLvlB = neoPixelBlue;
    analogWrite(LED_WHT_TANK, 0);
    analogWrite(LED_RED_TANK, dimmVal * 8);
    // tu set fan
  }


  // if minął czas -> resetVal = 0;

  if (resetVal == 1) {
    neoPixelFilter = 0;
  } else {
    neoPixelFilter = 1;
  }


  //NEOPIXELS
  //    #################################################################
  //    #################################################################
  pixels.clear();
  //(G,R,B)
  pixels.setPixelColor(ADR_NEOPXL_SPEED_1, pixels.Color(int((neoPixelGreen * dimmVal * neoPixelSwitch * neoPixelSpeed_1) / NON_ACTIVE_LED_DIVIDER), int((neoPixelRed * dimmVal * neoPixelSwitch * neoPixelSpeed_1) / NON_ACTIVE_LED_DIVIDER), int((neoPixelBlue * dimmVal * neoPixelSwitch * neoPixelSpeed_1) / NON_ACTIVE_LED_DIVIDER)));
  pixels.setPixelColor(ADR_NEOPXL_SPEED_2, pixels.Color(int((neoPixelGreen * dimmVal * neoPixelSwitch * neoPixelSpeed_2) / NON_ACTIVE_LED_DIVIDER), int((neoPixelRed * dimmVal * neoPixelSwitch * neoPixelSpeed_2) / NON_ACTIVE_LED_DIVIDER), int((neoPixelBlue * dimmVal * neoPixelSwitch * neoPixelSpeed_2) / NON_ACTIVE_LED_DIVIDER)));
  pixels.setPixelColor(ADR_NEOPXL_FILTER, pixels.Color(neoPixelGreen * dimmVal * neoPixelSwitch * neoPixelFilter, neoPixelRed * dimmVal * neoPixelSwitch * neoPixelFilter, neoPixelBlue * dimmVal * neoPixelSwitch * neoPixelFilter));
  pixels.setPixelColor(ADR_NEOPXL_L1, pixels.Color(int(neoPixelGreen * dimmVal * neoPixelSwitch * neoPixelLvl_1/ NON_ACTIVE_LED_DIVIDER), int(neoPixelRed * dimmVal * neoPixelSwitch * neoPixelLvl_1/ NON_ACTIVE_LED_DIVIDER), int(neoPixelBlue * dimmVal * neoPixelSwitch * neoPixelLvl_1 / NON_ACTIVE_LED_DIVIDER)));
  pixels.setPixelColor(ADR_NEOPXL_L2, pixels.Color(int(neoPixelGreen * dimmVal * neoPixelSwitch * neoPixelLvl_2/ NON_ACTIVE_LED_DIVIDER), int(neoPixelRed * dimmVal * neoPixelSwitch * neoPixelLvl_2/ NON_ACTIVE_LED_DIVIDER), int(neoPixelBlue * dimmVal * neoPixelSwitch * neoPixelLvl_2 / NON_ACTIVE_LED_DIVIDER)));
  pixels.setPixelColor(ADR_NEOPXL_L3, pixels.Color(int(neoPixelGreen * dimmVal * neoPixelSwitch * neoPixelLvl_3/ NON_ACTIVE_LED_DIVIDER), int(neoPixelRed * dimmVal * neoPixelSwitch * neoPixelLvl_3/ NON_ACTIVE_LED_DIVIDER), int(neoPixelBlue * dimmVal * neoPixelSwitch * neoPixelLvl_3 / NON_ACTIVE_LED_DIVIDER)));
  pixels.setPixelColor(ADR_NEOPXL_L4, pixels.Color(int(neoPixelGreen * dimmVal * neoPixelSwitch * neoPixelLvl_4/ NON_ACTIVE_LED_DIVIDER), int(neoPixelRed * dimmVal * neoPixelSwitch * neoPixelLvl_4/ NON_ACTIVE_LED_DIVIDER), int(neoPixelBlue * dimmVal * neoPixelSwitch * neoPixelLvl_4 / NON_ACTIVE_LED_DIVIDER)));
  pixels.setPixelColor(ADR_NEOPXL_L5, pixels.Color(int(neoPixelGreen * dimmVal * neoPixelSwitch * neoPixelLvl_5/ NON_ACTIVE_LED_DIVIDER), int(neoPixelRed * dimmVal * neoPixelSwitch * neoPixelLvl_5/ NON_ACTIVE_LED_DIVIDER), int(neoPixelBlue * dimmVal * neoPixelSwitch * neoPixelLvl_5 / NON_ACTIVE_LED_DIVIDER)));
  pixels.setPixelColor(ADR_NEOPXL_WATERLVL, pixels.Color(dimmVal * neoPixelSwitch * neoPixelWaterLvlG, dimmVal * neoPixelSwitch * neoPixelWaterLvlR, dimmVal * neoPixelSwitch * neoPixelWaterLvlB));
  // pixels.setPixelColor(ADR_NEOPXL_TANK,     pixels.Color(neoPixelGreen*dimmVal*neoPixelSwitch*neoPixelTank,    neoPixelRed*dimmVal*neoPixelSwitch*neoPixelTank,     neoPixelBlue*dimmVal*neoPixelSwitch*neoPixelTank));
  pixels.show();
  // rainbowCycle(10); // im mniejsza liczba, tym szybsza animacja

  homeSpan.poll();
  Serial.println("---------------------------");
  Serial.print("R: ");
  Serial.println(RedHomeKit);
  Serial.print("G: ");
  Serial.println(GreenHomeKit);
  Serial.print("B: ");
  Serial.println(BlueHomeKit);
  Serial.print("turnOnFlag: ");
  Serial.println(turnOnFlag);
  

  delay(500);

}
