using ArcGIS.Desktop.Framework.Threading.Tasks;
using OliviaAddIn;
using OliviaAddInPro.Model;
using OliviaAddInPro.Model.contract;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace OliviaAddInPro.Services
{
    public class ProcesoEjecServ
    {
        Cstr_socket soc = null;
        int m_out=0;
        int m_miliseconds=200;
        int m_tm_progr=1000;
        int modo_fin = 0;
        bool cancela = false;
        bool cancela_fin = false;
        bool conectado = false;
        bool permu = false;
        bool cancela_permu = false;
        int progr = 0;
        string args = "";
        string str_cfg = "";
        string tarea = "";
        public string err_str = "";
        double x, y;
        IprocessManager cps;
        enum TiposActu
        {
            ActuMal,
            ActuNoActu,
            ActuBien,
            ActuPermu,
            ActuMulti,
            ActuFinOk,
            ActuSect,
            ActuPlan,
            ActuFinNOk,
            ActuN,
        }

        public string Ip { get; set; }
        public int  Puerto { get; set; }

        public ProcesoEjecServ()
        {

        }
        public Respuesta<TiposEjecucion> start(string cfg, IprocessManager cps)
        {
            this.cps = cps;

            try
            {
                soc = new Cstr_socket();
            }
            catch
            
            {
                var resp = new Respuesta<TiposEjecucion>() { Value = TiposEjecucion.FinEjecNOk };
                resp.Error.Add("Error al crear conexión con olivia task (no se pudo crear socket)");
                return resp;
            }

            str_cfg = cfg;
            /*
            await ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(() =>
            {
                run();
            };*/
            var res = run();
            try
            {
                soc.termina();
            }
            catch
            {
                res.Error.Add("Error al cerrar la conexion con olivia task (no se pudo cerrar el socket)");
            }
            return res;
        }
        private Respuesta<TiposEjecucion> run()
        {
            int nint = 0;
            int nint_max = 5;
            bool sal = false;
            bool first_send_cfg = true;
            bool fin = false;
            int lastprog = 0;
            var res = new Respuesta<TiposEjecucion>() { Value = TiposEjecucion.FinEjecNOk };
            cps.SetProceso("Procesando Datos");
            cps.SetProgress(0);
            try
            {
                do
                {
                    Thread.Sleep(m_miliseconds);

                    if (first_send_cfg)//a continuación envía la configuración
                    {
                        Thread.Sleep(500);
                        if (!envia_cfg())
                        {
                            if (nint >= nint_max)
                            {
                                res.Error.Add("Error en la comunicación con OliviaTask");
                                var act = new Respuesta<TiposActu>() { Value = TiposActu.ActuFinNOk };
                                act.Error.AddRange(res.Error);
                                actualiza(act);
                                sal = true;
                            }
                            else
                                nint++;
                        }
                        else
                        {
                            first_send_cfg = false;
                            nint = 0;
                        }
                    }

                    int result = Environment.TickCount & Int32.MaxValue; //para que el tickcount no sea negativo
                    if (cps.Getcancelled()) //mira a ver si ha cancelado el usuario
                    {                        
                        //se ha cancelado, lo envía al OliviaTask
                        envia_cancel();
                        if (!cancela_permu)
                            sal = true;
                        else
                        {
                            cancela_permu = false;
                        }
                        if(!fin) //si no ha finalizado normal, el usuario lo ha cancelado
                            res.Error.Add("Proceso Cancelado por el usuario");
                    }
                    else if (!first_send_cfg && ((result - lastprog) >= m_tm_progr) && !fin) //en caso normal, todo va bien, pide el progreso y la tarea
                    {
                        //solo pide la programación cada m_tm_progr milis
                        var pp = pide_progr();
                        if (pp.Value> TiposActu.ActuFinOk)
                            fin = true;
                        if(pp.HasError)
                        {
                            if (nint >= nint_max)
                            {
                                res.Error.Add(pp.Error.FirstOrDefault());                                
                                sal = true;
                            }
                            else
                                nint++;
                        }
                        if (pp.Value == TiposActu.ActuFinNOk)
                            res.Error.Add("Finalizado proceso con fallos");
                        actualiza(pp);
                        lastprog = result;
                    }

                } while (!sal);
                
            }
            catch (Exception e)
            {
                res.Error.Add(e.Message);
                //MessageBox.Show("Error durante el proceso.", "Olivia", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return res;
            }
            if (!res.HasError)
                res.Value = TiposEjecucion.FinEjecOk;
            return res;
        }
        //funciones auxiliares----------------------------------------
        private bool conecta()
        {
            if (!conectado)
            {
                if (!soc.conecta(Ip, Puerto, Ip))
                {
                    conectado = false;
                    return false;
                }
                conectado = true;
            }
            return true;
        }
        /**
         * Envía la información de la configuración por socket a OliviaTask
         */
        public bool envia_cfg()
        {
            string args;

            try
            {
                if (!conecta())
                    return false;

                if (str_cfg == null || str_cfg.Length == 0)
                    return false;

                if (!soc.envia(str_cfg))
                {
                    conectado = false;
                    return false;
                }

                args = soc.recibe();
                if ((args.Length == 0) || (args != GeneralDef.SockConfOk))
                    return false;

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        /**
         * Pide por socket la información de sectorización a OliviaTask
         */
        private bool pide_sect()
        {
            //pide sectorización porque ya ha sido avisado de que ha terminado          
            try
            {
                if (!conecta())
                    return false;

                //pide secto
                if (!soc.envia(GeneralDef.SockSect))
                {
                    conectado = false;
                    return false;
                }

                //recibe sectorización
                args = soc.recibe();
                if (args.Length == 0)
                    return false;

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        /**
         * Pide por socket la información de progreso a OliviaTask
         * Devuelve 0 si ha ido mal, 1 si ha ido bien, 2 si ha recibido que hay que pedir sectorización
         */
        private Respuesta<TiposActu> pide_progr()
        {
            TiposActu tt;
            int i;
            var res = new Respuesta<TiposActu> { Value = TiposActu.ActuMal };
            //pide progreso y tarea por la que va            
            try
            {
                if (!conecta())
                {
                    res.Error.Add("Error en la comunicación con OliviaTask");
                    return res;
                }

                //pide progreso
                if (!soc.envia(GeneralDef.SockProgr))
                {
                    conectado = false;
                    res.Error.Add("Error en la comunicación con OliviaTask");
                    return res;
                }

                //recibe progreso
                args = soc.recibe();
                if (args.Length == 0)
                {
                    res.Value = TiposActu.ActuNoActu;
                    return res;
                }

                //comprueba progreso
                try
                {
                    progr = Convert.ToInt32(args);
                    if (progr < 0 || progr > GeneralDef.ProgrMax)
                    {
                        res.Value = TiposActu.ActuNoActu;
                        return res;
                    }
                }
                catch
                {
                    res.Value = TiposActu.ActuNoActu;
                    return res;
                }

                //pide tarea
                if (!soc.envia(GeneralDef.SockTarea))
                {
                    res.Error.Add( "");
                    return res;
                }

                //recibe tarea
                args = soc.recibe();
                if (args.Length == 0)
                {
                    res.Error.Add("Error recibe tarea mal");
                    return res;
                }
                if (args == "0") //se ha mezclado el progreso, vuelve a recibir la tarea
                    args = soc.recibe();
                if (args.Length == 0)
                {
                    res.Error.Add("Error recibe tarea mal");
                    return res;
                }

                i = 0;
                tt = TiposActu.ActuBien;
                //comprueba tarea
                if (args.StartsWith(GeneralDef.SockSectFin))
                {
                    i = GeneralDef.SockSectFin.Length;
                    tt = TiposActu.ActuSect;
                }
                else if (args.StartsWith(GeneralDef.SockPlanFin))
                {
                    i = GeneralDef.SockPlanFin.Length;
                    tt = TiposActu.ActuPlan;
                }
                else if (args.StartsWith(GeneralDef.SockFinOk))
                {
                    i = GeneralDef.SockFinOk.Length;
                    tt = TiposActu.ActuFinOk;
                }
                else if (args.StartsWith(GeneralDef.SockFinNOk))
                {
                    i = GeneralDef.SockFinNOk.Length;
                    tt = TiposActu.ActuFinNOk;
                }
                else if (args.Contains("permutaciones"))
                {
                    tt = TiposActu.ActuPermu;
                }
                else if (args.Contains("multitask"))
                {
                    tt = TiposActu.ActuMulti;
                }
                modo_fin = (int)tt;

                tarea = args.Substring(i);
                res.Value = tt;
                return res;
            }
            catch (Exception)
            {
                res.Error.Add("Error al preguntar por el progreso.");
                return res;
            }
        }
        /**
         * Envía a OliviaTask la orden de cancelar el proceso
         */
        private Respuesta<bool> envia_cancel()
        {
            var res = new Respuesta<bool> { Value = false };
            try
            {
                if (!conectado)
                    return res;

                //envía cancel
                if (!soc.envia(GeneralDef.SockCanc))
                    return res;

                //recibe respuesta
                args = soc.recibe();
                if (args.Length == 0)
                    return res;

                //comprueba que ok
                if (args != GeneralDef.SockCancOk)
                    return res;
                res.Value = true;
                return res;
            }
            catch (Exception)
            {
                res.Error.Add("Error al cancelar el progreso.");
                res.Value = false;
                //MessageBox.Show("Error al cancelar el progreso.", "Olivia", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return res;
            }
        }
        /**
        * Actualiza la barra de progreso
        * bool fallo_soc : indica si ha habido fallo en el socket
        */
        private void actualiza(Respuesta<TiposActu> actu)
        {
            try
            {
                permu = false;
                //actualiza la barra de tarea y  el texto
                switch (actu.Value)
                {
                    case TiposActu.ActuBien:
                        {
                            pon_progr(progr);
                            pon_texto(tarea);
                            break;
                        }
                    case TiposActu.ActuMal:
                        {
                            if (actu.HasError)
                                pon_texto(actu.Error.FirstOrDefault());
                            else
                                pon_texto(tarea);
                            break;
                        }
                    case TiposActu.ActuFinOk:
                        {
                            pon_progr(100);
                            pon_texto("Finalizado proceso con éxito");
                            pon_textBtn("Finalizar");
                            break;
                        }
                    case TiposActu.ActuFinNOk:
                        {
                            pon_progr(100);
                            err_str = "Finalizado proceso con fallos\n" + tarea;
                            pon_texto(err_str);
                            pon_textBtn("Finalizar");
                            break;
                        }
                    case TiposActu.ActuSect:
                        {
                            pon_progr(100);
                            pon_texto("Finalizada sectorización\n" + tarea);
                            pon_textBtn("Finalizar");
                            break;
                        }
                    case TiposActu.ActuPlan:
                        {
                            pon_progr(100);
                            pon_texto("Finalizada planificación\n" + tarea);
                            pon_textBtn("Finalizar");
                            break;
                        }
                    case TiposActu.ActuPermu:
                        {
                            permu = true;
                            pon_texto(tarea);
                            //pon_estilo(ProgressBarStyle.Marquee);
                            break;
                        }
                    case TiposActu.ActuMulti:
                        {
                            pon_texto(tarea);
                            //pon_estilo(ProgressBarStyle.Marquee);
                            break;
                        }
                    default:
                        return;
                }
                /*
                if (actu != TiposActu.ActuPermu && actu != TiposActu.ActuMulti)
                    pon_estilo(ProgressBarStyle.Continuous);*/
                pon_barstate(actu.Value);
            }
            catch (Exception)
            {
                //MessageBox.Show("Error al actualizar el progreso.", "Olivia", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        /*
         * Para poder actualizar el texto de la ventana de progreso hay que llamar a invoke
         * porque está desde otro thread
         */
        private void pon_texto(String txt)
        {
            cps.SetEstado(txt);
        }
        private void pon_textBtn(String txt)
        {
            cps.SetTextBtn(txt);
        }

        /*
        * Para poder actualizar la barra de progreso hay que llamar a invoke
        * porque está desde otro thread
        * Actualiza el tipo de progreso y el texto
        */
        private void pon_estilo()
        {
            //cps.Status
        }
        
        /*
         * Para poder actualizar la barra de progreso hay que llamar a invoke
         * porque está desde otro thread
         * Actualiza el estilo para indicar que ha habido error
         */
        private void pon_barstate(TiposActu actu)
        {
            int col = 1;

           
             if ((actu == TiposActu.ActuMal) || (actu == TiposActu.ActuFinNOk))
                 cps.SetProceso("Proceso Finalizado con errores.");
             else if(actu == TiposActu.ActuFinOk)
                cps.SetProceso("Proceso Finalizado.");

            if ((actu == TiposActu.ActuMal) || (actu == TiposActu.ActuFinNOk))
                cps.PonBarColorRojo();
            else
                cps.PonBarColorVerde();

            /*if (actu > TiposActu.ActuFinOk)
               cps.SetEstado("Calculando...");
            if ((actu == TiposActu.ActuPermu) && !cancela)
               cps.SetEstado("Calculando Permutaciones...");

           if (actu > TiposActu.ActuFinOk)
               button_canc.Text = "Finalizar";
           if ((actu == TiposActu.ActuPermu) && !cancela)
               button_canc.Text = "Parar permu.";*/
        }
        /*
         * Para poder actualizar la barra de progreso hay que llamar a invoke
         * porque está desde otro thread
         * Actualiza el progreso
         */
        private void pon_progr(int pro)
        {
            cps.SetProgress( pro);
 		}

        //configura los parámetros de la conexión
        public void ConfigConex()
        {
            var conf = ConfigServ.Serv.Leer();
            Puerto = conf.Puerto;
            Ip = conf.Ip;
        }
    }
}