#include <Arduino_GFX_Library.h>
#include <OneWire.h>
#include <Firebase_ESP_Client.h>
#include <DallasTemperature.h>
#include <Adafruit_GFX.h>
#include <FastLED.h>
#include <Stepper.h>
#include <WiFiManager.h>

const int stepsPerRevolution = 1024;

// ULN2003 Motor Driver Pins
#define IN1 26
#define IN2 25
#define IN3 33
#define IN4 32

Stepper myStepper(stepsPerRevolution, IN1, IN3, IN2, IN4);

#define API_KEY "AIzaSyDZ-oNBBvmzUCD1wxBPTmdf-P4PN1qPMyM"
#define DATABASE_URL "https://trspe-86989-default-rtdb.asia-southeast1.firebasedatabase.app/"
#define TFT_SCK    18
#define TFT_MOSI   23
#define TFT_MISO   19
#define TFT_CS     22
#define TFT_DC     21
#define TFT_RESET  15
#define ONE_WIRE_BUS 5
#define LED_PIN 4
#define NUM_LEDS 60

FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
Arduino_ESP32SPI bus = Arduino_ESP32SPI(TFT_DC, TFT_CS, TFT_SCK, TFT_MOSI, TFT_MISO);
Arduino_ILI9341 display = Arduino_ILI9341(&bus, TFT_RESET);
CRGB leds[NUM_LEDS];

int pHSense = 34;
int samples = 10;
float adc_resolution = 4095.0;

unsigned long sendDataPrevMillis = 0;
unsigned long stepperMovePrevMillis = 0;
unsigned long stepperMoveDuration = 1000;
int count = 0;
bool signupOK = false;
bool stepperInProgress = false;

void setup() {
    Serial.begin(115200);

    // Create an instance of WiFiManager
    WiFiManager wifiManager;

    // Uncomment the next line for a quick reset of the stored WiFi credentials (optional)
    // wifiManager.resetSettings();

    // Automatically connect using saved credentials, if available.
    // If no credentials are saved, it starts an access point to configure WiFi.
    if (!wifiManager.autoConnect("SmartQuarium")) {
        Serial.println("Failed to connect and hit timeout");
        delay(3000);
        // Reset and try again, or maybe put it to deep sleep
        ESP.restart();
        delay(5000);
    }

    Serial.println("Connected to WiFi!");

    config.api_key = API_KEY;
    config.database_url = DATABASE_URL;

    display.begin();
    display.fillScreen(BLACK);
    display.setCursor(20, 20);
    display.setTextSize(2);
    display.setTextColor(BLUE);

    sensors.begin();

    if (Firebase.signUp(&config, &auth, "", "")) {
        Serial.println("Firebase signup successful");
        signupOK = true;
    } else {
        Serial.printf("%s\n", config.signer.signupError.message.c_str());
    }

    Firebase.begin(&config, &auth);
    Firebase.reconnectWiFi(true);

    FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
    FastLED.clear();
    FastLED.show();

    // Stepper motor setup
    myStepper.setSpeed(5);
}

float ph(float voltage) {
    return 7 + ((2.50 - voltage) / 0.5);
}
void loop() {
    // Temperature sensing and Firebase update
    if (Firebase.ready() && signupOK && (millis() - sendDataPrevMillis > 15000 || sendDataPrevMillis == 0)) {
        sendDataPrevMillis = millis();

        sensors.requestTemperatures();
        float temperatureCelsius = sensors.getTempCByIndex(0);

        if (Firebase.RTDB.setFloat(&fbdo, "temp", temperatureCelsius)) {
            Serial.println("Temperature data sent to Firebase");
            Serial.println("PATH: " + fbdo.dataPath());
            Serial.println("TYPE: " + fbdo.dataType());
        } else {
            Serial.println("Failed to send temperature data to Firebase");
            Serial.println("REASON: " + fbdo.errorReason());
        }
    }

    // pH sensing and Firebase update
    int measuringSum = 0;

    for (int i = 0; i < samples; i++) {
        measuringSum += analogRead(pHSense);
        delay(10);
    }

    float voltage = 3.3 / adc_resolution * measuringSum / samples;
    Serial.print("pH= ");
    float pHValue = ph(voltage);
    Serial.println(pHValue);

    if (Firebase.ready() && signupOK) {
        if (Firebase.RTDB.setFloat(&fbdo, "ph", pHValue)) {
            Serial.println("pH data sent to Firebase");
        } else {
            Serial.println("Failed to send pH data to Firebase");
            Serial.println("REASON: " + fbdo.errorReason());
        }
    }

    // LED strip control based on Firebase color
    if (Firebase.ready() && signupOK) {
        if (Firebase.RTDB.getString(&fbdo, "color")) {
            String colorStr = fbdo.stringData();
            Serial.print("Received color: ");
            Serial.println(colorStr);

            if (colorStr.length() == 6) {
                uint32_t color = strtoul(colorStr.c_str(), NULL, 16);
                CRGB convertedColor = CRGB(color >> 16, (color >> 8) & 0xFF, color & 0xFF);
                fill_solid(leds, NUM_LEDS, convertedColor);
                FastLED.show();
            }
        }
    }

    // Check if the value in Firebase is 1 to move the stepper motor
   if (Firebase.ready() && signupOK) {
        if (Firebase.RTDB.getInt(&fbdo, "stepperControl")) {
            int stepperValue = fbdo.intData();
            Serial.print("Received stepper control value: ");
            Serial.println(stepperValue);

            if (stepperValue == 1 && !stepperInProgress) {
                // Stepper motor control
                Serial.println("Stepper Motor - Moving");
                myStepper.step(stepsPerRevolution);
                stepperMovePrevMillis = millis();  // Update the last movement time
                stepperInProgress = true;
            } else if (stepperInProgress && millis() - stepperMovePrevMillis >= stepperMoveDuration) {
                Serial.println("Stepper Motor - Stopped");
                stepperInProgress = false;

                // Disable stepper motor outputs by setting all driver pins to LOW
                digitalWrite(IN1, LOW);
                digitalWrite(IN2, LOW);
                digitalWrite(IN3, LOW);
                digitalWrite(IN4, LOW);

                // Set the "stepperControl" value back to 0
                if (Firebase.RTDB.setInt(&fbdo, "stepperControl", 0)) {
                    Serial.println("stepperControl set to 0 in Firebase");
                } else {
                    Serial.println("Failed to set stepperControl to 0 in Firebase");
                    Serial.println("REASON: " + fbdo.errorReason());
                }
            }
        }
    }
    // Display information on TFT
    display.fillScreen(BLACK);
    display.setRotation(3);
    display.setCursor(120, 100); // Center of the screen
    display.setTextSize(3);     // Larger text size
    display.setTextColor(WHITE);

    // Display pH value
    display.println("pH: " + String(pHValue));

    // Display temperature value
    display.println("Tempt: " + String(sensors.getTempCByIndex(0)) + " C");

    // Display Wi-Fi connection statu
    display.setRotation(0);
    delay(1000);
}
