using System.Text;

namespace Thread_Centraline
{
    internal class Program
    {
        private static List<Rilevazione> rilevazioni = new List<Rilevazione>();
        private static List<Centralina> centraline = new List<Centralina>();

        private static readonly object lockConsole = new object();

        static void Main(string[] args)
        {
            Console.OutputEncoding = Encoding.UTF8;

            CancellationTokenSource cts = new CancellationTokenSource();

            centraline.Add(new Centralina("C1", "P.za Repubblica 1", 3));
            centraline.Add(new Centralina("C2", "Viale Borri 120", 5));
            centraline.Add(new Centralina("C3", "Via Caracciolo 7", 4));
            centraline.Add(new Centralina("C4", "Via Sanvito Silvestro", 8));
            centraline.Add(new Centralina("C5", "P.za Trento 1", 6));

            List<Thread> threads = new List<Thread>();

            foreach (Centralina c in centraline)
            {
                threads.Add(new Thread(_ => LavoroCentralina(c, cts.Token)));
            }

            foreach (Thread t in threads)
            {
                t.IsBackground = true;
                t.Start();
            }

            Thread.Sleep(30 * 1000);

            cts.Cancel();

            foreach (Thread t in threads)
                t.Join();

            ReportFinale();
        }

        static void LavoroCentralina(Centralina c, CancellationToken ct)
        {
            while (!ct.IsCancellationRequested)
            {
                DateTime timestamp = DateTime.Now;
                Rilevazione v = new Rilevazione(c.Id);
                
                lock (lockConsole)
                {
                    rilevazioni.Add(v);
                    if (v.PM10 > Soglie.SogliaPM10 ||
                        v.PM25 > Soglie.SogliaPM25 ||
                        v.CO2 > Soglie.SogliaCO2 ||
                        v.NO2 > Soglie.SogliaNO2 ||
                        v.Rumore > Soglie.SogliaRumore)
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("⚠ ALERT [{0}] {1} - {2}", timestamp.ToShortTimeString(), c.Id, c.Indirizzo);

                        if (v.PM10 > Soglie.SogliaPM10)
                            Console.WriteLine("  → PM10 = {0:0.00} μg/m³\t(soglia {1})\tSUPERATA", v.PM10, Soglie.SogliaPM10);
                        if (v.PM25 > Soglie.SogliaPM25)
                            Console.WriteLine("  → PM25 = {0:0.00} μg/m³\t(soglia {1})\tSUPERATA", v.PM25, Soglie.SogliaPM25);
                        if (v.CO2 > Soglie.SogliaCO2)
                            Console.WriteLine("  → CO2 = {0:0.00} ppm\t(soglia {1})\tSUPERATA", v.CO2, Soglie.SogliaCO2);
                        if (v.NO2 > Soglie.SogliaNO2)
                            Console.WriteLine("  → NO2 = {0:0.00} μg/m³\t(soglia {1})\tSUPERATA", v.NO2, Soglie.SogliaNO2);
                        if (v.Rumore > Soglie.SogliaRumore)
                            Console.WriteLine("  → Rumore = {0:0.00} dB³\t(soglia {1})\tSUPERATA", v.Rumore, Soglie.SogliaRumore);
                        Console.ForegroundColor = ConsoleColor.White;
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine("[{0}] {1} - {2}", timestamp.ToShortTimeString(), c.Id, c.Indirizzo);
                        Console.ForegroundColor = ConsoleColor.White;
                    }
                }

                Thread.Sleep(c.Intervallo * 1000);
            }
        }

        static void ReportFinale()
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("========== REPORT FINALE ==========");
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("Rilevazioni totali: {0}\n", rilevazioni.Count);

            foreach (Centralina c in centraline)
            {
                Console.ForegroundColor = ConsoleColor.Blue;
                Console.WriteLine($"--- {c.Id} | {c.Indirizzo} ---");
                Console.ForegroundColor = ConsoleColor.White;

                List<Rilevazione> rilevazioniC = rilevazioni.FindAll(x => x.IdCentralina == c.Id);

                Console.WriteLine("  Rilevazioni: {0}", rilevazioniC.Count);

                Console.Write("  PM10\t:\t");
                ReportRilevazioni(rilevazioniC.Select(x => x.PM10), Soglie.SogliaPM10);

                Console.Write("  PM2.5\t:\t");
                ReportRilevazioni(rilevazioniC.Select(x => x.PM25), Soglie.SogliaPM25);

                Console.Write("  CO2\t:\t");
                ReportRilevazioni(rilevazioniC.Select(x => x.CO2), Soglie.SogliaCO2);

                Console.Write("  NO2\t:\t");
                ReportRilevazioni(rilevazioniC.Select(x => x.NO2), Soglie.SogliaNO2);

                Console.Write(" Rumore\t:\t");
                ReportRilevazioni(rilevazioniC.Select(x => x.Rumore), Soglie.SogliaRumore);

                Console.ForegroundColor = ConsoleColor.Red;
                int alertTot = 0;
                rilevazioniC.ForEach(x =>
                {
                    if (x.PM10 > Soglie.SogliaPM10)
                        alertTot++;
                    if (x.PM25 > Soglie.SogliaPM25)
                        alertTot++;
                    if (x.CO2 > Soglie.SogliaCO2)
                        alertTot++;
                    if (x.NO2 > Soglie.SogliaNO2)
                        alertTot++;
                    if (x.Rumore > Soglie.SogliaRumore)
                        alertTot++;
                });
                Console.WriteLine("  Alert totali: {0}\n", alertTot);
            }
        }

        private static void ReportRilevazioni(IEnumerable<double> r, int soglia)
        {
            if (!r.Any())
            {
                Console.WriteLine("N/A (Nessun dato raccolto)");
                return;
            }

            double min = r.Min();
            double max = r.Max();
            double med = r.Average();

            int nAlert = r.Count(x => x > soglia);

            Console.WriteLine($"min={min:0.00}\tmax={max:0.00}\tmedia={med:0.00}\talert={nAlert}");
        }
    }
}
