using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ArcGIS.Core.CIM;
using ArcGIS.Core.Data;
using ArcGIS.Core.Data.UtilityNetwork.Trace;
using ArcGIS.Core.Geometry;
using ArcGIS.Desktop.Catalog;
using ArcGIS.Desktop.Core;
using ArcGIS.Desktop.Editing;
using ArcGIS.Desktop.Extensions;
using ArcGIS.Desktop.Framework;
using ArcGIS.Desktop.Framework.Contracts;
using ArcGIS.Desktop.Framework.Dialogs;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.Mapping;
using System.Collections.ObjectModel;
using OliviaAddInPro.Model;
using OliviaAddInPro.Helper;
using ArcGIS.Desktop.Internal.Layouts.Utilities;
using OliviaAddInPro.Services;

namespace OliviaAddInPro
{
    internal class PaneLimpiezaSub1ViewModel : PanelViewModelBase
    {

        private ObservableCollection<string> tiposTto = new ObservableCollection<string>(LimpiezaDef.tipos_tto_str);
        private ObservableCollection<string> opsAmbs=new ObservableCollection<string>();
        private int selOpAmb = -1;
        private ObservableCollection<CheckedListItem<string>> ambitos = new ObservableCollection<CheckedListItem<string>>();  
        /**
        * Array de variables de tipo AmbitsList para mostrar en la lista, se rellena al elegir el tipo de tratamiento
        */
        private ObservableCollection<AmbitsList> ambs_list = null;
        /**
        * Array de variables de tipo AmbitsList para mostrar en la lista, para la opción editable (puedes elegir cualquier ámbito)
        */
        private ObservableCollection<AmbitsList> ambs_list_tod_lin = null;
        /**
        * Array de variables de tipo AmbitsList para mostrar en la lista, para la opción editable (puedes elegir cualquier ámbito)
        */
        private ObservableCollection<AmbitsList> ambs_list_tod_pto = null;
        /**
        * Array de variables de tipo AmbitsList para mostrar en la lista, para la opción genérica (puedes elegir los ejes de las calles)
        */
        private ObservableCollection<AmbitsList> ambs_list_eje = null;
        /**
         * Matriz que da true aquellos ámbitos de trabajo que están contemplados en .gdb una vez leida
         */
        bool[] amb_tra_gdb = null;
        private LimpiezaServ limpServ;

        private bool[] ambitosSel;

        #region Properties     

        public override string DisplayName
        {
            get { return Resource1.String_tto; }
        }

        private string textVeloDespl;
        public string TextVeloDespl
        {
            get { return textVeloDespl; }
            set { base.SetProperty(ref textVeloDespl, value, () => TextVeloDespl); }
        }
        private string lblUdsVeloDespl;
        public string LblUdsVeloDespl
        {
            get { return lblUdsVeloDespl; }
            set { base.SetProperty(ref lblUdsVeloDespl, value, () => LblUdsVeloDespl); }
        }

        private int timeTto;
        public int TimeTto
        {
            get { return timeTto; }
            set { base.SetProperty(ref timeTto, value, () => TimeTto); }
        }
        private string lblUdsTimeTto;
        public string LblUdsTimeTto
        {
            get { return lblUdsTimeTto; }
            set { base.SetProperty(ref lblUdsTimeTto, value, () => LblUdsTimeTto); }
        }
        private int udsTTto;
        public int UdsTTto
        {
            get { return udsTTto; }
            set { base.SetProperty(ref udsTTto, value, () => UdsTTto); }
        }
        private string lblTimeTto;
        public string LblTimeTto
        {
            get { return lblTimeTto; }
            set { base.SetProperty(ref lblTimeTto, value, () => LblTimeTto); }
        }
        private bool respSentCirc;
        public bool RespCirc
        {
            get { return respSentCirc; }
            set { base.SetProperty(ref respSentCirc, value, () => RespCirc); }
        }
        
        public ObservableCollection<string> TiposTto
        {
            get { return tiposTto; }
            set
            {
                tiposTto = value;
                base.NotifyPropertyChanged("TiposTto");
            }
        }
        private int tipoTto = -1;
        public int TipoTto
        {
            get { return tipoTto; }
            set
            {
                tipoTto = value;
                base.NotifyPropertyChanged("TipoTto");
            }
        }
        public ObservableCollection<string> OpsAmbs
        {
            get { return opsAmbs; }
            set
            {
                opsAmbs = value;
                base.NotifyPropertyChanged("OpsAmbs");
            }
        }
        public int SelOpAmb
        {
            get { return selOpAmb; }
            set
            {
                selOpAmb = value;
                base.NotifyPropertyChanged("SelOpAmb");
            }
        }
        public ObservableCollection<CheckedListItem<string>> Ambitos
        {
            get { return ambitos; }
            set
            {
                ambitos = value;
                base.NotifyPropertyChanged("Ambitos");
            }
        }
        public bool[] AmbitosSel
        {
            get { return ambitosSel; }
            set
            {
                ambitosSel = value;
                base.NotifyPropertyChanged("AmbitosSel");
            }
        }
        private string textAnchoVia;
        public string TextAnchoVia
        {
            get { return textAnchoVia; }
            set { base.SetProperty(ref textAnchoVia, value, () => TextAnchoVia); }
        }
        private System.Windows.Visibility visTextAnchoVia = System.Windows.Visibility.Hidden;
        public System.Windows.Visibility VisTextAnchoVia
        {
            get { return visTextAnchoVia; }
            set { base.SetProperty(ref visTextAnchoVia, value, () => VisTextAnchoVia); }
        }
        /*private bool enableListBoxAmb;
        public bool EnableListBoxAmb
        {
            get { return (enableListBoxAmb && CapaAbierta); }
            set { base.SetProperty(ref enableListBoxAmb, value, () => EnableListBoxAmb); }
        }
       public override bool CapaAbierta
        {
            get { return base.CapaAbierta; }
            set
            {
                base.CapaAbierta = value;
                base.NotifyPropertyChanged("CapaAbierta");
                NotifyPropertyChanged("EnableListBoxAmb");
            }
        }*/
        #endregion Properties

        public PaneLimpiezaSub1ViewModel()
        {
            LblCapaElems = Resource1.String_selec_capa;
            timeTto = 0;
            textVeloDespl = "";
            lblUdsTimeTto = "min";
            lblUdsVeloDespl = "km/h";
            limpServ = new LimpiezaServ(null); //no hace falta instancia limp
            textAnchoVia = LimpiezaDef.Parametros.ancho_via.ToString();
        }
        /**
         * Realiza las comprobaciones para cuando se ha abierto una capa
         */ 
        public bool AbiertaCapa(string capa)
        {
            CapaAbierta = false;
            CapaElems = string.Empty;
            OpsAmbs.Clear();
            Ambitos.Clear();
            TipoTto = -1;
            VisTextAnchoVia = System.Windows.Visibility.Hidden;
            TextVeloDespl = "";
            cambia_tiempo_tto();
            OliviaGlob.AddFlagTipEjec(TiposEjecucion.Config); //lo reinicia, por si estaba después de planificar
            OliviaGlob.RemoveFlagTipEjec(TiposEjecucion.Config2);            
            if (string.IsNullOrEmpty(capa))
                return false;
            //comprueba los campos de limpieza
            if (!limpServ.CompruebaCamposLimp(capa))
            {
                HelperGlobal.ponMsg(limpServ.ErrStr, System.Windows.MessageBoxImage.Warning);
                return false;
            }
            LimpiezaDef.forma_filtros();
            amb_tra_gdb = limpServ.BuscAmbGdb(capa);
            bool hay_alguno = false;
            foreach (bool b in amb_tra_gdb)
            {
                if (b)
                {
                    hay_alguno = true;
                    break;
                }
            }
            if (!hay_alguno)
            {
                HelperGlobal.ponMsg(Resource1.String_error_elems_tabla,
                    System.Windows.MessageBoxImage.Warning);
                return false;
            }
            //hay elementos en la gdb            
            CapaElems = capa;
            CapaAbierta = true;
            return true;
        }
        /**
         * Realiza los cambios en los ámbitos cuando se ha seleccionado un tratamiento
         */
         public void ComboTtoSel(int tto)
        {
            if (tto < 0)
                return;
            TipoTto = tto;            
            cambia_tiempo_tto();
            ambitosSel = new bool[(int)LimpiezaDef.AmbitsTra.AmbN];   //reinicia los ambitos seleccionados

            ////////////////////////////////////////
            //Otros cambios que dependen del tratamiento
            if (TipoTto != (int)LimpiezaDef.TiposTto.TtoNoDef)
            {
                RespCirc = LimpiezaDef.rest_circ[TipoTto];

                if (LimpiezaDef.v_desplazamiento[TipoTto] > 0)
                {
                    TextVeloDespl = Convert.ToString(LimpiezaDef.v_desplazamiento[TipoTto]);
                }
                else
                {
                    TextVeloDespl = Resource1.String_velo_nodef;
                }
            }

            //////////////////////////////////////////////////////////////
            //rellena el combo de opciones de los ambitos
            VisTextAnchoVia = System.Windows.Visibility.Hidden;
            Ambitos.Clear();
            OpsAmbs.Clear();
            OpsAmbs.Add("Editable");
            OpsAmbs.Add("Genérico");
            //en el caso del tratamiento customizable no se pondrán sus opciones particulares, sólo interesan las opciones globales a todos los tratamientos.
            //las opciones particulares del tratamiento customizable, son realemnte las opciones globales a todos los ttos.
            if ((TipoTto != (int)LimpiezaDef.TiposTto.TtoNoDef) && (TipoTto < (int)LimpiezaDef.TiposTto.TtoCustom))
            {
                for (int i = 0; i < (LimpiezaDef.ambs_val[TipoTto].n_ops); i++)
                    OpsAmbs.Add("Opción " + (i + 1));
            }
        }

            /**
             * Cambio el tiempo del tratamiento en función del tipo de tratamiento seleccionado
             */
            private void cambia_tiempo_tto()
        {
            if (TipoTto == (int)LimpiezaDef.TiposTto.TtoNoDef)
            {
                //no está definido
                LblTimeTto = Resource1.String_lbl_timetto;
                return;
            }
            else if (TipoTto >= (int)LimpiezaDef.TiposTto.TtoPapeVaci)
            {
                //tipo puntual, el tiempo es en minutos
                LblTimeTto = Resource1.String_lbl_timetto;
                UdsTTto = (int)GeneralDef.OlvTiposTto.OlvTipTtoMin;
            }
            else if ((TipoTto == (int)LimpiezaDef.TiposTto.TtoBarrMecCalz) ||
                (TipoTto == (int)LimpiezaDef.TiposTto.TtoBaldMecCalz))
            {
                //tipo lineal, es velocidad en m/h
                LblTimeTto = Resource1.String_lbl_velotto;
                UdsTTto = (int)GeneralDef.OlvTiposTto.OlvTipTtoMh;
            }
            else
            {
                //tipo lineal, es velocidad en m2/h
                LblTimeTto = Resource1.String_lbl_velotto;
                UdsTTto = (int)GeneralDef.OlvTiposTto.OlvTipTtoM2h;
            }
            LblUdsTimeTto = GeneralDef.UdsTto[UdsTTto];
            TimeTto = (int)LimpiezaDef.tiempos_tto[(int)TipoTto];
        }
        /**
         * Realiza los cambios en los ámbitos cuando se ha seleccionado un tratamiento
         */
        public void ComboAmbSel(int opAmb)
        {
            if (opAmb < 0)
                return;
            try { 
                Ambitos.Clear();
                SelOpAmb = opAmb;
                ObservableCollection<AmbitsList> lista;
                lista = pon_ambitos();
                //checkea los ámbitos de esta opción
                check_ambitos(SelOpAmb, lista);

                if (SelOpAmb == 1)
                {
                    //permite visualizar los elementos del ancho de vía que se activará cuando se seleccione la opción genérica de ejes de calle siempre y cuando el tiempo de tratamiento sea en ml/h
                    if (UdsTTto != (int)GeneralDef.OlvTiposTto.OlvTipTtoM2h_eje)
                    {
                        VisTextAnchoVia = System.Windows.Visibility.Visible;
                    }
                }
                else
                {
                    VisTextAnchoVia = System.Windows.Visibility.Hidden;
                }
                /*if (((TipoTto == (int)LimpiezaDef.TiposTto.TtoBaldMecCalz) && (SelOpAmb >= 2)) ||
                    ((TipoTto == (int)LimpiezaDef.TiposTto.TtoCaidaHoja) && (SelOpAmb >= 2)))
                    EnableListBoxAmb = false; //si uno de los ámbitos es "eje de calle" no se puede combinar con los demás ámbitos
                else
                    EnableListBoxAmb = true;*/
            }
            catch (Exception)
            {
                //MessageBox.Show("Error al seleccionar ámbitos", "Olivia", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }

        /**Checkea de la lista los ámbitos de trabajo en función del tipo de tratamiento
         * y la opción elegida
         */
        private void check_ambitos(int iop, ObservableCollection<AmbitsList> lista)
        {
            bool check = false;
            bool enable = false;
            if (lista == null)
                return;
            for (int i = 0; i < lista.Count; i++)
            {
                check = false;
                if (iop < 0)
                {
                    //para reseteo
                    check=false;
                    enable = true;
                }
                else if (iop == 0)
                {
                    //dependiendo del tratamiento seleccionado en la opción 0(Editable) se seleccionarán diferentes opciones del TtoCustom(diferenciando entre ttos lineales o puntuales)
                    //para ámbitos lineales
                    if (TipoTto < (int)LimpiezaDef.TiposTto.TtoPapeVaci)
                    {
                        check = LimpiezaDef.ambs_val[(int)LimpiezaDef.TiposTto.TtoCustom].ambs_ops[iop].ambs[(int)((AmbitsList)lista[i]).amb_i]
                            && amb_tra_gdb[(int)((AmbitsList)lista[i]).amb_i];
                    }
                    //para ámbitos puntuales
                    else if ((TipoTto > (int)LimpiezaDef.TiposTto.TtoCaidaHoja) && (TipoTto < (int)LimpiezaDef.TiposTto.TtoCustom))
                    {
                        //es la opción Genérica que es la opción 1 del TtoCustom, de ahi que el índice sea [iop + 1]
                        check = LimpiezaDef.ambs_val[(int)LimpiezaDef.TiposTto.TtoCustom].ambs_ops[iop + 1].ambs[(int)((AmbitsList)lista[i]).amb_i]
                            && amb_tra_gdb[(int)((AmbitsList)lista[i]).amb_i];
                    }
                    //para ámbito custom
                    else
                    {
                        check = LimpiezaDef.ambs_val[(int)LimpiezaDef.TiposTto.TtoCustom].ambs_ops[iop].ambs[(int)((AmbitsList)lista[i]).amb_i]
                            && amb_tra_gdb[(int)((AmbitsList)lista[i]).amb_i];
                    }
                }
                else if (iop == 1)
                {
                    //opción genérica, ejes de vía
                    check = true;
                    enable = true;
                    //es la opción Genérica que es la opción 2 del TtoCustom, de ahi que el índice sea [iop + 1]
                    /*LimpiezaDef.ambs_val[(int)LimpiezaDef.TiposTto.TtoCustom].ambs_ops[iop + 1].ambs[(int)((AmbitsList)lista[i]).amb_i]
                 && amb_tra_gdb[(int)((AmbitsList)lista[i]).amb_i]*/
                }
                else if ((iop - 2) < (LimpiezaDef.ambs_val[TipoTto].n_ops))
                {
                    //pone el check si el elemento que aparece en la lista porque es común o no común 
                    //esa opción en concreto le tiene
                    check = LimpiezaDef.ambs_val[TipoTto].ambs_ops[iop - 2].ambs[(int)((AmbitsList)lista[i]).amb_i]
                        && amb_tra_gdb[(int)((AmbitsList)lista[i]).amb_i];
                }
                if (iop != 1)
                    enable = amb_tra_gdb[(int)((AmbitsList)lista[i]).amb_i];

                Ambitos.ElementAt(i).IsChecked = check;
                Ambitos.ElementAt(i).IsEnabled = enable;
            }
        }
        /**
         * Rellena una lista con los ámbitos comunes y no comunes para mostrar en el listbox en función del tipo de tratamiento
         */
        private void rellena_amb_list()
        {
            bool[] amb_val;
            bool[] amb_val_tod;
            bool[] amb_val_eje;

            //lo hace para todos los tratamientos excepto para el Customizble ya que sólo se quieren mostrar las opciones alternaitvas al tratamiento
            if (TipoTto < (int)LimpiezaDef.TiposTto.TtoCustom)
            {
                //pide un array de booleanos donde es true en las posiciones de los ámbitos comunes
                amb_val = limpServ.DameAmbTto(tipoTto);
                //crea un array con el número de elementos del array anterior que sean true
                ambs_list = new ObservableCollection<AmbitsList>();//cuenta los trues
                //rellena el array apuntando los índices y los textos correspondientes a los trues
                for (int i = 0; i < amb_val.Length; i++)
                {
                    if (amb_val[i])
                    {
                        ambs_list.Add(new AmbitsList((LimpiezaDef.AmbitsTra)i, LimpiezaDef.ambits_tra_str[i]));
                    }
                }
            }

            //se hace lo mismo para la opción que da la posibilidad de seleccionar cualquier ámbito lineal
            amb_val_tod = LimpiezaDef.ambs_val[(int)LimpiezaDef.TiposTto.TtoCustom].ambs_ops[0].ambs;
            ambs_list_tod_lin = new ObservableCollection<AmbitsList>();
            for (int i = 0; i < amb_val_tod.Length; i++)
            {
                if (amb_val_tod[i])
                {
                    ambs_list_tod_lin.Add(new AmbitsList((LimpiezaDef.AmbitsTra)i, LimpiezaDef.ambits_tra_str[i]));
                }
            }

            //se hace lo mismo para la opción que da la posibilidad de seleccionar cualquier ámbito puntual
            amb_val_tod = LimpiezaDef.ambs_val[(int)LimpiezaDef.TiposTto.TtoCustom].ambs_ops[1].ambs;
            ambs_list_tod_pto = new ObservableCollection<AmbitsList>();
            for (int i = 0; i < amb_val_tod.Length; i++)
            {
                if (amb_val_tod[i])
                {
                    ambs_list_tod_pto.Add(new AmbitsList((LimpiezaDef.AmbitsTra)i, LimpiezaDef.ambits_tra_str[i]));
                }
            }

            //se hace lo mismo para la opción que permite seleccionar únicamente los ejes de calle
            amb_val_eje = LimpiezaDef.ambs_val[(int)LimpiezaDef.TiposTto.TtoCustom].ambs_ops[2].ambs;
            ambs_list_eje = new ObservableCollection<AmbitsList>();
            for (int i = 0; i < amb_val_eje.Length; i++)
            {
                if (amb_val_eje[i])
                {
                    ambs_list_eje.Add(new AmbitsList((LimpiezaDef.AmbitsTra)i, LimpiezaDef.ambits_tra_str[i]));
                }
            }
        }
        /**Pone en la lista los ámbitos de trabajo en función del tipo de tratamiento
         * Los comunes y no comunes a sus opciones. Luego checkea los correspondientes a la opción
         */
        private ObservableCollection<AmbitsList> pon_ambitos()
        {
            ObservableCollection<AmbitsList> lista = null;
            try
            {
                if (TipoTto == (int)LimpiezaDef.TiposTto.TtoNoDef)
                {
                    return lista;
                }

                rellena_amb_list();
                if ((SelOpAmb == 0) && ((TipoTto < (int)LimpiezaDef.TiposTto.TtoPapeVaci) || 
                    (TipoTto >= (int)LimpiezaDef.TiposTto.TtoCustom)))
                {                    
                    lista = ambs_list_tod_lin;
                }
                else if ((SelOpAmb == 0) && (TipoTto > (int)LimpiezaDef.TiposTto.TtoCaidaHoja))
                {
                    lista = ambs_list_tod_pto;
                }
                else if (SelOpAmb == 1)
                {
                    lista = ambs_list_eje;
                }
                else
                {
                    lista = ambs_list;
                }

                AmbitsList al;
                for (int i = 0; i < lista.Count; i++)
                {
                    al = lista.ElementAt(i);
                    Ambitos.Add(new CheckedListItem<string>(al.amb_str));
                }
                return lista;
            }
            catch (Exception)
            {
                return null;
            }

        }

        /**
         * Añade a la lista de ambitos seleccionados los que están checkeados de la lista de posibles
         */
        public bool lee_ambitos()
        {
            ambitosSel = new bool[(int)LimpiezaDef.AmbitsTra.AmbN];   //reinicia los ambitos seleccionados
            if (ambitos.Count == 0)
                return false;
            else if ((selOpAmb == 0) && (tipoTto < (int)LimpiezaDef.TiposTto.TtoPapeVaci))
            {
                for (int i = 0; i < ambitos.Count; i++)
                {
                    //lo pone a true si ese elemento está checkeado, para lo que coge el índice al array de ámbitos de ese elemento
                    if(ambitos.ElementAt(i).IsChecked)
                        ambitosSel[(int)((AmbitsList)ambs_list_tod_lin[i]).amb_i] = true;
                }
            }
            else if ((selOpAmb == 0) && (tipoTto >= (int)LimpiezaDef.TiposTto.TtoPapeVaci))
            {
                for (int i = 0; i < ambitos.Count; i++)
                {
                    //lo pone a true si ese elemento está checkeado, para lo que coge el índice al array de ámbitos de ese elemento
                    if (ambitos.ElementAt(i).IsChecked)
                        ambitosSel[(int)((AmbitsList)ambs_list_tod_pto[i]).amb_i] = true;
                }
            }
            else if (selOpAmb == 1)
            {
                for (int i = 0; i < ambitos.Count; i++)
                {
                    //lo pone a true si ese elemento está checkeado, para lo que coge el índice al array de ámbitos de ese elemento
                    if (ambitos.ElementAt(i).IsChecked)
                        ambitosSel[(int)((AmbitsList)ambs_list_eje[i]).amb_i] = true;
                }
            }
            else
            {
                for (int i = 0; i < ambitos.Count; i++)
                {
                    //lo pone a true si ese elemento está checkeado, para lo que coge el índice al array de ámbitos de ese elemento
                    if (ambitos.ElementAt(i).IsChecked)
                        ambitosSel[(int)((AmbitsList)ambs_list[i]).amb_i] = true;
                }
            }
            if (ambitosSel[(int)LimpiezaDef.AmbitsTra.AmbEjeCalle])//si es ámbito tipo eje de calle 
            {
                if (udsTTto == (int)GeneralDef.OlvTiposTto.OlvTipTtoMh)
                    udsTTto = (int)GeneralDef.OlvTiposTto.OlvTipTtoMh_eje;
                else if (udsTTto == (int)GeneralDef.OlvTiposTto.OlvTipTtoM2h)
                    udsTTto = (int)GeneralDef.OlvTiposTto.OlvTipTtoM2h_eje;
            }
            return true;
        }              

    }

    /**
        * Clase para la lista de ámbitos, relaciona el tipo de ámbito con su texto
        */
    internal class AmbitsList
    {
        private LimpiezaDef.AmbitsTra mi_amb_i;
        private string mi_amb_str;

        public AmbitsList(LimpiezaDef.AmbitsTra amb_i, string amb_str)
        {
            this.mi_amb_i = amb_i;
            this.mi_amb_str = amb_str;
        }
        public LimpiezaDef.AmbitsTra amb_i
        {
            get { return mi_amb_i; }
            set { mi_amb_i = value; }
        }
        public string amb_str
        {
            get { return mi_amb_str; }
            set { mi_amb_str = value; }
        }
    }   
}
