#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_NeoPixel.h>
#include <HCSR04.h>

#define PIN1        6 // Pin pro původní LED pásek
#define PIN2        5 // Pin pro nový LED pásek
#define NUMPIXELS   66 // Celkový počet LED na obou páscích
#define MAX_DISTANCE 100 // Maximální vzdálenost (prázdná nádrž) v cm
#define FULL_DISTANCE 10 // Minimální vzdálenost (plná nádrž) v cm

Adafruit_NeoPixel pixels1(NUMPIXELS, PIN1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixels2(6, PIN2, NEO_GRB + NEO_KHZ800); // Nový LED pásek pro písmena "ONCESS"
LiquidCrystal_I2C lcd(0x27, 16, 2);
UltraSonicDistanceSensor distanceSensor(9, 10);  // Inicializace senzoru vzdálenosti, který používá digitální piny 9 a 10.

const int nadrz_sirka_cm = 120;
const int nadrz_delka_cm = 100;
const float max_objem_litru = 1200.0; // Maximální objem nádrže v litrech
const int pocet_nadrzi = 4; // Počet nádrží

float filterMeasurement(float measurement) {
    static float measurements[5] = {0}; // Uchovává posledních 5 měření
    static int index = 0; // Index pro ukládání nových měření
    static bool initialized = false; // Indikátor, zda byl buffer inicializován
    static float sum = 0; // Suma měření

    if (!initialized) { // Pokud není buffer inicializován, proveď inicializaci
        for (int i = 0; i < 5; ++i) {
            measurements[i] = measurement; // Nastav všechna měření na aktuální hodnotu
            sum += measurement; // Aktualizuj sumu měření
        }
        initialized = true; // Nastav indikátor inicializace na true
    } else {
        sum -= measurements[index]; // Odeber staré měření z sumy
        measurements[index] = measurement; // Ulož nové měření
        sum += measurement; // Přidej nové měření do sumy
        index = (index + 1) % 5; // Posuň index na další pozici v bufferu
    }

    return sum / 5; // Vrať průměr posledních 5 měření
}

void setup() {
    pixels1.begin(); // Spuštění LED pásku
    pixels1.clear(); // Nastavení všech barev pixelů na 'vypnuto'
    pixels1.show();  // Odeslání aktualizovaných barev pixelů na hardware.
  
    pixels2.begin(); // Spuštění nového LED pásku
    pixels2.clear(); // Nastavení všech barev pixelů na 'vypnuto'
    pixels2.show();  // Odeslání aktualizovaných barev pixelů na hardware.

    lcd.init(); // Inicializace LCD displeje
    lcd.backlight(); // Zapnutí podsvícení

    lcd.setCursor(0, 0);
    lcd.print("OncesSuv        "); // Nulový stav
    lcd.setCursor(0, 1);
    lcd.print("      HLADINOMER"); // Nulový stav
    
    // Bliknutí písmen "ONCESS" na novém LED pásku
    for (int i = 5; i >= -1; i--) {
        pixels2.setPixelColor(i, pixels2.Color(255, 255, 255)); // Nastavení bílé barvy
        pixels2.show();
        delay(300); // Doba bliknutí
        pixels2.setPixelColor(i, pixels2.Color(0, 0, 0)); // Vypnutí LED
    }
    delay(200); // Krátká pauza
}

float objem_v_litreach(int vzdalenost_cm) {
    // Výpočet objemu nádrže v litrech
    float objem = max_objem_litru - (max_objem_litru * (vzdalenost_cm - FULL_DISTANCE) / (MAX_DISTANCE - FULL_DISTANCE)); // Objem v litrach
    return max(0, objem); // Zajištění, že hodnota objemu nebude záporná
}

void duhaEfekt() {
    // Opakování efektu duhy 3x za sebou
    for (int k = 0; k < 2; k++) {
        // Efekt duhy
        for(int j = 255; j > 0; j--) { // Obrácený směr efektu
            for(int i = 0; i < pixels1.numPixels(); i++) {
                pixels1.setPixelColor(i, pixels1.ColorHSV(i * 65536 / pixels1.numPixels() + j * 256));
            }
            pixels1.show();
            delay(5);
        }
    }
  
}

void zobrazHladinuNaLCD(int hladinaProcent) {
    lcd.setCursor(0, 0);
    lcd.print("0%           "); // Nulový stav
    lcd.setCursor(12, 0);
    lcd.print("100%"); // Plný stav
    lcd.setCursor(2, 0);
    int celkemSegmentu = map(hladinaProcent, 0, 100, 0, 10);
    for (int i = 0; i < 10 - celkemSegmentu; i++) {
        lcd.write(byte(0xFF)); // Plný segment
    }
}

void vlniciVoda() {
    // Efekt výrazného vlnění vody mezi jednotlivými diodami
    for(int j = 0; j < 255; j+=10) { // Zvýšení kroku změny jasu
        for(int i = 0; i < 6; i++) {
            int brightness = map(j, 0, 255, 0, 255); // Postupné zvýšení jasu
            pixels2.setPixelColor(i, pixels2.Color(brightness, brightness, 255)); // Nastavení barvy a jasu pixelu
        }
        pixels2.show();
        delay(35); // Zkrácení zpoždění pro rychlejší vlnění
    }
    for(int j = 255; j >= 0; j-=10) { // Zvýšení kroku změny jasu
        for(int i = 0; i < 6; i++) {
            int brightness = map(j, 0, 255, 0, 255); // Postupné snížení jasu
            pixels2.setPixelColor(i, pixels2.Color(brightness, brightness, 255)); // Nastavení barvy a jasu pixelu
        }
        pixels2.show();
        delay(35); // Zkrácení zpoždění pro rychlejší vlnění
    }
}



void cervenyScanner() {
    // Efekt červeného skeneru s dlouhou stopou
    for(int j = 0; j < NUMPIXELS * 2; j++) { // Vytvoření dlouhé stopy
        for(int i = 0; i < NUMPIXELS; i++) {
            int pixelIndex = (j + i) % NUMPIXELS; // Index pixelu posunutý o aktuální hodnotu j
            int brightness = map(abs(j - NUMPIXELS) - abs(i - NUMPIXELS), 0, NUMPIXELS, 255, 0); // Postupné snižování jasu podle pozice
            pixels1.setPixelColor(pixelIndex, pixels1.Color(brightness, 0, 0)); // Nastavení barvy a jasu pixelu
        }
        pixels1.show();
        delay(10);
    }
    // Vymazání stopy
    for(int i = 0; i < NUMPIXELS; i++) {
        pixels1.setPixelColor(i, pixels1.Color(0, 0, 0)); // Vypnutí pixelu
    }
    pixels1.show();
}

void loop() {
    // Měření vzdálenosti
    int distance = filterMeasurement(distanceSensor.measureDistanceCm());
    
    // Přepočet vzdálenosti pro výpočet objemu
    int adjusted_distance = min(MAX_DISTANCE, max(FULL_DISTANCE, distance));

    // Přemapování vzdálenosti na LED pásek
    int current_Last_LED = map(adjusted_distance, FULL_DISTANCE, MAX_DISTANCE, 0, NUMPIXELS);

    static int counter = 0; // Počítadlo pro efekt duhy
    counter++; // Inkrementace počítadla

    // Spuštění efektu duhy každých pět cyklů
    if (counter % 13 == 0) {
        duhaEfekt();
    }

    // Nastavení barev pixelů na LED pásku
    if (objem_v_litreach(adjusted_distance) <= 120) {
        // Pokud je objem v litrech roven 0, spusť efekt červeného skeneru
        cervenyScanner();
    } else {
        // Jinak nastav LED pásek podle stavu hladiny
        for(int i = 0; i < NUMPIXELS; i++) { 
            if(i < current_Last_LED) {
                pixels1.setPixelColor(i, 100, 50, 50); // Nastavení barvy pixelu na modrou podle stavu hladiny
            } else {
                pixels1.setPixelColor(i, 0, 0, 255); // Nastavení barvy pixelu na červenou, pokud nesvítí
            }
        }
    }

    pixels1.show(); // Odeslání aktualizovaných barev pixelů na hardware.
  
    // Výpočet objemu v litrech na základě naměřené vzdálenosti
    float objem_litru = objem_v_litreach(adjusted_distance) * pocet_nadrzi;
  
    // Zobrazení hladiny v nádrži na LCD displeji (v procentech)
    int hladinaProcent = map(adjusted_distance, FULL_DISTANCE, MAX_DISTANCE, 0, 100);
    zobrazHladinuNaLCD(hladinaProcent);

    // Zobrazení celkového počtu litrů na LCD displeji (celé číslo)
    lcd.setCursor(0, 1);
    lcd.print("CELKEM: ");
    lcd.print((int)objem_litru);
    lcd.print(" l     ");

    // Efekt mírně se vlnící vody
    vlniciVoda();

    delay(1000); // Zpoždění pro snížení četnosti aktualizace
}