using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using NcProgramManager.Cnc;
using NcProgramManager.Cnc.Models;
using NcProgramManager.Licensing;

namespace NcProgramManager
{
    internal class Program
    {
        private static InputArgs inputArgs = new InputArgs();

        public static int Main(string[] args)
        {
            try
            {
                parseArgomenti(args);
            }
            catch (Exception e)
            {
                Console.Write("Gestore Programmi FANUC: ");
                Console.WriteLine(e.Message);
                Console.WriteLine("Errore parametri input. Consulta `-help' per maggiori informazioni.");
                vediErrore(-101);
                return -101;
            }

            if (inputArgs.mostraHelp)
            {
                MostraAiuto();
                vediErrore(0);
                return 0;
            }

            string _licFingerprint = new MachineFingerprint().GetFingerprint();
            string _licBase64;
            string _licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic");
            if (!LicenseFile.TryRead(_licPath, out _licBase64)
                || !new LicenseValidator().Validate(_licFingerprint, _licBase64))
            {
                Console.WriteLine("Licenza ERRATA");
                vediErrore(-100);
                return -1;
            }

            // Validate required params
            bool needsIp = inputArgs.manufacturer != CncManufacturer.Fanuc || inputArgs.nodoHssb == -1;
            if (inputArgs.azione == "" || (needsIp && string.IsNullOrEmpty(inputArgs.ip)) || inputArgs.pathLocaleProgramma == "")
            {
                vediErrore(-102);
                return -102;
            }

            Console.WriteLine(
                "manufacturer: " + inputArgs.manufacturer + "\n" +
                "IndirizzoIP: " + inputArgs.ip + "\n" +
                "Porta: " + inputArgs.porta + "\n" +
                "Compatibility: " + inputArgs.compatibilityMode + "\n" +
                "commentoProgramma: " + inputArgs.commentoProgramma + "\n" +
                "pathCNC: " + inputArgs.pathCNC + "\n" +
                "pathLocaleProgramma: " + inputArgs.pathLocaleProgramma + "\n"
            );

            ICncMachine machine = null;
            try
            {
                machine = CncMachineFactory.Create(inputArgs);

                switch (inputArgs.azione.ToUpperInvariant())
                {
                    case "SCARICA": return Scarica(machine);
                    case "INVIA":   return Invia(machine);
                    default:
                        vediErrore(-103);
                        return -103;
                }
            }
            finally
            {
                if (machine != null)
                    machine.Dispose();
            }
        }

        static int Scarica(ICncMachine macchina)
        {
            Console.WriteLine("RICEVI DA CNC-->");
            try
            {
                var connResult = macchina.ConnectAsync().GetAwaiter().GetResult();
                if (!connResult.Success)
                {
                    Console.WriteLine("Macchina non connessa");
                    vediErrore(-201);
                    return -201;
                }

                Console.WriteLine("Inizio RICEZIONE NCProgram");
                var progResult = macchina.ReadProgramAsync(inputArgs.pathCNC.ToString()).GetAwaiter().GetResult();
                Console.WriteLine("Fine RICEZIONE NCProgram");
                if (!progResult.Success || string.IsNullOrEmpty(progResult.Value))
                {
                    macchina.DisconnectAsync().GetAwaiter().GetResult();
                    vediErrore(-204);
                    return -204;
                }

                FanucProgram programma = new FanucProgram(progResult.Value);

                IFanucMachine fanuc = macchina as IFanucMachine;

                if (inputArgs.hasToolOffsetData)
                {
                    if (fanuc == null)
                    {
                        Console.WriteLine("ToolOffset non supportato per questo produttore");
                        macchina.DisconnectAsync().GetAwaiter().GetResult();
                        vediErrore(-205);
                        return -205;
                    }
                    Console.WriteLine("Inizio RICEZIONE ToolOffsetData");
                    var toolResult = fanuc.ReadToolDataAsync(inputArgs.pathCNC.ToString()).GetAwaiter().GetResult();
                    Console.WriteLine("Fine RICEZIONE ToolOffsetData");
                    if (!toolResult.Success || string.IsNullOrEmpty(toolResult.Value))
                    {
                        macchina.DisconnectAsync().GetAwaiter().GetResult();
                        vediErrore(-205);
                        return -205;
                    }
                    programma.setValue(toolResult.Value, FanucProgram.FILE_TYPE.TOOL_OFFEST_DATA);
                }

                if (inputArgs.hasWorkZeroOffsetData)
                {
                    if (fanuc == null)
                    {
                        Console.WriteLine("WorkZeroOffset non supportato per questo produttore");
                        macchina.DisconnectAsync().GetAwaiter().GetResult();
                        vediErrore(-206);
                        return -206;
                    }
                    Console.WriteLine("Inizio RICEZIONE WorkZeroOffsetData");
                    var workResult = fanuc.ReadWorkZeroDataAsync(inputArgs.pathCNC.ToString()).GetAwaiter().GetResult();
                    Console.WriteLine("Fine RICEZIONE WorkZeroOffsetData");
                    if (!workResult.Success || string.IsNullOrEmpty(workResult.Value))
                    {
                        macchina.DisconnectAsync().GetAwaiter().GetResult();
                        vediErrore(-206);
                        return -206;
                    }
                    programma.setValue(workResult.Value, FanucProgram.FILE_TYPE.WORK_ZERO_OFFSET);
                }

                macchina.DisconnectAsync().GetAwaiter().GetResult();
                File.WriteAllText(inputArgs.pathLocaleProgramma, programma.ToString());
                vediErrore(0);
                return 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                vediErrore(-202);
                return -202;
            }
        }

        static int Invia(ICncMachine macchina)
        {
            Console.WriteLine("INVIA A CNC-->");
            try
            {
                var connResult = macchina.ConnectAsync().GetAwaiter().GetResult();
                if (!connResult.Success)
                {
                    Console.WriteLine("Macchina non connessa");
                    vediErrore(-201);
                    return -201;
                }

                FanucProgram programma = new FanucProgram(File.ReadAllText(inputArgs.pathLocaleProgramma));
                if (!string.IsNullOrEmpty(inputArgs.commentoProgramma))
                    programma.ChangeComment(inputArgs.commentoProgramma);

                if (programma.Program != null)
                {
                    Console.WriteLine("Inizio INVIO NCProgram");
                    var cncProg = new CncProgram
                    {
                        Name    = programma.GetProgramTitle(),
                        Comment = programma.GetProgramComment(),
                        Content = programma.Program,
                        Path    = inputArgs.pathCNC.ToString()
                    };
                    var writeResult = macchina.WriteProgramAsync(inputArgs.pathCNC.ToString(), cncProg).GetAwaiter().GetResult();
                    Console.WriteLine("Fine INVIO NCProgram");
                    if (!writeResult.Success)
                    {
                        macchina.DisconnectAsync().GetAwaiter().GetResult();
                        vediErrore(-204);
                        return -204;
                    }
                }

                IFanucMachine fanuc = macchina as IFanucMachine;

                if (programma.ToolOffsetData != null && inputArgs.hasToolOffsetData)
                {
                    if (fanuc == null)
                    {
                        Console.WriteLine("ToolOffset non supportato per questo produttore");
                        macchina.DisconnectAsync().GetAwaiter().GetResult();
                        vediErrore(-205);
                        return -205;
                    }
                    Console.WriteLine("Inizio INVIO ToolOffsetData");
                    var toolResult = fanuc.WriteToolDataAsync(inputArgs.pathCNC.ToString(), programma.ToolOffsetData).GetAwaiter().GetResult();
                    Console.WriteLine("Fine INVIO ToolOffsetData");
                    if (!toolResult.Success)
                    {
                        macchina.DisconnectAsync().GetAwaiter().GetResult();
                        vediErrore(-205);
                        return -205;
                    }
                }

                if (programma.WorkZeroOffsetData != null && inputArgs.hasWorkZeroOffsetData)
                {
                    if (fanuc == null)
                    {
                        Console.WriteLine("WorkZeroOffset non supportato per questo produttore");
                        macchina.DisconnectAsync().GetAwaiter().GetResult();
                        vediErrore(-206);
                        return -206;
                    }
                    Console.WriteLine("Inizio INVIO WorkZeroOffsetData");
                    var workResult = fanuc.WriteWorkZeroDataAsync(inputArgs.pathCNC.ToString(), programma.WorkZeroOffsetData).GetAwaiter().GetResult();
                    Console.WriteLine("Fine INVIO WorkZeroOffsetData");
                    if (!workResult.Success)
                    {
                        macchina.DisconnectAsync().GetAwaiter().GetResult();
                        vediErrore(-206);
                        return -206;
                    }
                }

                macchina.DisconnectAsync().GetAwaiter().GetResult();
                vediErrore(0);
                return 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                vediErrore(-202);
                return -202;
            }
        }

        static void vediErrore(int errore)
        {
            Console.WriteLine("Errore:" + errore);
            Console.WriteLine();
            Console.WriteLine("Codici Errori:");
            Console.WriteLine("-100 : Licenza Errata/non valida.");
            Console.WriteLine("-101 : Impossibile parsare i parametri.");
            Console.WriteLine("-102 : Parametri mancanti. ");
            Console.WriteLine("-103 : Codice connessione errato: 3->Ethernet 2->HSSB. ");
            Console.WriteLine("-201 : Macchina non connessa. ");
            Console.WriteLine("-202 : Eccezione nell'esecuzione dell'AZIONE. ");
            Console.WriteLine("-203 : L'azione è fallita. ");
            Console.WriteLine("-204 : Errore in fase invio/ricezione Programma CN. ");
            Console.WriteLine("-205 : Errore in fase invio/ricezione tool offset data ");
            Console.WriteLine("-206 : Errore in fase invio/ricezione work zero offest data");
            Console.WriteLine("-207 : Posizionare il CN in MDI o EDIT per eseguire l'invio di un programma");
            Console.WriteLine("-208 : Programma CN Caricato ma non settato a main.");
            if (inputArgs.debugMode)
                Console.ReadLine();
        }

        static void MostraAiuto()
        {
            Console.WriteLine("Utilizzo del Software gestore Programmi FANUC");
            Console.WriteLine("Le funzioni pricipali sono due.");
            Console.WriteLine("INVIA: caricamento di programma su CNC.");
            Console.WriteLine("SCARICA: scaricamento di programma su CNC.");
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Lista dei parametri da fornire all'eseguibile\nDevono essere separati da uno spazio tra l'uno e l'altro,\nnon andare a capo e non lasciare spazi dopo l'= ");
            Console.WriteLine();
            Console.WriteLine("Il file 'license.lic' deve essere posizionato nella stessa cartella dell'eseguibile.");
            Console.WriteLine("-azione=");
            Console.WriteLine("    l'{AZIONE} da eseguire.");
            Console.WriteLine("-manufacturer=");
            Console.WriteLine("    Il produttore del CNC: fanuc (default), heidenhain, siemens, mitsubishi");
            Console.WriteLine("-tipo=");
            Console.WriteLine("    Il tipo di connessione utilizzato dalla macchina: 3 per ethernet\n 2 per HSSB (solo Fanuc, legacy)");
            Console.WriteLine("-ip=");
            Console.WriteLine("    Dato per Ethernet indirizzo IP della macchina");
            Console.WriteLine("-porta=");
            Console.WriteLine("    Dato per Ethernet PORTA di connessione");
            Console.WriteLine("-nodo=");
            Console.WriteLine("    Dato per HSSB Nodo di connesione (solo Fanuc)");
            Console.WriteLine("-username=");
            Console.WriteLine("    Username per autenticazione (Heidenhain/Siemens)");
            Console.WriteLine("-password=");
            Console.WriteLine("    Password per autenticazione (Heidenhain/Siemens)");
            Console.WriteLine("-compatibility");
            Console.WriteLine("    Attivazione modalità compatibilità");
            Console.WriteLine("-commento=");
            Console.WriteLine("    Commento da applicare al programma scelto");
            Console.WriteLine("-path=");
            Console.WriteLine("    Path dei programmi del CNC dove vengono salvati i programmi, se non definito è impostato a 1 cioè //CNC_MEM/USER/PATH1/");
            Console.WriteLine("-pathprogramma=");
            Console.WriteLine("    Necessario per INVIO: Path del programma locale da caricare sul CNC");
            Console.WriteLine("-workzero=");
            Console.WriteLine("    Invia/Scarica anche i dati relativi al workzero offset");
            Console.WriteLine("-tooloffset=");
            Console.WriteLine("    Invia/Scarica anche i dati relativi al tool offset");
            Console.WriteLine("-debug=");
            Console.WriteLine("    Mantiene il terminale aperto alla fine delle operazioni");
            Console.WriteLine("-help");
            Console.WriteLine("    Mostra questo messaggio e termina");
            Console.WriteLine();
            Console.WriteLine("Codici Errori:");
            Console.WriteLine("-100 : Licenza Errata/non valida.");
            Console.WriteLine("-101 : Impossibile parsare i parametri.");
            Console.WriteLine("-102 : Parametri mancanti. ");
            Console.WriteLine("-103 : Codice connessione errato: 3->Ethernet 2->HSSB. ");
            Console.WriteLine("-201 : Macchina non connessa. ");
            Console.WriteLine("-202 : Eccezione nell'esecuzione dell'AZIONE. ");
            Console.WriteLine("-203 : L'azione è fallita. ");
            Console.WriteLine("-204 : Errore in fase invio/ricezione Programma CN. ");
            Console.WriteLine("-205 : Errore in fase invio/ricezione tool offset data ");
            Console.WriteLine("-206 : Errore in fase invio/ricezione work zero offest data");
            Console.WriteLine("-207 : Posizionare il CN in MDI o EDIT per eseguire l'invio di un programma");
            Console.WriteLine("-208 : Programma CN Caricato ma non settato a main.");
        }

        private static void parseArgomenti(string[] args)
        {
            List<string> arguments = new List<string>(args);
            var help = arguments.Find(it => new Regex("-help.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(help))
            {
                inputArgs.mostraHelp = true;
                return;
            }
            var azione = arguments.Find(it => new Regex("-azione=.*", RegexOptions.IgnoreCase).IsMatch(it));
            inputArgs.azione = azione.Substring(8);
            var manufacturer = arguments.Find(it => new Regex("-manufacturer=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(manufacturer))
            {
                string mfgVal = manufacturer.Substring(14).Trim().ToLowerInvariant();
                switch (mfgVal)
                {
                    case "heidenhain":  inputArgs.manufacturer = CncManufacturer.Heidenhain;  break;
                    case "siemens":     inputArgs.manufacturer = CncManufacturer.Siemens;     break;
                    case "mitsubishi":  inputArgs.manufacturer = CncManufacturer.Mitsubishi;  break;
                    default:            inputArgs.manufacturer = CncManufacturer.Fanuc;       break;
                }
            }
            string tipo = arguments.Find(it => new Regex("-tipo=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(tipo))
                inputArgs.tipo = Int32.Parse(tipo.Substring(6));
            else inputArgs.tipo = 3;
            var ip = arguments.Find(it => new Regex("-ip=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(ip))
                inputArgs.ip = ip.Substring(4);
            var porta = arguments.Find(it => new Regex("-porta=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(porta))
                inputArgs.porta = porta.Substring(7);
            var nodo = arguments.Find(it => new Regex("-nodo=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(nodo))
                inputArgs.nodoHssb = Int32.Parse(nodo.Substring(6));
            else inputArgs.nodoHssb = -1;
            var username = arguments.Find(it => new Regex("-username=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(username))
                inputArgs.username = username.Substring(10);
            var password = arguments.Find(it => new Regex("-password=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(password))
                inputArgs.password = password.Substring(10);
            var compatibility = arguments.Find(it => new Regex("-compatibility.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(compatibility))
                inputArgs.compatibilityMode = true;
            var commento = arguments.Find(it => new Regex("-commento=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(commento))
                inputArgs.commentoProgramma = commento.Substring(10);
            var path = arguments.Find(it => new Regex("-path=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(path))
                inputArgs.pathCNC = Int16.Parse(path.Substring(6));
            var pathprogramma = arguments.Find(it => new Regex("-pathprogramma=.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(pathprogramma))
                inputArgs.pathLocaleProgramma = pathprogramma.Substring(15);
            var hasToolOffestData = arguments.Find(it => new Regex("-tooloffset.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(hasToolOffestData))
                inputArgs.hasToolOffsetData = true;
            var hasWorkZeroOffsetData = arguments.Find(it => new Regex("-workzero.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(hasWorkZeroOffsetData))
                inputArgs.hasWorkZeroOffsetData = true;
            var debugMode = arguments.Find(it => new Regex("-debug.*", RegexOptions.IgnoreCase).IsMatch(it));
            if (!string.IsNullOrEmpty(debugMode))
                inputArgs.debugMode = true;
        }
    }
}
