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

namespace NcProgramManager
{
    internal class Program
    {
        public static int Main(string[] args)
        {
            InputArgs inputArgs;
            try
            {
                inputArgs = ArgParser.Parse(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, false);
                return -101;
            }

            if (inputArgs.mostraHelp)
            {
                MostraAiuto();
                VediErrore(0, inputArgs.debugMode);
                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, inputArgs.debugMode);
                return -1;
            }

            bool needsIp = inputArgs.manufacturer != CncManufacturer.Fanuc || inputArgs.nodoHssb == -1;
            if (inputArgs.azione == "" || (needsIp && string.IsNullOrEmpty(inputArgs.ip)) || inputArgs.pathLocaleProgramma == "")
            {
                VediErrore(-102, inputArgs.debugMode);
                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, inputArgs);
                    case "INVIA":   return Invia(machine, inputArgs);
                    default:
                        VediErrore(-103, inputArgs.debugMode);
                        return -103;
                }
            }
            finally
            {
                if (machine != null)
                    machine.Dispose();
            }
        }

        static int Scarica(ICncMachine macchina, InputArgs inputArgs)
        {
            Console.WriteLine("RICEVI DA CNC-->");
            bool connected = false;
            try
            {
                var connResult = macchina.ConnectAsync().GetAwaiter().GetResult();
                if (!connResult.Success)
                {
                    Console.WriteLine("Macchina non connessa");
                    PrintErrors(connResult.Errors);
                    VediErrore(-201, inputArgs.debugMode);
                    return -201;
                }
                connected = true;

                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))
                {
                    PrintErrors(progResult.Errors);
                    VediErrore(-204, inputArgs.debugMode);
                    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");
                        VediErrore(-205, inputArgs.debugMode);
                        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))
                    {
                        PrintErrors(toolResult.Errors);
                        VediErrore(-205, inputArgs.debugMode);
                        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");
                        VediErrore(-206, inputArgs.debugMode);
                        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))
                    {
                        PrintErrors(workResult.Errors);
                        VediErrore(-206, inputArgs.debugMode);
                        return -206;
                    }
                    programma.setValue(workResult.Value, FanucProgram.FILE_TYPE.WORK_ZERO_OFFSET);
                }

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

        static int Invia(ICncMachine macchina, InputArgs inputArgs)
        {
            Console.WriteLine("INVIA A CNC-->");
            bool connected = false;
            try
            {
                var connResult = macchina.ConnectAsync().GetAwaiter().GetResult();
                if (!connResult.Success)
                {
                    Console.WriteLine("Macchina non connessa");
                    PrintErrors(connResult.Errors);
                    VediErrore(-201, inputArgs.debugMode);
                    return -201;
                }
                connected = true;

                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)
                    {
                        PrintErrors(writeResult.Errors);
                        VediErrore(-204, inputArgs.debugMode);
                        return -204;
                    }
                }

                IFanucMachine fanuc = macchina as IFanucMachine;

                if (programma.ToolOffsetData != null && inputArgs.hasToolOffsetData)
                {
                    if (fanuc == null)
                    {
                        Console.WriteLine("ToolOffset non supportato per questo produttore");
                        VediErrore(-205, inputArgs.debugMode);
                        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)
                    {
                        PrintErrors(toolResult.Errors);
                        VediErrore(-205, inputArgs.debugMode);
                        return -205;
                    }
                }

                if (programma.WorkZeroOffsetData != null && inputArgs.hasWorkZeroOffsetData)
                {
                    if (fanuc == null)
                    {
                        Console.WriteLine("WorkZeroOffset non supportato per questo produttore");
                        VediErrore(-206, inputArgs.debugMode);
                        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)
                    {
                        PrintErrors(workResult.Errors);
                        VediErrore(-206, inputArgs.debugMode);
                        return -206;
                    }
                }

                VediErrore(0, inputArgs.debugMode);
                return 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                VediErrore(-202, inputArgs.debugMode);
                return -202;
            }
            finally
            {
                if (connected)
                    macchina.DisconnectAsync().GetAwaiter().GetResult();
            }
        }

        static void PrintErrors(IReadOnlyList<CncError> errors)
        {
            if (errors == null) return;
            foreach (var e in errors)
                Console.WriteLine("  " + e.ToString());
        }

        static void VediErrore(int errore, bool debugMode)
        {
            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 (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.");
        }
    }
}
