using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OliviaDef;
using System.Windows.Forms;

using ESRI.ArcGIS.ArcMap;
using ESRI.ArcGIS.DataSourcesFile;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Carto;
using System.IO;
using ESRI.ArcGIS.GeoDatabaseUI;

namespace OliviaAddIn
{
    /**
     *	@file DatosGDBLimp.cs
     *	Clase con funciones para lectura de GDB relacionado con limpieza viaria.
     *	Contiene definiciones y funciones necesarias para exportar los datos de GDB a SHP, y viceversa
     *	concretados con las opciones de configuración de la limpieza viaria.
     */
    /**
     *	Clase con funciones para lectura de GDB relacionado con limpieza viaria.
     *	Contiene definiciones y funciones necesarias para exportar los datos de GDB a SHP, y viceversa
     *	concretados con las opciones de configuración de la limpieza viaria.
     */
    public class DatosGDBLimp : DatosGDB
    {
        //*************************************************************************************
        //Variables   
        //*************************************************************************************
        //Métodos
        public DatosGDBLimp()
        {

            rellena_ftclass();
        }

        /**
         * Rellenea el array ftclass con el nombre de las tablas de las que se extraerán datos
         */
        public void rellena_ftclass()
        {
            int i;
            LimpiezaDef.ftclass[(int)LimpiezaDef.AmbitsTra.AmbEjeCalle] = OliviaGlob.Capas.ftclass_ejes;
            for(i = (int)LimpiezaDef.AmbitsTra.AmbBordLibreMec; i < (int)LimpiezaDef.AmbitsTra.AmbConte; i++)
            {
                LimpiezaDef.ftclass[i] = LimpiezaDef.Capas.ftclass_limp_mec;
            }
            LimpiezaDef.ftclass[(int)LimpiezaDef.AmbitsTra.AmbConte] = LimpiezaDef.Capas.ftclass_cont;
            for(i=(int)LimpiezaDef.AmbitsTra.AmbPape; i<(int)LimpiezaDef.AmbitsTra.AmbN; i++)
            {
                LimpiezaDef.ftclass[i] = LimpiezaDef.Capas.ftclass_mobi;
            }
        }

        /**
         * Lee la geodatabase dónde están los datos referentes a los ámbitos de trabajo
         */
        public bool[] lee_amb_gdb(Limpieza limp)
        {
            String consulta;
            int numero_lin;
            bool[] amb_gdb = new bool[(int)LimpiezaDef.AmbitsTra.AmbN];//se inician a false
            string ftclass;
            
            //mira a ver si hay ejes de calle
            ftclass = LimpiezaDef.ftclass[(int)LimpiezaDef.AmbitsTra.AmbEjeCalle];
            consulta = LimpiezaDef.filtro_str[(int)LimpiezaDef.AmbitsTra.AmbEjeCalle];
            numero_lin = FunGDB.cuenta_filas_ftclass(OliviaGlob.Paths.PathGdbNw, ftclass, consulta);
            if (numero_lin > 0)
            {
                amb_gdb[(int)LimpiezaDef.AmbitsTra.AmbEjeCalle] = true;
            }
            //mira a ver si hay el resto de capas y tienen entidades            
            for (int i = (int)LimpiezaDef.AmbitsTra.AmbBordLibreMec; i < (int)LimpiezaDef.AmbitsTra.AmbN; i++)
            {
                consulta = LimpiezaDef.filtro_str[i];
                if (path_class == null) //como tradicionalmente, hay una gdb y los nombres de las feature classes del archivo de configuración
                {
                    ftclass = LimpiezaDef.ftclass[i];
                    numero_lin = FunGDB.cuenta_filas_ftclass(OliviaGlob.Paths.PathGdbGen, ftclass, consulta);
                }
                else
                    numero_lin = FunGDB.cuenta_filas_ftclass(path_class, consulta);

                if (numero_lin > 0)
                {
                    amb_gdb[i] = true;
                }
            }            
            
            return amb_gdb;
        }

        /*
         * Lee la capa que se ha seleccionzdo de limpieza y se comprueba que los campos que se han editado corresponden con la capa 
         * (es decir, se puede leer la capa con los campos configurados)
         */
        public bool comprueba_campos_limp(Limpieza limp)
        {
            int ind_campo;
            string[] camps;
            int i;
            ITable tabla;
            int NCAMPS = 2;

            if(path_class!=null)
                tabla = FunGDB.dame_tabla_clase(path_class);
            else
                tabla = FunGDB.dame_tabla_clase(OliviaGlob.Paths.PathGdbGen, LimpiezaDef.Capas.ftclass_limp_mec);

            if (tabla == null)
            {
                if (path_class != null)
                    limp.err_st = "No se encuentra la capa " + path_class;
                else
                    limp.err_st = "No se encuentra la capa " + LimpiezaDef.Capas.ftclass_limp_mec;

                return false;
            }

            //se comprueba que existen todos los campos que se han configurado en la capa seleccionada para la recogida
            camps = new string[NCAMPS];
            camps[0] = LimpiezaDef.Campos.consulta_entidad;
            camps[1] = LimpiezaDef.Campos.consulta_mecan;

            for (i = 0; i < NCAMPS; i++)
            {
                ind_campo = tabla.FindField(camps[i]);
                if (ind_campo == -1)
                {
                    limp.err_st = "No se encuentra el campo " + camps[i];
                    break;
                }
            }
            if (i < NCAMPS)
                return false;
            return true;
        }

        /**
         * Devuelve qué consulta hay que hacer, el filtro de los ámbitos y el nombre de la clase donde están
         */
        public string dame_consul_amb_clas(Limpieza limp, out string ambitos, out string nom_class)
        {
            string consulta, orstr;

            consulta = null;
            ambitos = "";
            orstr = null;
            nom_class = null;
            for (int i = 0; i < limp.amb_tra_sel.Length; i++)
            {
                if (limp.amb_tra_sel[i])
                {
                    if(nom_class==null)
                        nom_class = LimpiezaDef.ftclass[i];
                    consulta = consulta + orstr + "(" + LimpiezaDef.filtro_str[i] + ")";
                    ambitos = ambitos + i.ToString("00");                    
                    if (orstr == null)
                        orstr = " OR ";
                }
            }
            ambitos = "_A" + ambitos;
            if (consulta == "()")
                consulta = "";

            return consulta;
        }

        /**
         * Descodifica el nombre del sahpefile de entrada identificando el tipo de tratamiento y los ámbitos de trabajo
         */
        public void decode_gdb(string shapefile, out string tratamiento, out string ambitos)
        {
            int aux, auxl, mbito, indice, tratamient;
            string auxili, ambi, auxi;

            indice = shapefile.IndexOf("_");
            indice = indice + 2;//para saltarse la T que va antes del identificador del tipo de tratamiento
            auxili = shapefile.Substring(indice, 2);
            tratamient = Convert.ToInt32(auxili);
            tratamiento = LimpiezaDef.tto_gdb[tratamient];
            OliviaGlob.cartela_tto = LimpiezaDef.tipos_tto_str[tratamient];
            indice = shapefile.IndexOf("_", indice);
            indice = indice + 2;//para saltarse la A que va antes del identificador de los ámbitos que intervienen
            aux = shapefile.IndexOf("_", indice);
            auxl = aux - indice;
            auxi = "";
            while (auxl > 0)
            {
                ambi = shapefile.Substring(indice, 2);
                mbito = Convert.ToInt32(ambi);
                indice = indice + 2;
                auxl = auxl - 2;
                if (auxl != 0)
                    auxi = auxi + LimpiezaDef.ambs_gdb[mbito] + "_";
                else
                    auxi = auxi + LimpiezaDef.ambs_gdb[mbito];                
            }
            ambitos = LimpiezaDef.preftto_gdb[tratamient] + "_" + auxi;

            //quita los espacios
            tratamiento = tratamiento.Replace(" ", "_");
            ambitos = ambitos.Replace(" ", "_");

            auxi = shapefile;
            //para poner la zona cuando es seleccionada
            for (int i = 0; i < 2; i++)
            {
                indice = auxi.LastIndexOf("_");
                auxi = auxi.Substring(0, indice);
            }
            auxl = indice - aux;
            if (auxl <= 0)
                return;
            auxili = shapefile.Substring(aux, auxl);
            ambitos = ambitos + auxili;

        }

        /**
         * Exporta a shapefile las red navegable y los ámbitos de trabajo seleccionados. El archivo de salida será el nombre del tratamiento escogido.
         */
        public bool exporta(Limpieza limp, bool comp_planif)
        {
            int cont=0;
            string fecha_hora = "", ambitos = "";
            string consulta = "";
            IQueryFilter filtro = null;
            IGeometry geom_zonniv = null;
            IGeometry geom_ambits = null;
            string nomb_class_limp = null;
            IDatasetName datasetn=null;
            bool hayzonniv = false;
            
            if (limp == null)
            {
                err_st = "No se han recibido correctamente los datos para la exportación";
                return false;
            }
                        
            /////////////////////////////////////////////
            //lee ambitos de trabajo
            consulta = dame_consul_amb_clas(limp, out ambitos, out nomb_class_limp);
            if (consulta == null)
            {
                err_st = "Error al configurar consulta";
                return false;
            }

            /////////////////////////////////////////////
            //si va a planificar antes comprueba que estén las columnas de sector y secuencia            
            if (comp_planif)
            {
                if (!comprueba_planif(path_class, nomb_class_limp))
                {
                    err_st = "No existen en la tabla " + path_class + " columna de SECTOR y/o SECUENCIA, necesarias para planificar";
                    return false;
                }
            }
            //si path_secto!=null, hay que actualizar la sectorización, si ==null, en los datos de entrada ya hay sectorización 
            //y secuencia y se van a exportar
            //////////////////////////////////////////////////

            if (path_class == null) //como tradicionalmente, hay una gdb y los nombres de las feature classes del archivo de configuración
            {
                if(nomb_class_limp==OliviaGlob.Capas.ftclass_ejes)
                    cont = FunGDB.cuenta_filas_ftclass(OliviaGlob.Paths.PathGdbNw, nomb_class_limp, consulta);
                else
                    cont = FunGDB.cuenta_filas_ftclass(OliviaGlob.Paths.PathGdbGen, nomb_class_limp, consulta);
            }
            else
                cont = FunGDB.cuenta_filas_ftclass(path_class, consulta);
            
            if (cont <= 0)
            {
                err_st = "No existen ámbitos que cumplan las condiciones introducidas para la exportación "+consulta;
                return false;
            }            

            /////////////////////////////////////////////
            //consigue el polígono común a las dos geometrías: zonas y niveles
            //si no hay zonas ni niveles, es null
            geom_zonniv = FunGDB.intersec_geoms(limp.geom_zonas, limp.geom_niveles);
            //además, le quita las restricciones
            if (geom_zonniv != null && limp.geom_rest_acces != null)
            {
                geom_zonniv = FunGDB.diferencia_geoms(geom_zonniv, limp.geom_rest_acces);
            }

            if (geom_zonniv != null && geom_zonniv.IsEmpty)
            {
                err_st = "Error, la geometría resultado de intersecar zonas, niveles y restricciones de circulación está vacía";
                return false;
            }
            if (geom_zonniv == null && comp_planif && (path_secto != null))
            { 
                //en este caso coge la geom de la secto
                //obtiene el polígono que engloba los ámbitos
                geom_zonniv = FunGDB.dame_geom_envelope(path_secto);
                if (geom_zonniv == null)
                {
                    err_st = "Error al obtener polígono de datos";
                    return false;
                }
                hayzonniv = false;
            }
            if (geom_zonniv == null && nomb_class_limp == OliviaGlob.Capas.ftclass_ejes)
            {
                err_st = "Al emplear ejes de calle como ámbitos es necesario indicar polígono de exportación";
                return false;
            }
            
            /////////////////////////////////////////////
            //crea el filtro en base a las consultas configuradas
            filtro = crea_filtro(limp, nomb_class_limp, consulta, geom_zonniv);
            if (filtro == null)
            {
                err_st = "Error al crear el filtro para la exportación";
                return false;
            }
            
            /////////////////////////////////////////////
            //se consigue el tiempo en este instante para añadirlo a los nombres de los archivos de salida (shapefiles)
            fecha_hora = DateTime.Now.ToString("yyyyMMdd_Hmmss");

            /////////////////////////////////////////////
            //exporta los ámbitos
            if (!exporta_data(limp, filtro, nomb_class_limp, ambitos, fecha_hora, out datasetn))
            {
                err_st = "Error al exportar data. "+err_st;
                return false;
            }

            OliviaGlob.Paths.PathData = OliviaGlob.Paths.DirData + datasetn.Name;

            //hace intersecciones espaciales en caso de ámbitos lineales para quitar los que tienen más parte fuera de la zona que dentro
            if (geom_zonniv != null && (limp.tipo_tto < (int)LimpiezaDef.TiposTto.TtoPapeVaci) && hayzonniv)
            {
                if (!quita_amb_sobresalen(OliviaGlob.Paths.PathData, geom_zonniv,0.4))
                {
                    err_st = "Error al quitar los ámbitos que sobresalen";
                    return false;
                }
            }

            //quita los ámbitos que caen en la zona de restricciones, si es que no había zonas y no se ha quitado ya
            if (geom_zonniv == null && limp.geom_rest_acces != null)
            {
                if (!quita_amb_restric(OliviaGlob.Paths.PathData, limp.geom_rest_acces, (limp.tipo_tto < (int)LimpiezaDef.TiposTto.TtoPapeVaci)))
                {
                    err_st = "Error al quitar los ámbitos en zona de restricción";
                    return false;
                }
            }

            //comprueba que sigue habiendo ámbitos exportados
            cont = FunGDB.cuenta_filas_ftclass(OliviaGlob.Paths.PathData, "");
            if (cont <= 0)
            {
                err_st = "No existen ámbitos que cumplan las condiciones geométricas introducidas";
                return false;
            }   

            //ahora si está en modo planificación exporta la sectorización
            if (comp_planif && (path_secto!=null))
            {
                if (!actualiza_secto(OliviaGlob.Paths.PathData, path_secto))
                {
                    err_st = "Error al exportar campos de SECTOR y/o SECUENCIA de la capa "+ path_secto + " " + err_st;
                    return false;
                }
            }
                        
            ///////////////////////////////////////////////////////////////////////////            
            //obtiene el polígono que engloba los ámbitos
            geom_ambits = FunGDB.dame_geom_envelope(OliviaGlob.Paths.PathData);
            if (geom_ambits == null)
            {
                err_st = "Error al obtener polígono de datos";
                return false;
            } 

            ///////////////////////////////////////////////////////////////////////////            
            //amplia el poligono para englobar las instalaciones
            if (limp.coords_instala[0] != 0 && limp.coords_instala[1] != 0)
            {
                //hay instalación
                if (!FunGDB.is_pto_in_geom(limp.coords_instala[0], limp.coords_instala[1], geom_ambits))
                    geom_ambits = FunGDB.amplia_geom(geom_ambits, limp.coords_instala[0], limp.coords_instala[1]);

                //comprueba, si hay restricciones de circulación, que la instalación no está en ellas
                if (limp.geom_rest_acces != null)
                {
                    if (FunGDB.is_pto_in_geom(limp.coords_instala[0], limp.coords_instala[1], limp.geom_rest_acces))
                    {
                        err_st = "Error, la instalación sal/lleg está en la zona restringida a la circulación";
                        return false;
                    }
                }
            }

            ///////////////////////////////////////////////////////////////////////////     
            //exporta la red navegable
            if (!exporta_nw(geom_ambits, limp.geom_rest_acces,limp.rest_circ[limp.tipo_tto], fecha_hora, out datasetn))
            {
                return false;
            }

            //guarda los nombres del shape
            OliviaGlob.Paths.PathNW = OliviaGlob.Paths.DirData + datasetn.Name;
            
            return true;
        }

        /*
         * Dado un nombre de featureclass, una consulta y una geometría, interseca entre un poligono dado (geom) y una capa lineal (fc1)
         */
        public IQueryFilter dame_filt_inter(Limpieza limp, string ftclass, string consulta, IGeometry geom)
        {
            string dir = null;
            IFeatureClass fc = null;
            IQueryFilter fil= null;

            if (ftclass == OliviaGlob.Capas.ftclass_ejes)
                dir = OliviaGlob.Paths.PathGdbNw;
            else if (path_class == null)
                dir = OliviaGlob.Paths.PathGdbGen;

            if (dir != null)
                fc = FunGDB.abre_ftclass(dir, ftclass);
            else
                fc = FunGDB.abre_ftclass(path_class);

            if (fc == null)
                return null;

            fil=base.dame_filt_inter(fc, consulta, geom);
            FunGDB.libera(fc);

            return fil;
        }

        /**
         * Realiza las intersecciones espaciales de los ámbitos
         */
        public IQueryFilter crea_filtro(Limpieza limp, string nomb_class, string consulta, IGeometry geom1)
        {
            IQueryFilter filtro;

            //crea el filtro espacial y el añadido de consulta de atributos si fuera necesario
            filtro = dame_filt_inter(limp,nomb_class, consulta, geom1);
            if (filtro == null)
            {
                err_st = "Error al intersecar el ámbito de limpieza con la zona de estudio";
                return null;
            }

            //FALTA REDEFINIR - elena jun 18
            //-------------------------------------------------------------------------------------------------------------------
            //Cuando se trate del vaciado de papeleras habrá que verificar si está marca el checkbox que indica si sólo van a recogerse los que superen el umbral de carga.
            //De modo que la secotrizacion y la planificacion se realizará sólo sobre los contenedores que superan un porcetaje de carga en la basura.
            //Se conocen esos contenedores por el nombre de un campo que indicará si SI(hay que ir a recogerlos) o NO (no tienen suficiente carga y se pueden recoger en otro momento)
            if ((limp.tipo_tto == (int)LimpiezaDef.TiposTto.TtoPapeVaci) && (limp.umbral_reco_conten == true))
            {
                if (filtro.WhereClause == "")
                    filtro.WhereClause = carga_conten + " >= " + limp.umbral_basura;
                else
                    filtro.WhereClause = "(" + filtro.WhereClause + ") AND (" + carga_conten + " >= " + limp.umbral_basura + ")";
            }

            return filtro;
        }


        /**
         * Devuelve el string a concatenar en el nombre del path dependiendo de los polígonos seleccionados (zonas, turnos... etc)
         */
        public string dame_str_poligs(Limpieza limp)
        {
            string str = "";

            if (limp.text_niv != "" && limp.geom_niveles != null)
                str += "_N" + limp.text_niv;
            if(limp.text_zon!="" && limp.geom_zonas!=null)
                str += "_Z" + limp.text_zon;
            if (limp.text_restr != "" && limp.geom_rest_acces != null)
                str += "_R" + limp.text_restr;

            return str;
        }

        /**
         * Exporta los ámbitos de la gdb a un shape en datasetname
         */
        public bool exporta_data(Limpieza limp, IQueryFilter filtro, string nomb_class, string ambitos, string fecha_hora, out IDatasetName shp_data_name)
        {
            string name="", err_str="";
            string dir = null;
            IDatasetName shp_dsn = null;

            shp_data_name = null;

            //Pone nombre al shape en función de los ámbitos, el tratamiento, y los polígonos + timestamp
            name = name_export_amb + "T" + limp.tipo_tto.ToString("00") + ambitos + dame_str_poligs(limp) + "_" + fecha_hora + ext_shp;
            
            if (nomb_class == OliviaGlob.Capas.ftclass_ejes)
                dir = OliviaGlob.Paths.PathGdbNw;
            else if (path_class == null)
                dir = OliviaGlob.Paths.PathGdbGen;

            if (dir == null)
            {
                dir = path_class;
                nomb_class = null;
            }

            inicia_coords(dir, nomb_class);

            if (!FunGDB.exporta(dir, nomb_class, OliviaGlob.Paths.DirData, name, filtro, out shp_dsn, out err_str))
            {
                err_st = err_str;
                return false;
            }
			
            shp_data_name = (IDatasetName)shp_dsn;
            return true;
        }
    }
}
