// ============================================
// KODE C - ESP8266 DISPLAY MONITOR
// Versi: 4.1 - Layout Baru (Icon Kiri, Rank Kanan)
// ============================================

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ArduinoJson.h>

// ============================================
// KONFIGURASI LCD
// ============================================
#define LCD_COLUMNS 16
#define LCD_ROWS 2
#define LCD_ADDRESS 0x27
LiquidCrystal_I2C lcd(LCD_ADDRESS, LCD_COLUMNS, LCD_ROWS);

// ============================================
// KONFIGURASI WIFI
// ============================================
const char* ssid = "Galapagos";
const char* password = "@Galapagos123";

// ============================================
// KONFIGURASI API
// ============================================
const char* baseUrl = "http://zidcreative.com/sishopint/api";
const char* getAllPoinUrl = "/get_all_peserta.php";

// ============================================
// KONFIGURASI UPDATE & SCROLL
// ============================================
const unsigned long UPDATE_INTERVAL = 300000;  // 5 MENIT
const unsigned long SCROLL_INTERVAL = 2500;    // 2.5 DETIK

// ============================================
// STRUKTUR DATA
// ============================================
#define MAX_PESERTA 150

struct Peserta {
    int id;
    String nama;
    int totalPoin;
    int poinSholat;
    int poinKegiatan;
    int poinHariIni;
};

Peserta daftarPeserta[MAX_PESERTA];
int jumlahPeserta = 0;
int currentDisplayIndex = 0;
int maxPoin = 0;

// ============================================
// VARIABEL GLOBAL
// ============================================
bool wifiConnected = false;
bool isUpdating = false;
unsigned long lastUpdate = 0;
unsigned long lastScroll = 0;
String statusMessage = "Siap";
unsigned long lastDisplayRefresh = 0;

// ============================================
// FUNGSI UTILITY LCD
// ============================================

void lcdPrintRight(int row, String text) {
    int padding = 16 - text.length();
    if (padding < 0) padding = 0;
    lcd.setCursor(padding, row);
    lcd.print(text);
}

String truncateText(String text, int maxLen = 10) {
    if (text.length() <= maxLen) return text;
    return text.substring(0, maxLen - 3) + "...";
}

// ============================================
// FUNGSI EKSPRESI BERDASARKAN PERSENTASE
// ============================================

String getExpressionIcon(int poin, int maxPoin) {
    if (maxPoin == 0) return ":|";
    
    float percentage = ((float)poin / maxPoin) * 100.0;
    
    // ============================================
    // KARAKTER ASCII UNTUK LCD 16x2
    // ============================================
    if (percentage >= 90.0) {
        return ":)";   // Senyum lebar
    } else if (percentage >= 70.0) {
        return ":)";   // Senyum
    } else if (percentage >= 40.0) {
        return ":|";   // Muka datar
    } else if (percentage >= 20.0) {
        return ":(";   // Sedih
    } else {
        return ":'(";  // Menangis
    }
}

// ============================================
// FUNGSI KONEKSI WIFI
// ============================================

void connectWiFi() {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Menghubungkan...");
    
    WiFi.begin(ssid, password);
    int attempts = 0;
    
    while (WiFi.status() != WL_CONNECTED && attempts < 20) {
        delay(500);
        lcd.setCursor(0, 1);
        lcd.print(".");
        for(int i = 0; i < attempts % 10; i++) lcd.print(".");
        attempts++;
        Serial.print(".");
    }
    
    if (WiFi.status() == WL_CONNECTED) {
        wifiConnected = true;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("WiFi OK!");
        lcd.setCursor(0, 1);
        lcd.print(WiFi.localIP().toString());
        Serial.println("\nWiFi Connected!");
        Serial.print("IP: ");
        Serial.println(WiFi.localIP());
        delay(1500);
    } else {
        wifiConnected = false;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("WiFi GAGAL!");
        lcd.setCursor(0, 1);
        lcd.print("Mode Offline");
        Serial.println("\nWiFi Failed!");
        delay(2000);
    }
}

void checkWiFi() {
    if (WiFi.status() == WL_CONNECTED) {
        if (!wifiConnected) {
            wifiConnected = true;
            Serial.println("WiFi Reconnected");
        }
    } else {
        if (wifiConnected) {
            wifiConnected = false;
            Serial.println("WiFi Disconnected");
            showErrorQuick("WiFi", "Putus");
        }
    }
}

// ============================================
// FUNGSI FETCH DATA
// ============================================

bool fetchData() {
    if (WiFi.status() != WL_CONNECTED) {
        return false;
    }
    
    if (isUpdating) {
        Serial.println("Update already in progress...");
        return false;
    }
    
    isUpdating = true;
    Serial.println("=== START FETCH DATA ===");
    
    String url = String(baseUrl) + getAllPoinUrl;
    Serial.print("Fetching: ");
    Serial.println(url);
    
    WiFiClient client;
    HTTPClient http;
    http.begin(client, url);
    http.addHeader("User-Agent", "ESP8266-HTTPClient/1.0");
    http.addHeader("Accept", "application/json");
    http.setTimeout(15000);
    
    int httpCode = http.GET();
    Serial.print("HTTP Code: ");
    Serial.println(httpCode);
    
    bool success = false;
    
    if (httpCode == 200) {
        String response = http.getString();
        http.end();
        
        if (response.length() > 0) {
            success = parseDataV7(response);
        }
    } else {
        Serial.print("HTTP Error: ");
        Serial.println(httpCode);
        http.end();
    }
    
    isUpdating = false;
    Serial.println("=== FETCH COMPLETE ===");
    
    return success;
}

// ============================================
// FUNGSI PARSE DATA
// ============================================

bool parseDataV7(String json) {
    Serial.println("=== Parsing JSON ===");
    
    JsonDocument doc;
    DeserializationError error = deserializeJson(doc, json);
    
    if (error) {
        Serial.print("JSON Error: ");
        Serial.println(error.c_str());
        return false;
    }
    
    bool success = doc["success"].as<bool>();
    if (!success) {
        Serial.println("API Error: " + doc["message"].as<String>());
        return false;
    }
    
    JsonArray dataArray = doc["data"].as<JsonArray>();
    
    jumlahPeserta = 0;
    maxPoin = 0;
    
    for (JsonObject obj : dataArray) {
        if (jumlahPeserta >= MAX_PESERTA) break;
        
        const char* namaPtr = obj["nama"].as<const char*>();
        String nama = namaPtr ? String(namaPtr) : "";
        
        if (nama.length() > 0) {
            daftarPeserta[jumlahPeserta].id = obj["peserta_id"].as<int>();
            daftarPeserta[jumlahPeserta].nama = nama;
            daftarPeserta[jumlahPeserta].totalPoin = obj["total_poin"].as<int>();
            daftarPeserta[jumlahPeserta].poinSholat = obj["poin_sholat"].as<int>();
            daftarPeserta[jumlahPeserta].poinKegiatan = obj["poin_kegiatan"].as<int>();
            daftarPeserta[jumlahPeserta].poinHariIni = obj["poin_hari_ini"].as<int>();
            
            if (daftarPeserta[jumlahPeserta].totalPoin > maxPoin) {
                maxPoin = daftarPeserta[jumlahPeserta].totalPoin;
            }
            
            jumlahPeserta++;
        }
    }
    
    Serial.print("Total peserta dimuat: ");
    Serial.println(jumlahPeserta);
    Serial.print("Max Poin: ");
    Serial.println(maxPoin);
    
    if (jumlahPeserta == 0) {
        return false;
    }
    
    sortPesertaByPoin();
    
    currentDisplayIndex = 0;
    lastScroll = millis();
    
    statusMessage = String(jumlahPeserta) + " Peserta";
    
    return true;
}

// ============================================
// FUNGSI SORTING
// ============================================

void sortPesertaByPoin() {
    for (int i = 0; i < jumlahPeserta - 1; i++) {
        for (int j = 0; j < jumlahPeserta - i - 1; j++) {
            if (daftarPeserta[j].totalPoin < daftarPeserta[j + 1].totalPoin) {
                Peserta temp = daftarPeserta[j];
                daftarPeserta[j] = daftarPeserta[j + 1];
                daftarPeserta[j + 1] = temp;
            }
        }
    }
}

// ============================================
// FUNGSI DISPLAY - LAYOUT BARU
// ============================================

void displayPeserta(int index) {
    if (jumlahPeserta == 0) {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Belum ada data");
        lcd.setCursor(0, 1);
        lcd.print("Tunggu update...");
        return;
    }
    
    if (index >= jumlahPeserta) {
        index = 0;
        currentDisplayIndex = 0;
    }
    
    Peserta p = daftarPeserta[index];
    
    lcd.clear();
    
    // ============================================
    // BARIS 1: Nama (kiri) + Total Poin (kanan)
    // ============================================
    String namaDisplay = truncateText(p.nama, 10);
    lcd.setCursor(0, 0);
    lcd.print(namaDisplay);
    
    String poinText = String(p.totalPoin);
    lcdPrintRight(0, poinText);
    
    // ============================================
    // BARIS 2: Icon (kiri) + Peringkat (kanan)
    // ============================================
    
    // Icon ekspresi di kiri (di bawah nama)
    String icon = getExpressionIcon(p.totalPoin, maxPoin);
    lcd.setCursor(0, 1);
    lcd.print(icon);
    
    // Peringkat di kanan (di bawah poin)
    String rankText = String(index + 1) + "/" + String(jumlahPeserta);
    lcdPrintRight(1, rankText);
}

// ============================================
// FUNGSI TAMPILAN LAINNYA
// ============================================

void showSplashScreen() {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Monitor Poin");
    lcd.setCursor(0, 1);
    lcd.print("v4.1");
    delay(1500);
}

void showDataLoaded(String info) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Data Dimuat!");
    lcd.setCursor(0, 1);
    lcd.print(info.substring(0, 16));
    delay(1500);
}

void showErrorQuick(String title, String msg) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("ERROR: " + title);
    lcd.setCursor(0, 1);
    lcd.print(msg.substring(0, 16));
    delay(1000);
}

void showStatusUpdate() {
    lcd.setCursor(0, 1);
    lcd.print("Updating...     ");
}

// ============================================
// SETUP
// ============================================

void setup() {
    Serial.begin(115200);
    Serial.println("\n=== Monitor Poin v4.1 ===");
    Serial.print("ArduinoJson Version: ");
    Serial.println(ARDUINOJSON_VERSION);
    
    lcd.init();
    lcd.backlight();
    lcd.clear();
    
    showSplashScreen();
    connectWiFi();
    
    if (wifiConnected) {
        delay(1000);
        if (fetchData()) {
            showDataLoaded(String(jumlahPeserta) + " peserta");
            displayPeserta(0);
        } else {
            showErrorQuick("Data", "Gagal load");
        }
    }
    
    lastUpdate = millis();
    lastScroll = millis();
    lastDisplayRefresh = millis();
}

// ============================================
// LOOP
// ============================================

void loop() {
    unsigned long now = millis();
    
    // Cek WiFi
    static unsigned long lastWifiCheck = 0;
    if (now - lastWifiCheck >= 10000) {
        checkWiFi();
        lastWifiCheck = now;
    }
    
    // Update data
    if (wifiConnected && !isUpdating && (now - lastUpdate >= UPDATE_INTERVAL)) {
        Serial.println("=== SCHEDULED UPDATE ===");
        
        lcd.setCursor(0, 1);
        lcd.print("Updating...     ");
        
        if (fetchData()) {
            currentDisplayIndex = 0;
            lastScroll = now;
            showDataLoaded(String(jumlahPeserta) + " peserta");
            displayPeserta(0);
        } else {
            showErrorQuick("Update", "Gagal");
        }
        
        lastUpdate = now;
    }
    
    // ============================================
    // SLIDESHOW
    // ============================================
    if (jumlahPeserta > 0) {
        // Pindah slide setiap SCROLL_INTERVAL
        if (now - lastScroll >= SCROLL_INTERVAL) {
            currentDisplayIndex = (currentDisplayIndex + 1) % jumlahPeserta;
            lastScroll = now;
            displayPeserta(currentDisplayIndex);
        } else {
            // Refresh display untuk animasi
            if (now - lastDisplayRefresh >= 500) {
                displayPeserta(currentDisplayIndex);
                lastDisplayRefresh = now;
            }
        }
    } else {
        if (now - lastUpdate > 10000) {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Tidak ada data");
            lcd.setCursor(0, 1);
            lcd.print("Cek koneksi...");
        }
    }
    
    delay(50);
}