/*
  ____ ___ ____   _   _ _____ _______        _____  ____  _  __
 / ___/ _ \___ \ | \ | | ____|_   _\ \      / / _ \|  _ \| |/ /
| |  | | | |__) ||  \| |  _|   | |  \ \ /\ / / | | | |_) | ' / 
| |__| |_| / __/ | |\  | |___  | |   \ V  V /| |_| |  _ <| . \ 
 \____\___/_____||_| \_|_____| |_|    \_/\_/  \___/|_| \_\_|\_\

Combined CO2 + PM2.5 + PM10 + Network Status Display
Shows 3 key measurements plus network connectivity status

Pour compiler:
g++ -I/var/www/moduleair_pro_4g/matrix/include -L/var/www/moduleair_pro_4g/matrix/lib /var/www/moduleair_pro_4g/matrix/screenSensors/displayCO2_PM_Network.cc -o /var/www/moduleair_pro_4g/matrix/screenSensors/displayCO2_PM_Network -lrgbmatrix -lsqlite3

Pour lancer:
sudo /var/www/moduleair_pro_4g/matrix/screenSensors/displayCO2_PM_Network
*/

#include "led-matrix.h"
#include "graphics.h"
#include <unistd.h>
#include <string.h>
#include <fstream>
#include <iostream>
#include <signal.h>
#include <atomic>
#include <sqlite3.h>
#include <sstream>
#include <iomanip>
#include <chrono>
#include <ctime>
#include <algorithm>
#include <cmath>

using rgb_matrix::RGBMatrix;
using rgb_matrix::Canvas;

std::atomic<bool> running(true);

// Define color codes
#define RESET   "\033[0m"
#define RED     "\033[31m"
#define GREEN   "\033[32m"
#define YELLOW  "\033[33m"
#define BLUE    "\033[34m"
#define MAGENTA "\033[35m"
#define CYAN    "\033[36m"
#define WHITE   "\033[37m"

// Path to the SQLite database
const std::string DB_PATH = "/var/www/moduleair_pro_4g/sqlite/sensors.db";

void signal_handler(int signum) {
    running = false;
}

void log(const std::string& type, const std::string& message) {
    std::string color;
    
    if (type == "BLUE") color = BLUE;
    else if (type == "GREEN") color = GREEN;
    else if (type == "YELLOW") color = YELLOW;
    else if (type == "RED") color = RED;
    else color = WHITE;
    
    std::cout << color << message << RESET << std::endl;
}

// Function to retrieve latest sensor data
bool get_sensor_data(sqlite3* db, float& pm25, float& pm10, float& co2) {
    sqlite3_stmt* stmt;
    bool pm_success = false;
    bool co2_success = false;
    
    log("BLUE", "Querying sensor data from database...");
    
    // Get PM2.5 and PM10 data from NPM table
    const char* pm_query = "SELECT PM25, PM10 FROM data_NPM ORDER BY rowid DESC LIMIT 1";
    if (sqlite3_prepare_v2(db, pm_query, -1, &stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(stmt) == SQLITE_ROW) {
            pm25 = sqlite3_column_double(stmt, 0);
            pm10 = sqlite3_column_double(stmt, 1);
            pm_success = true;
            std::cout << "  Retrieved PM values - PM2.5: " << pm25 << ", PM10: " << pm10 << std::endl;
        }
        sqlite3_finalize(stmt);
    }
    
    // Get CO2 data
    const char* co2_query = "SELECT CO2 FROM data_CO2 ORDER BY rowid DESC LIMIT 1";
    if (sqlite3_prepare_v2(db, co2_query, -1, &stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(stmt) == SQLITE_ROW) {
            co2 = sqlite3_column_double(stmt, 0);
            co2_success = true;
            std::cout << "  Retrieved CO2 value: " << co2 << " ppm" << std::endl;
        }
        sqlite3_finalize(stmt);
    }
    
    return pm_success && co2_success;
}

// Function to get network status
bool get_network_status(sqlite3* db, std::string& network_status, std::string& wifi_status) {
    sqlite3_stmt* stmt;
    bool status_found = false;
    
    // Get 4G network status
    const char* network_query = "SELECT value FROM config_table WHERE key='SARA_network_status'";
    if (sqlite3_prepare_v2(db, network_query, -1, &stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(stmt) == SQLITE_ROW) {
            network_status = (char*)sqlite3_column_text(stmt, 0);
            status_found = true;
        } else {
            network_status = "unknown";
        }
        sqlite3_finalize(stmt);
    }
    
    // Get WiFi status
    const char* wifi_query = "SELECT value FROM config_table WHERE key='WIFI_status'";
    if (sqlite3_prepare_v2(db, wifi_query, -1, &stmt, NULL) == SQLITE_OK) {
        if (sqlite3_step(stmt) == SQLITE_ROW) {
            wifi_status = (char*)sqlite3_column_text(stmt, 0);
        } else {
            wifi_status = "unknown";
        }
        sqlite3_finalize(stmt);
    }
    
    return status_found;
}

// Function to get color based on air quality thresholds
rgb_matrix::Color get_quality_color(float value, const std::string& type) {
    if (type == "pm25") {
        if (value < 10) return rgb_matrix::Color(0, 255, 0);      // Green - Good
        else if (value < 20) return rgb_matrix::Color(255, 255, 0); // Yellow - Moderate
        else if (value < 50) return rgb_matrix::Color(255, 165, 0); // Orange - Degraded
        else return rgb_matrix::Color(255, 0, 0);                   // Red - Bad
    } else if (type == "pm10") {
        if (value < 15) return rgb_matrix::Color(0, 255, 0);      // Green - Good
        else if (value < 30) return rgb_matrix::Color(255, 255, 0); // Yellow - Moderate
        else if (value < 75) return rgb_matrix::Color(255, 165, 0); // Orange - Degraded
        else return rgb_matrix::Color(255, 0, 0);                   // Red - Bad
    } else if (type == "co2") {
        if (value < 800) return rgb_matrix::Color(0, 255, 0);     // Green - Good
        else if (value < 1500) return rgb_matrix::Color(255, 165, 0); // Orange - Moderate
        else return rgb_matrix::Color(255, 0, 0);                     // Red - Bad
    }
    return rgb_matrix::Color(255, 255, 255); // White default
}

// Function to get status text based on thresholds
std::string get_status_text(float value, const std::string& type) {
    if (type == "pm25") {
        if (value < 10) return "BON";
        else if (value < 20) return "MOYEN";
        else if (value < 50) return "DEGRADE";
        else return "MAUVAIS";
    } else if (type == "pm10") {
        if (value < 15) return "BON";
        else if (value < 30) return "MOYEN";
        else if (value < 75) return "DEGRADE";
        else return "MAUVAIS";
    } else if (type == "co2") {
        if (value < 800) return "BON";
        else if (value < 1500) return "AERER SVP";
        else return "AERER VITE";
    }
    return "UNKNOWN";
}

// Function to draw network connection indicators
void draw_network_indicators(Canvas* canvas, const std::string& network_status, const std::string& wifi_status) {
    rgb_matrix::Color green(0, 255, 0);
    rgb_matrix::Color red(255, 0, 0);
    rgb_matrix::Color orange(255, 165, 0);
    rgb_matrix::Color yellow(255, 255, 0);
    
    // Draw 4G indicator (small rectangle)
    rgb_matrix::Color net_color = (network_status == "connected") ? green : 
                                 (network_status == "connecting" || network_status == "booting") ? yellow : red;
    
    for (int x = 100; x < 108; x++) {
        for (int y = 2; y < 8; y++) {
            canvas->SetPixel(x, y, net_color.r, net_color.g, net_color.b);
        }
    }
    
    // Draw WiFi indicator (small rectangle)
    rgb_matrix::Color wifi_color = (wifi_status == "connected") ? green :
                                  (wifi_status == "hotspot") ? orange : red;
    
    for (int x = 112; x < 120; x++) {
        for (int y = 2; y < 8; y++) {
            canvas->SetPixel(x, y, wifi_color.r, wifi_color.g, wifi_color.b);
        }
    }
}

// Main display function
void draw_combined_screen(Canvas* canvas, float pm25, float pm10, float co2, 
                         const std::string& network_status, const std::string& wifi_status) {
    rgb_matrix::Color white(255, 255, 255);
    rgb_matrix::Color cyan(0, 255, 255);
    rgb_matrix::Color bg_color(0, 0, 0);
    
    rgb_matrix::Font title_font, value_font, small_font;
    
    // Load fonts
    if (!title_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf") ||
        !value_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/9x18B.bdf") ||
        !small_font.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf")) {
        std::cerr << "Error loading fonts!" << std::endl;
        return;
    }
    
    canvas->Clear();
    
    // Draw title
    rgb_matrix::DrawText(canvas, title_font, 2, title_font.baseline() + 2, cyan, &bg_color, "CO2 + PM + NET", 0);
    
    // Draw network indicators in top right
    draw_network_indicators(canvas, network_status, wifi_status);
    
    // Format sensor values
    std::stringstream ss_pm25, ss_pm10, ss_co2;
    ss_pm25 << std::fixed << std::setprecision(1) << pm25;
    ss_pm10 << std::fixed << std::setprecision(1) << pm10;
    ss_co2 << std::fixed << std::setprecision(0) << co2;
    
    std::string str_pm25 = ss_pm25.str();
    std::string str_pm10 = ss_pm10.str();
    std::string str_co2 = ss_co2.str();
    
    // Get colors and status for each measurement
    rgb_matrix::Color pm25_color = get_quality_color(pm25, "pm25");
    rgb_matrix::Color pm10_color = get_quality_color(pm10, "pm10");
    rgb_matrix::Color co2_color = get_quality_color(co2, "co2");
    
    std::string pm25_status = get_status_text(pm25, "pm25");
    std::string pm10_status = get_status_text(pm10, "pm10");
    std::string co2_status = get_status_text(co2, "co2");
    
    // Layout: 3 measurements with proper spacing to avoid overlap
    int y_start = 16;
    int y_spacing = 16;  // Increased spacing to prevent overlap
    
    // PM2.5 - using smaller positioning to fit everything
    rgb_matrix::DrawText(canvas, small_font, 2, y_start, cyan, &bg_color, "PM2.5:", 0);
    rgb_matrix::DrawText(canvas, small_font, 32, y_start, white, &bg_color, str_pm25.c_str(), 0);
    rgb_matrix::DrawText(canvas, small_font, 55, y_start, pm25_color, &bg_color, pm25_status.c_str(), 0);
    
    // PM10
    y_start += y_spacing;
    rgb_matrix::DrawText(canvas, small_font, 2, y_start, cyan, &bg_color, "PM10:", 0);
    rgb_matrix::DrawText(canvas, small_font, 32, y_start, white, &bg_color, str_pm10.c_str(), 0);
    rgb_matrix::DrawText(canvas, small_font, 55, y_start, pm10_color, &bg_color, pm10_status.c_str(), 0);
    
    // CO2
    y_start += y_spacing;
    rgb_matrix::DrawText(canvas, small_font, 2, y_start, cyan, &bg_color, "CO2:", 0);
    rgb_matrix::DrawText(canvas, small_font, 28, y_start, white, &bg_color, str_co2.c_str(), 0);
    rgb_matrix::DrawText(canvas, small_font, 55, y_start, co2_color, &bg_color, co2_status.c_str(), 0);
    
    // Draw network status text at bottom - simplified to fit
    y_start += y_spacing;
    std::string net_text = std::string("4G:") + (network_status == "connected" ? "OK" : "NO") + 
                          std::string(" WiFi:") + (wifi_status == "connected" ? "OK" : "NO");
    rgb_matrix::DrawText(canvas, small_font, 2, y_start, rgb_matrix::Color(150, 150, 150), &bg_color, net_text.c_str(), 0);
    
    // Draw timestamp
    auto now = std::chrono::system_clock::now();
    auto time_t = std::chrono::system_clock::to_time_t(now);
    auto tm = *std::localtime(&time_t);
    
    std::stringstream time_ss;
    time_ss << std::put_time(&tm, "%H:%M");
    
    rgb_matrix::DrawText(canvas, small_font, 100, 62, rgb_matrix::Color(100, 100, 100), &bg_color, 
                        time_ss.str().c_str(), 0);
}

int main(int argc, char *argv[]) {
    log("BLUE", "CO2 + PM + Network Display started");

    // Handle signals for graceful exit
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);

    // Initialize database connection
    sqlite3* db;
    std::cout << "Opening SQLite database at " << DB_PATH << std::endl;
    int rc = sqlite3_open(DB_PATH.c_str(), &db);
    if (rc) {
        std::cerr << "Error opening SQLite database: " << sqlite3_errmsg(db) << std::endl;
        sqlite3_close(db);
        return 1;
    }

    // Initialize LED matrix
    log("BLUE", "Initializing LED matrix...");
    RGBMatrix::Options defaults;
    defaults.hardware_mapping = "moduleair_pinout";
    defaults.rows = 64;
    defaults.cols = 128;
    defaults.chain_length = 1;
    defaults.parallel = 1;
    defaults.row_address_type = 3;
    defaults.show_refresh_rate = false;
    defaults.brightness = 100;
    defaults.pwm_bits = 1;
    defaults.panel_type = "FM6126A";
    defaults.disable_hardware_pulsing = false;

    rgb_matrix::RuntimeOptions runtime_opt;
    runtime_opt.gpio_slowdown = 4;
    runtime_opt.daemon = 0;
    runtime_opt.drop_privileges = 0;

    Canvas *canvas = RGBMatrix::CreateFromOptions(defaults, runtime_opt);
    if (canvas == NULL) {
        std::cerr << "Error creating LED matrix canvas" << std::endl;
        sqlite3_close(db);
        return 1;
    }
    log("GREEN", "LED matrix initialized successfully");

    // Main loop
    log("BLUE", "Starting display loop");
    while (running) {
        float pm25 = 0, pm10 = 0, co2 = 0;
        std::string network_status, wifi_status;
        
        // Get sensor data
        bool sensor_success = get_sensor_data(db, pm25, pm10, co2);
        bool network_success = get_network_status(db, network_status, wifi_status);
        
        if (!sensor_success) {
            std::cerr << "Error retrieving sensor data from database" << std::endl;
        }
        
        if (!network_success) {
            std::cerr << "Error retrieving network data from database" << std::endl;
            network_status = "unknown";
            wifi_status = "unknown";
        }
        
        // Draw the combined screen
        draw_combined_screen(canvas, pm25, pm10, co2, network_status, wifi_status);
        
        // Sleep before next update
        std::cout << "Update complete, sleeping for 5 seconds..." << std::endl;
        for (int i = 0; i < 5 && running; i++) {
            sleep(1);
        }
    }

    // Clean up
    std::cout << "Program terminating, cleaning up..." << std::endl;
    canvas->Clear();
    delete canvas;
    sqlite3_close(db);
    std::cout << "CO2 + PM + Network Display terminated" << std::endl;
    return 0;
}