using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace MonitoraggioAmbientale
{
    // ──────────────────────────────────────────────
    // Classi dati (semplici contenitori, nessuna logica)
    // ──────────────────────────────────────────────

    class Rilevazione
    {
        public string   IdCentralina { get; set; }
        public DateTime Timestamp    { get; set; }
        public double   PM10         { get; set; }
        public double   PM25         { get; set; }
        public double   CO2          { get; set; }
        public double   NO2          { get; set; }
        public double   Rumore       { get; set; }
    }

    class Centralina
    {
        public string Id        { get; set; }
        public string Posizione { get; set; }
        public string Indirizzo { get; set; }
        public int    Intervallo { get; set; }   // secondi
    }

    // ──────────────────────────────────────────────
    // Programma principale
    // ──────────────────────────────────────────────

    class Program
    {
        // Risorse condivise
        private static readonly List<Rilevazione> _rilevazioni = new List<Rilevazione>();
        private static readonly Random            _random      = new Random();
        private static readonly object            _lock        = new object();

        // Flag di terminazione (volatile: visibile subito a tutti i thread)
        private static volatile bool _running = true;

        // Soglie di alert
        private const double SOGLIA_PM10   = 50.0;
        private const double SOGLIA_PM25   = 25.0;
        private const double SOGLIA_CO2    = 1000.0;
        private const double SOGLIA_NO2    = 40.0;
        private const double SOGLIA_RUMORE = 70.0;

        // ── Definizione delle 5 centraline ──────────────────────────
        private static readonly Centralina[] Centraline =
        {
            new Centralina { Id="C1", Posizione="Piazza Repubblica",  Indirizzo="P.za Repubblica 1",     Intervallo=3 },
            new Centralina { Id="C2", Posizione="Viale Borri",        Indirizzo="Viale Borri 120",        Intervallo=5 },
            new Centralina { Id="C3", Posizione="Via Caracciolo",     Indirizzo="Via Caracciolo 7",       Intervallo=4 },
            new Centralina { Id="C4", Posizione="Parco Mirabello",    Indirizzo="Via Sanvito Silvestro",  Intervallo=8 },
            new Centralina { Id="C5", Posizione="Stazione FS",        Indirizzo="P.za Trento 1",          Intervallo=6 },
        };

        // ── Entry point ──────────────────────────────────────────────
        static void Main(string[] args)
        {
            Console.WriteLine("=== Sistema di Monitoraggio Ambientale — Varese ===");
            Console.WriteLine($"Simulazione avviata alle {DateTime.Now:HH:mm:ss} (durata 60 s)\n");

            // Creare e avviare un thread per ogni centralina
            Thread[] threads = new Thread[Centraline.Length];
            for (int i = 0; i < Centraline.Length; i++)
            {
                Centralina c = Centraline[i];          // closure sicura
                threads[i] = new Thread(() => CicloCentralina(c))
                {
                    Name         = c.Id,
                    IsBackground = true
                };
                threads[i].Start();
            }

            // Il main attende 60 secondi, poi segnala la fine
            Thread.Sleep(60_000);
            _running = false;

            // Attendere che tutti i thread terminino prima del report
            foreach (Thread t in threads)
                t.Join();

            // Stampare il report finale (nessun lock necessario: lista definitiva)
            StampaReport();
        }

        // ── Ciclo di rilevamento di una centralina ───────────────────
        static void CicloCentralina(Centralina centralina)
        {
            while (_running)
            {
                // 1. Generare la rilevazione con valori casuali realistici
                Rilevazione r = GeneraRilevazione(centralina.Id);

                // 2. Controllare quali parametri superano la soglia
                var superati = new List<(string Nome, double Valore, double Soglia, string Unita)>();
                if (r.PM10   > SOGLIA_PM10)   superati.Add(("PM10",   r.PM10,   SOGLIA_PM10,   "µg/m³"));
                if (r.PM25   > SOGLIA_PM25)   superati.Add(("PM2.5",  r.PM25,   SOGLIA_PM25,   "µg/m³"));
                if (r.CO2    > SOGLIA_CO2)    superati.Add(("CO2",    r.CO2,    SOGLIA_CO2,    "ppm"));
                if (r.NO2    > SOGLIA_NO2)    superati.Add(("NO2",    r.NO2,    SOGLIA_NO2,    "µg/m³"));
                if (r.Rumore > SOGLIA_RUMORE) superati.Add(("Rumore", r.Rumore, SOGLIA_RUMORE, "dB"));

                // 3. Aggiungere alla lista e stampare (tutto dentro lo stesso lock)
                lock (_lock)
                {
                    _rilevazioni.Add(r);

                    ConsoleColor coloreOriginale = Console.ForegroundColor;

                    if (superati.Count > 0)
                    {
                        // ── ALERT ──────────────────────────────────────
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine($"⚠ ALERT [{r.Timestamp:HH:mm:ss}] " +
                                          $"{centralina.Id} - {centralina.Posizione} ({centralina.Indirizzo})");
                        foreach (var (nome, valore, soglia, unita) in superati)
                            Console.WriteLine($"   → {nome} = {valore:F1} {unita} (soglia {soglia}) SUPERATA");
                    }
                    else
                    {
                        // ── Rilevazione normale ────────────────────────
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine($"[{r.Timestamp:HH:mm:ss}] {centralina.Id} {centralina.Posizione,-20} " +
                                          $"PM10={r.PM10:F1} PM2.5={r.PM25:F1} " +
                                          $"CO2={r.CO2:F0} NO2={r.NO2:F1} Rumore={r.Rumore:F1}");
                    }

                    Console.ForegroundColor = coloreOriginale;
                }

                // 4. Attendere l'intervallo specifico di questa centralina
                Thread.Sleep(centralina.Intervallo * 1000);
            }
        }

        // ── Generazione di una rilevazione casuale realistica ────────
        static Rilevazione GeneraRilevazione(string idCentralina)
        {
            lock (_lock)
            {
                bool picco = _random.NextDouble() < 0.10;   // 10 % picco, 90 % normale

                return new Rilevazione
                {
                    IdCentralina = idCentralina,
                    Timestamp    = DateTime.Now,
                    PM10         = picco ? ValorePicco(50, 80)   : ValoreNormale(0,   50),
                    PM25         = picco ? ValorePicco(25, 40)   : ValoreNormale(0,   25),
                    CO2          = picco ? ValorePicco(1000,1200): ValoreNormale(400, 1000),
                    NO2          = picco ? ValorePicco(40, 60)   : ValoreNormale(0,   40),
                    Rumore       = picco ? ValorePicco(70, 90)   : ValoreNormale(40,  70),
                };
            }
        }

        // Helper: valore nel range normale
        static double ValoreNormale(double min, double max) =>
            min + _random.NextDouble() * (max - min);

        // Helper: valore nel range di picco
        static double ValorePicco(double min, double max) =>
            min + _random.NextDouble() * (max - min);

        // ── Report finale ────────────────────────────────────────────
        static void StampaReport()
        {
            Console.WriteLine("\n========== REPORT FINALE ==========");
            Console.WriteLine($"Rilevazioni totali: {_rilevazioni.Count}");

            foreach (Centralina c in Centraline)
            {
                List<Rilevazione> dati = _rilevazioni
                    .Where(r => r.IdCentralina == c.Id)
                    .ToList();

                int effettive = dati.Count;
                int attese    = 60 / c.Intervallo;

                Console.WriteLine($"\n--- {c.Id} | {c.Posizione} | {c.Indirizzo} ---");
                Console.WriteLine($"  Rilevazioni: {effettive} (attese: {attese}, ogni {c.Intervallo}s)");

                if (effettive == 0)
                {
                    Console.WriteLine("  Nessuna rilevazione registrata.");
                    continue;
                }

                // Statistiche per ogni parametro
                int alertTotali = 0;

                alertTotali += StampaStatisticaParametro("PM10  ",
                    dati.Select(r => r.PM10).ToList(),   SOGLIA_PM10,   "µg/m³");
                alertTotali += StampaStatisticaParametro("PM2.5 ",
                    dati.Select(r => r.PM25).ToList(),   SOGLIA_PM25,   "µg/m³");
                alertTotali += StampaStatisticaParametro("CO2   ",
                    dati.Select(r => r.CO2).ToList(),    SOGLIA_CO2,    "ppm");
                alertTotali += StampaStatisticaParametro("NO2   ",
                    dati.Select(r => r.NO2).ToList(),    SOGLIA_NO2,    "µg/m³");
                alertTotali += StampaStatisticaParametro("Rumore",
                    dati.Select(r => r.Rumore).ToList(), SOGLIA_RUMORE, "dB");

                Console.WriteLine($"  Alert totali: {alertTotali}");
            }

            Console.WriteLine("\n====================================");
        }

        // Stampa le statistiche di un singolo parametro e restituisce il conteggio alert
        static int StampaStatisticaParametro(string nome, List<double> valori, double soglia, string unita)
        {
            double min   = valori[0];
            double max   = valori[0];
            double somma = 0;
            int    alert = 0;

            foreach (double v in valori)
            {
                if (v < min) min = v;
                if (v > max) max = v;
                somma += v;
                if (v > soglia) alert++;
            }

            double media = somma / valori.Count;

            Console.WriteLine($"  {nome}: min={min,6:F1} max={max,6:F1} media={media,6:F1} alert={alert}");
            return alert;
        }
    }
}
