using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ArcGIS.Core.Geometry;
using ArcGIS.Desktop.Internal.Layouts.Utilities;
using OliviaAddInPro.Helper;
using OliviaAddInPro.Model.contract;
using OliviaAddInPro.Services;
using static OliviaAddInPro.Model.ComunDef;
using ArcGIS.Core.CIM;
using ArcGIS.Core.Data;
using System.Collections.ObjectModel;

namespace OliviaAddInPro.Model
{
    public class TratamientoComun
    {
        /**
         * Tipo de tratamiento, en limpieza es sobre LimpiezaDef.TiposTto y en reco sobre RecogidaDef.tipos_fracc_str
         */
        public int TipoTto { get; set; } = (int)LimpiezaDef.TiposTto.TtoNoDef;
        /**
         * String para Tipo de tratamiento, se guarda como columna en el shp
         */
        public string TipoTtoStr { get; set; } = String.Empty;
        /**
         * Capa de ámbitos
         */
        public string CapaElems { get; set; } = string.Empty;
        /**
         * Capa de ámbitos después de haber planificado
         */
        public string CapaPlanif { get; set; } = string.Empty;
        /**
         * Nombre dle shp exportado de datos
         */
        public string NombreShpExport { get; set; } = string.Empty;
        /**
         * Nombre dle shp exportado de datos
         */
        public string NombreShpExp_PrefTto { get; set; } = string.Empty;
        /**
         * Nombre dle shp exportado de nw
         */
        public string NombreShpExportNw { get; set; } = string.Empty;
        /**
         * Velocidad de desplazamiento, en km/h. Si es 0, es determinada por la vía
         */
        public int VDespl { get; set; } = 0;
        /**
         * Tiempo de tratamiento, en minutos
         */
        public int Ttto { get; set; } = 5;
        /**
         * Geometría para el filtro de ámbitos, incluye zonas, se añaden los ámbitos que pertenecen a esa geom
         */
        public Geometry GeomZon { get; set; } = null;
        public string TextGeomZon { get; set; } = string.Empty;
        /**
         * Geometría para el filtro de ámbitos, incluye zonas, se añaden los ámbitos que pertenecen a esa geom
         */
        public Geometry GeomNiv { get; set; } = null;
        public string TextGeomNiv { get; set; } = string.Empty;
        /**
         * Geometría para el filtro de ámbitos, incluye restricciones, se quitan los ámbitos que pertenecen a esa geom
         */
        public Geometry GeomRestr { get; set; } = null;
        public string TextGeomRestr { get; set; } = string.Empty;
        /**
         * Coordenadas de la instalación
         */
        public Coordinate2D CoordsInstal { get; set; } = new Coordinate2D(0, 0);
        /**
         * Coordenadas de la planta de descarga, solo hace falta para recogida
         */
        public Coordinate2D CoordsPlanta { get; set; } = new Coordinate2D(0, 0);
        /**
         * Buffer para la exportación, en metros
         */
        public int BuffExport { get; set; } = 1000;
        /**
         * Tiempo de trabajo al día por convenio, en minutos
         */
        public double TConv { get; set; } = LimpiezaDef.Parametros.t_conv;
        /**
         * Tiempo de descanso del personal, en minutos
         */
        public double TDescan { get; set; } = LimpiezaDef.Parametros.t_descanso;
        /**
         * Tiempo de carga y descarga, en minutos
         */
        public double TDescarg { get; set; } = LimpiezaDef.Parametros.t_cardesc;
        /**
         * Tiempo de desplazamiento inicio/fin a las instalaciones, en minutos
         */
        public double TDesplIniFin { get; set; } = LimpiezaDef.Parametros.t_desp;
        /**
        * hora de inicio de la jornada de trabajo, en minutos
        */
        public double HIni { get; set; } = LimpiezaDef.Parametros.h_inicio;
        /**
        * Porcentaje de tráfico que se estima, ya que si no los vehículos irían siempre a la velocida máxima permitida por la vía
        */
        public int Trafico { get; set; } = LimpiezaDef.Parametros.reduc_traf;
        /**
         * Número de puntos de control
         */
        public int NPtosCtrl { get; set; } = 3;
        /**
         * Número de sectores, si es 0, se calcula automáticamente, si es negativo, se ajusta a jornada
         */
        public int NSect { get; set; } = 3;
        /**
         * Ignorar o noelementos aislados
         */
        public bool IgnoAis { get; set; } = LimpiezaDef.Parametros.igno_ais;
        /**
         * Coordenadas Min y Max de la red navegable
         */
        public Envelope MimaNw { get; set; } = null;
        /**
         * Almacena la consulta para exportar los ámbitos
         */
        public string ConsultaAmbs { get; set; } = "";
        /**
         * String para errores o mensajes
         */
        private string err_str = string.Empty;
        public string ErrStr
        {
            get
            {
                /*string val = string.Empty;
                val.CopyFrom(err_str);
                err_str = string.Empty;
                return val; */
                return err_str;
            }
            set { err_str = value; }
        }

        private ObservableCollection<string> nombCamposOrig = new ObservableCollection<string>();
        private IReadOnlyList<ArcGIS.Core.Data.Field> camposOrig = null;
        /**
         * Para la ventana de progreso
         */
        public MyCancelableProgressorSource ProgrSrc { get; set; } = null;
        public ProcesoEjecServ ProceSrv { get; set; } = null;
        public FinProcServ FinProceSrv { get; set; } = null;
        public EjecServ ServCom { get; set; } = null;

        public TratamientoComun()
        {
            ProgrSrc = new MyCancelableProgressorSource(OliviaGlob.progrDialog.GetViewModel());
            ProceSrv = new ProcesoEjecServ();
            FinProceSrv = new FinProcServ();
        }

        public void ComienzaEjec(OliviaAddInPro.Services.ModosEjec modo)
        {
            //muestra progreso
            OliviaGlob.progrDialog.Show();
            //oculta la ventana
            OliviaGlob.ShowHidePane(false);
            //comienza ejecución
            Action<Respuesta<TiposEjecucion>, TratamientoComun> ac = FinProceSrv.finEjecuta;
            //pone los flags
            OliviaGlob.RemoveFlagTipEjec(TiposEjecucion.Config);
            if (modo == ModosEjec.Sectoriza)
                OliviaGlob.AddFlagTipEjec(TiposEjecucion.EjecSecto);
            else if (modo == ModosEjec.Planifica || modo == ModosEjec.SoloPlanifica)
                OliviaGlob.AddFlagTipEjec(TiposEjecucion.EjecPlanif);
            //llama ejecuta
            EjecutaAsync(modo, ac);
        }
        public async void EjecutaAsync(ModosEjec modo, Action<Respuesta<TiposEjecucion>, TratamientoComun> ffin)
        {
            await ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(() =>
            {
                var res = Ejecuta(modo);
                ffin(res, this);
                return res;
            });

        }
        public virtual Respuesta<TiposEjecucion> Ejecuta(ModosEjec modo)
        {
            Respuesta<TiposEjecucion> res = new Respuesta<TiposEjecucion>() { Value = TiposEjecucion.FinEjecNOk };
            res.Error.Add("No implementado");
            return res;
        }

        /**
         * Descodifica el nombre del sahpefile de entrada identificando el tipo de tratamiento y los ámbitos de trabajo
         */
        public virtual void decode_gdb(string shapefile, out string tratamiento, out string ambitos)
        {
            tratamiento = string.Empty;
            ambitos = string.Empty;
        }
        /**
         * Devuelve los campos característicos de Limpieza o Recogida
         */
        public virtual string[] GetCampos(string capa=null)
        {
            return null;
        }


        /**
         * Restaura el nombre original del campo si se ha truncado a 10 car al exportar a shp
         */
        public virtual string[] GetCamposOrig()
        {
            string[] camps = null;

            if (nombCamposOrig.Count > 0)
            {
                camps = new string[nombCamposOrig.Count];
                for (int i=0;i<nombCamposOrig.Count;i++)
                {
                    camps[i] = nombCamposOrig[i];
                }
            }

            return camps;
        }

        /**
         * Restaura el nombre original del campo si se ha truncado a 10 car al exportar a shp
         */
        public Respuesta<bool> RestauraNomCampos(string nombFtClass)
        {
            int LENGTHCAMPSHP = 10;
            Respuesta<bool> resp = new Respuesta<bool> { Value = false };
            FeatureClass fc = HelperGdb.GetFtClassSync(nombFtClass);
            if (fc == null)
                return resp;
            ////////////////////////////////////////////////////////            
            if (camposOrig == null || camposOrig.Count==0)
            {
                HelperGdb.Free(fc);
                return resp;
            }

            ObservableCollection<string> fields = HelperGdb.GetFieldsSync(fc);
            HelperGdb.Free(fc);
            if (fields == null)
            {
                HelperGdb.Free(fc);
                return resp;
            }
            try
            {
                int i = 0;
                int indx;
                string campTrunc = string.Empty;
                for (i = 0; i < camposOrig.Count; i++)
                {
                    if (camposOrig[i].Name.Length <= LENGTHCAMPSHP)
                        continue;
                    if (EstaRepeNomb(fields, camposOrig[i].Name))
                        continue;
                    campTrunc = camposOrig[i].Name.Substring(0, LENGTHCAMPSHP);                    
                    //si tiene más de 10 caracteres, lo ha truncado al exportar
                    //busca el nombre actual del campo en la fc
                    indx = fields.IndexOf(campTrunc);
                    if (indx < 0)
                        break;
                    if (!HelperGdb.RenameFieldSync(nombFtClass, campTrunc, camposOrig[i].Name, camposOrig[i].AliasName))
                        break;
                }
                if (i < camposOrig.Count)
                {
                    resp.Error.Add("Error en el campo " + camposOrig[i].Name);
                }
                else
                    resp.Value = true;
            }
            catch
            {

            }
            HelperGdb.Free(fc);
            return resp;
        }

        private bool EstaRepeNomb(ObservableCollection<string> fields, string nomb)
        {
            foreach( string fnomb in fields)
            {
                if (string.Compare(fnomb, nomb, true)==0)
                    return true;
            }
            return false;
        }

        /**
         * Guarda el nombre de los campos originales para luego restaurarlos
         */
        public void GuardaNombCamposOrig(string pathFtClass)
        {
            FeatureClass fc = HelperGdb.GetFtClassSync(pathFtClass);
            if (fc != null)
            {
                nombCamposOrig = HelperGdb.GetFieldsSync(fc);
            }
            else
                nombCamposOrig = new ObservableCollection<string>();
            HelperGdb.Free(fc);
        }
        /**
         * Guarda el nombre de los campos originales para luego restaurarlos
         */
        public void GuardaCamposOrig(string pathFtClass)
        {
            FeatureClass fc = HelperGdb.GetFtClassSync(pathFtClass);
            if (fc != null)
            {
                camposOrig = HelperGdb.GetFieldsSyncEx(fc);
            }
            else
                camposOrig = null;
            HelperGdb.Free(fc);
        }
    }
}
