using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text;
using System.Xml;
using System.Xml.XPath;

namespace GMLC01HerramientasCad.Logica
{
    class GML
    {
        //internal Metadatos Metadatos { get; set; }
        internal ListaAtributos Metadatos { get; set; }
        internal Collection<Elemento> Elementos { get; set; }
        internal ConfigGml ConfigExport { get; set; }

        internal string ficheroOrigen;

        #region Generación de GML

        private static void GenerarHeader(System.IO.StreamWriter fOut,ConfigGml configExport)
        {
            //fOut.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");//"ISO-8859-1\"?>");
            fOut.WriteLine(configExport.Head.Trim());
        }

        private static void GenerarGeometriaPuntual(Elemento elemento, System.IO.StreamWriter fOut,ConfigGml configExport)
        {
            String parametros = "";
            if (configExport.Srs != null && configExport.Srs.Length!=0)
                parametros = " srsName=\"" + configExport.Srs+ "\"";
            if (configExport.Dimension != null && configExport.Dimension.Length!=0)
                parametros += " srsDimension=\"" + configExport.Dimension + "\"";

            fOut.WriteLine("\t\t\t<gml:Point" + parametros + ">");

            if(configExport.CoordinatesFormat["point"]!= null && configExport.CoordinatesFormat["point"].ToString().ToLower(CultureInfo.CurrentCulture).Equals("pos"))
            {
                fOut.WriteLine("\t\t\t\t<gml:pos>");

                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    fOut.WriteLine("\t\t\t\t\t" + elemento.Coordenadas[0].CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                  elemento.Coordenadas[0].CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                  elemento.Coordenadas[0].CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                }
                else
                {
                    fOut.WriteLine("\t\t\t\t\t" + elemento.Coordenadas[0].CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                  elemento.Coordenadas[0].CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                }
                fOut.WriteLine("\t\t\t\t</gml:pos>");
            }
            else
            {
                fOut.WriteLine("\t\t\t\t<gml:coordinates>");
                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    fOut.WriteLine("\t\t\t\t\t" + elemento.Coordenadas[0].CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                  elemento.Coordenadas[0].CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                  elemento.Coordenadas[0].CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                }
                else {
                    fOut.WriteLine("\t\t\t\t\t" + elemento.Coordenadas[0].CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                  elemento.Coordenadas[0].CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                }
                fOut.WriteLine("\t\t\t\t</gml:coordinates>");
                
            }

            fOut.WriteLine("\t\t\t</gml:Point>");
        }

        private static void GenerarGeometriaLineal(Elemento elemento, System.IO.StreamWriter fOut, ConfigGml configExport)
        {
            String parametros = "";
            if (configExport.Srs != null && configExport.Srs.Length != 0)
                parametros = " srsName=\"" + configExport.Srs + "\"";
            if (configExport.Dimension != null && configExport.Dimension.Length != 0)
                parametros += " srsDimension=\"" + configExport.Dimension + "\"";
            
            fOut.WriteLine("\t\t\t<gml:LineString" + parametros+ ">");

            if (configExport.CoordinatesFormat["linestring"]!=null && configExport.CoordinatesFormat["linestring"].ToString().ToLower(CultureInfo.CurrentCulture).Equals("pos"))
            {
                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                    {
                        fOut.WriteLine("\t\t\t\t<gml:pos>");
                        fOut.WriteLine("\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                        fOut.WriteLine("\t\t\t\t</gml:pos>");
                    }
                }
                else
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                    {
                        fOut.WriteLine("\t\t\t\t<gml:pos>");
                        fOut.WriteLine("\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                        fOut.WriteLine("\t\t\t\t</gml:pos>");
                    }
                }
                
            }
            else if (configExport.CoordinatesFormat["linestring"]!=null && configExport.CoordinatesFormat["linestring"].ToString().ToLower(CultureInfo.CurrentCulture).Equals("poslist"))
            {
                /*
                fOut.WriteLine("\t\t\t\t<gml:posList>");
                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    string prefix = "\t\t\t\t\t";
                    foreach (Coordenada coord in elemento.Coordenadas)
                        prefix += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                    fOut.WriteLine(prefix.TrimEnd(' '));
                }
                else
                {
                    string prefix = "\t\t\t\t\t";
                    foreach (Coordenada coord in elemento.Coordenadas)
                        prefix += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                    fOut.WriteLine(prefix.TrimEnd(' '));
                }
                fOut.WriteLine("\t\t\t\t</gml:posList>");
                */
                String lin = "\t\t\t\t<gml:posList>";
                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                        lin += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                }
                else
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                        lin += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                }
                fOut.WriteLine(lin.TrimEnd(' ') + "</gml:posList>");
            }
            else
            {
                fOut.WriteLine("\t\t\t\t<gml:coordinates>");
                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                        fOut.WriteLine("\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                      coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                }
                else {
                    foreach (Coordenada coord in elemento.Coordenadas)
                        fOut.WriteLine("\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                }
                fOut.WriteLine("\t\t\t\t</gml:coordinates>");
            }

            fOut.WriteLine("\t\t\t</gml:LineString>");
        }

        private static void GenerarGeometriaPoligonal(Elemento elemento, System.IO.StreamWriter fOut, ConfigGml configExport)
        {
            String parametros = "";
            if (configExport.Srs != null && configExport.Srs.Length != 0)
                parametros = " srsName=\"" + configExport.Srs + "\"";
            if (configExport.Dimension != null && configExport.Dimension.Length != 0)
                parametros += " srsDimension=\"" + configExport.Dimension + "\"";

            fOut.WriteLine("\t\t\t<gml:Polygon" + parametros + ">");
            fOut.WriteLine("\t\t\t\t<gml:outerBoundaryIs>");
            fOut.WriteLine("\t\t\t\t\t<gml:LinearRing>");

            // LINEA EXTERIOR
            if (configExport.CoordinatesFormat["polygon"] != null && configExport.CoordinatesFormat["polygon"].ToString().ToLower(CultureInfo.CurrentCulture).Equals("pos"))
            {
                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                    {
                        fOut.WriteLine("\t\t\t\t\t\t<gml:pos>");
                        fOut.WriteLine("\t\t\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                        fOut.WriteLine("\t\t\t\t\t\t</gml:pos>");
                    }
                }
                else
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                    {
                        fOut.WriteLine("\t\t\t\t\t\t<gml:pos>");
                        fOut.WriteLine("\t\t\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                        fOut.WriteLine("\t\t\t\t\t\t</gml:pos>");
                    }
                }

            }
            else if (configExport.CoordinatesFormat["polygon"] != null && configExport.CoordinatesFormat["polygon"].ToString().ToLower(CultureInfo.CurrentCulture).Equals("poslist"))
            {
                /*
                fOut.WriteLine("\t\t\t\t\t\t<gml:posList>");
                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    string prefix = "\t\t\t\t\t\t\t";
                    foreach (Coordenada coord in elemento.Coordenadas)
                        prefix += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                    fOut.WriteLine(prefix.TrimEnd(' '));
                }
                else
                {
                    string prefix = "\t\t\t\t\t\t\t";
                    foreach (Coordenada coord in elemento.Coordenadas)
                        prefix += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                    fOut.WriteLine(prefix.TrimEnd(' '));
                }
                fOut.WriteLine("\t\t\t\t\t\t</gml:posList>");
                */
                String lin = "\t\t\t\t\t\t<gml:posList>";
                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                        lin += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                }
                else
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                        lin += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                }
                fOut.WriteLine(lin.TrimEnd(' ') + "</gml:posList>");
            }
            else
            {
                fOut.WriteLine("\t\t\t\t\t\t<gml:coordinates>");
                if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                        fOut.WriteLine("\t\t\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                      coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                }
                else
                {
                    foreach (Coordenada coord in elemento.Coordenadas)
                        fOut.WriteLine("\t\t\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                      coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                }
                fOut.WriteLine("\t\t\t\t\t\t</gml:coordinates>");
            }


            // LINEA INTERIOR

            fOut.WriteLine("\t\t\t\t\t</gml:LinearRing>");
            fOut.WriteLine("\t\t\t\t</gml:outerBoundaryIs>");

            foreach (Ring interior in elemento.Interiores)
            {
                fOut.WriteLine("\t\t\t\t<gml:innerBoundaryIs>");
                fOut.WriteLine("\t\t\t\t\t<gml:LinearRing>");


                if (configExport.CoordinatesFormat["polygon"] != null && configExport.CoordinatesFormat["polygon"].ToString().ToLower(CultureInfo.CurrentCulture).Equals("pos"))
                {
                    if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                    {
                        foreach (Coordenada coord in interior.Coordenadas)
                        {
                            fOut.WriteLine("\t\t\t\t\t\t<gml:pos>");
                            fOut.WriteLine("\t\t\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                          coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                          coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                            fOut.WriteLine("\t\t\t\t\t\t</gml:pos>");
                        }
                    }
                    else
                    {
                        foreach (Coordenada coord in interior.Coordenadas)
                        {
                            fOut.WriteLine("\t\t\t\t\t\t<gml:pos>");
                            fOut.WriteLine("\t\t\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                          coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                            fOut.WriteLine("\t\t\t\t\t\t</gml:pos>");
                        }
                    }

                }
                else if (configExport.CoordinatesFormat["polygon"] != null && configExport.CoordinatesFormat["polygon"].ToString().ToLower(CultureInfo.CurrentCulture).Equals("poslist"))
                {
                    /*
                    fOut.WriteLine("\t\t\t\t\t\t<gml:posList>");
                    if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                    {
                        string prefix = "\t\t\t\t\t\t\t";
                        foreach (Coordenada coord in interior.Coordenadas)
                            prefix += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                          coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                          coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                        fOut.WriteLine(prefix.TrimEnd(' '));
                    }
                    else
                    {
                        string prefix = "\t\t\t\t\t\t\t";
                        foreach (Coordenada coord in interior.Coordenadas)
                            prefix += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                          coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                        fOut.WriteLine(prefix.TrimEnd(' '));
                    }
                    fOut.WriteLine("\t\t\t\t\t\t</gml:posList>");
                    */
                    String lin = "\t\t\t\t\t\t<gml:posList>";
                    if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                    {
                        foreach (Coordenada coord in interior.Coordenadas)
                            lin += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                          coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                          coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                    }
                    else
                    {
                        foreach (Coordenada coord in interior.Coordenadas)
                            lin += coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                                          coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " ";
                    }
                    fOut.WriteLine(lin.TrimEnd(' ') + "</gml:posList>");
                }
                else
                {
                    fOut.WriteLine("\t\t\t\t\t\t<gml:coordinates>");
                    if (configExport.Dimension.Equals("3") || string.IsNullOrEmpty(configExport.Dimension))
                    {
                        foreach (Coordenada coord in interior.Coordenadas)
                            fOut.WriteLine("\t\t\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                          coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                          coord.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                    }
                    else
                    {
                        foreach (Coordenada coord in interior.Coordenadas)
                            fOut.WriteLine("\t\t\t\t\t\t\t" + coord.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + "," +
                                                          coord.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.'));
                    }
                    fOut.WriteLine("\t\t\t\t\t\t</gml:coordinates>");
                }

                fOut.WriteLine("\t\t\t\t\t</gml:LinearRing>");
                fOut.WriteLine("\t\t\t\t</gml:innerBoundaryIs>");
            }

            fOut.WriteLine("\t\t\t</gml:Polygon>");
        }

        private static void GenerarGeometria(Elemento elemento, System.IO.StreamWriter fOut, ConfigGml configExport)
        {
            fOut.WriteLine("\t\t<app:the_geom>");
            switch (ModComun.XSD.Entidades[elemento.CodigoEntidad].TipoGeometria)
            {
                case TiposGeometria.Punto:
                    GenerarGeometriaPuntual(elemento, fOut, configExport);
                    break;
                case TiposGeometria.Linea:
                    GenerarGeometriaLineal(elemento, fOut, configExport);
                    break;
                case TiposGeometria.Area:
                    GenerarGeometriaPoligonal(elemento, fOut, configExport);
                    break;
            }
            fOut.WriteLine("\t\t</app:the_geom>");
        }

        private static void GenerarAtributos(Elemento elemento, System.IO.StreamWriter fOut, ConfigGml configExport)
        {
            foreach (Atributo atr in elemento.Atributos)
            {
                String parametros = "";
                if (configExport.NillableGml != null && configExport.NillableGml.ToLower(CultureInfo.CurrentCulture).Equals("true"))
                {
                    if (atr.Nillable && string.IsNullOrEmpty(atr.Valor))
                        parametros += " xsi:nil=\"true\"";
                }
                if (configExport.UomGml != null && configExport.NillableGml.ToLower(CultureInfo.CurrentCulture).Equals("true"))
                {
                    if (atr.Unidades!=null && atr.Unidades.Length!=0)
                        parametros += " uom=\"" + atr.Unidades + "\"";
                }
                fOut.WriteLine("\t\t<app:" + atr.Codigo + parametros + ">" + atr.Valor + "</app:" + atr.Codigo + ">");
            }
        }

        private static void GenerarFeatureMember(Elemento elemento, System.IO.StreamWriter fOut, ConfigGml configExport)
        {
            fOut.WriteLine("<gml:featureMember>");
            if (configExport.IdGml != null && configExport.IdGml.ToLower(CultureInfo.CurrentCulture).Equals("true"))
                fOut.WriteLine("\t<app:" + elemento.CodigoEntidad + " gml:id=\"" + elemento.Atributos[ModComun.XSD.Clave].Valor + "\">");
            else
                fOut.WriteLine("\t<app:" + elemento.CodigoEntidad + ">");
            GenerarGeometria(elemento, fOut, configExport);
            GenerarAtributos(elemento, fOut, configExport);
            fOut.WriteLine("\t</app:" + elemento.CodigoEntidad + ">");
            fOut.WriteLine("</gml:featureMember>");
        }

        private void GenerarFeatureCollection(System.IO.StreamWriter fOut,ConfigGml configExport)
        {
            /*
            fOut.WriteLine("<gml:FeatureCollection xmlns:app=\"http://www.ccasa.com/ccasa\" " +
                        "xmlns:gml=\"http://www.opengis.net/gml\" " +
                        "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
                        "xsi:schemaLocation=\"http://www.ccasa.com/ccasa entidades.XSD\">");
            */
            fOut.WriteLine();
            GenerarMetadatos(fOut);
            fOut.WriteLine();
            GenerarBoundedBy(fOut, configExport);
            fOut.WriteLine();

            foreach (Elemento elemento in Elementos)
            {
                GenerarFeatureMember(elemento, fOut, configExport);
                fOut.WriteLine();
            }

            fOut.WriteLine("</gml:FeatureCollection>");
        }

        private void GenerarMetadatos(System.IO.StreamWriter fOut)
        {
            fOut.WriteLine("<gml:metaDataProperty>");
            fOut.WriteLine("\t<app:metadata>");
            //fOut.WriteLine("\t\t<app:expediente>" + Metadatos.Expediente + "</app:expediente>");
            //fOut.WriteLine("\t\t<app:fecha>" + Metadatos.Fecha + "</app:fecha>");
            //fOut.WriteLine("\t\t<app:proveedor>" + Metadatos.Proveedor + "</app:proveedor>");
            //fOut.WriteLine("\t\t<app:titulo>" + Metadatos.Titulo + "</app:titulo>");
            //fOut.WriteLine("\t\t<app:descripcion>" + Metadatos.Descripcion + "</app:descripcion>");
            foreach (Atributo att in Metadatos)
            {
                if (att.Nillable && string.IsNullOrEmpty(att.Valor))
                    fOut.WriteLine("\t\t<app:" + att.Codigo + " xsi:nil=\"true\" />");
                else
                    fOut.WriteLine("\t\t<app:" + att.Codigo + ">" + att.Valor + "</app:" + att.Codigo + ">");
            }
            fOut.WriteLine("\t</app:metadata>");
            fOut.WriteLine("</gml:metaDataProperty>");
        }

        private void GenerarBoundedBy(System.IO.StreamWriter fOut, ConfigGml configExport)
        {
            fOut.WriteLine("<gml:boundedBy>");
            fOut.WriteLine("\t<gml:Envelope srsName=\"" + configExport.Srs + "\">");
            Coordenada min = new Coordenada(float.MaxValue, float.MaxValue, float.MaxValue);
            Coordenada max = new Coordenada(float.MinValue, float.MinValue, float.MinValue);
            foreach (Elemento ele in Elementos)
                foreach (Coordenada coord in ele.Coordenadas)
                {
                    min.CoordX = (float)Math.Min(min.CoordX, coord.CoordX);
                    min.CoordY = (float)Math.Min(min.CoordY, coord.CoordY);
                    min.CoordZ = (float)Math.Min(min.CoordZ, coord.CoordZ);
                    max.CoordX = (float)Math.Max(max.CoordX, coord.CoordX);
                    max.CoordY = (float)Math.Max(max.CoordY, coord.CoordY);
                    max.CoordZ = (float)Math.Max(max.CoordZ, coord.CoordZ);
                }
            fOut.WriteLine("\t\t\t<gml:lowerCorner>" +
                                min.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                min.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                min.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.') +
                           "</gml:lowerCorner>");
            fOut.WriteLine("\t\t\t<gml:upperCorner>" +
                                max.CoordX.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                max.CoordY.ToString(CultureInfo.InvariantCulture).Replace(',', '.') + " " +
                                max.CoordZ.ToString(CultureInfo.InvariantCulture).Replace(',', '.') +
                           "</gml:upperCorner>");

            fOut.WriteLine("\t</gml:Envelope>");
            fOut.WriteLine("</gml:boundedBy>");
        }

        internal void Generar(String fichero,ConfigGml confGml)
        {
            this.ConfigExport = confGml;
            if (Elementos == null || Elementos.Count == 0)
                throw new ExcepcionGMLC(Properties.Resources.NoHayElementosQueSalvar);

            System.IO.StreamWriter fOut = new System.IO.StreamWriter(fichero);
            GenerarHeader(fOut, this.ConfigExport);
            GenerarFeatureCollection(fOut,this.ConfigExport);
            fOut.Close();
        }
        #endregion

        #region Lectura de GML
        
        private XmlDocument gmlDoc;
        private XmlNamespaceManager gmlNSM;

        private void LeerMetadatos()
        {
            //XmlNode iExpediente = gmlDoc.SelectSingleNode("/gml:FeatureCollection/gml:metaDataProperty/app:metadata/app:expediente", gmlNSM);
            //if (iExpediente == null)
            //    Metadatos.Expediente = "";
            //    //throw new ExcepcionGMLC(Properties.Resources.NoSeEncontroLaDefinicionDelMetadatoExpedienteEnElFicheroGML);
            //else
            //    Metadatos.Expediente = iExpediente.InnerText;

            //XmlNode iFecha = gmlDoc.SelectSingleNode("/gml:FeatureCollection/gml:metaDataProperty/app:metadata/app:fecha", gmlNSM);
            //if (iFecha == null)
            //    Metadatos.Fecha = "";
            //    //throw new ExcepcionGMLC(Properties.Resources.NoSeEncontroLaDefinicionDelMetadatoFechaEnElFicheroGML);
            //else
            //    Metadatos.Fecha = iFecha.InnerText;

            //XmlNode iProveedor = gmlDoc.SelectSingleNode("/gml:FeatureCollection/gml:metaDataProperty/app:metadata/app:proveedor", gmlNSM);
            //if (iProveedor == null)
            //    Metadatos.Proveedor = "";
            //    //throw new ExcepcionGMLC(Properties.Resources.NoSeEncontroLaDefinicionDelMetadatoProveedorEnElFicheroGML);
            //else
            //    Metadatos.Proveedor = iProveedor.InnerText;

            //XmlNode iTitulo = gmlDoc.SelectSingleNode("/gml:FeatureCollection/gml:metaDataProperty/app:metadata/app:titulo", gmlNSM);
            //if (iTitulo == null)
            //    Metadatos.Titulo = "";
            //    //throw new ExcepcionGMLC(Properties.Resources.NoSeEncontroLaDefinicionDelMetadatoTituloEnElFicheroGML);
            //else
            //    Metadatos.Titulo = iTitulo.InnerText;

            //XmlNode iDescripcion = gmlDoc.SelectSingleNode("/gml:FeatureCollection/gml:metaDataProperty/app:metadata/app:descripcion", gmlNSM);
            //if (iDescripcion == null)
            //    Metadatos.Descripcion = "";
            //    //throw new ExcepcionGMLC(Properties.Resources.NoSeEncontroLaDefinicionDelMetadatoDescripcionEnElFicheroGML);
            //else
            //    Metadatos.Descripcion = iDescripcion.InnerText;

            foreach (Atributo att in Metadatos)
            {
                XmlNode iNode = gmlDoc.SelectSingleNode("/gml:FeatureCollection/gml:metaDataProperty/app:metadata/app:" + att.Codigo + "", gmlNSM);
                if (iNode == null)
                    att.Valor = "";
                    //throw new ExcepcionGMLC(Properties.Resources.NoSeEncontroLaDefinicionDelMetadatoDescripcionEnElFicheroGML);
                else
                    att.Valor = iNode.InnerText;
            }
        }

        private void LeerAtributo(XPathNavigator nav, Elemento elem)
        {
            //if (elem.Atributos [nav.Name.Replace("app:", "")]!=null)
            try
            {
                    elem.Atributos[nav.Name.Replace("app:", "")].Valor = nav.Value;
                    if (!string.IsNullOrEmpty(nav.Value) && (elem.Atributos[nav.Name.Replace("app:", "")].Action + "").Equals(GMLC01HerramientasCad.Logica.Properties.Resources.abrirFichero, StringComparison.OrdinalIgnoreCase))
                        elem.Atributos[nav.Name.Replace("app:", "")].Valor = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(ficheroOrigen), elem.Atributos[nav.Name.Replace("app:", "")].Valor);
            }
            catch (KeyNotFoundException)
            {
            }
        }

        private static String NormalizarEspacios(String input)
        {
            String output = "";
            bool enEspacio = true;
            String espacios = "\t\n\r ";

            for (int index = 0; index < input.Length; index++)
            {
                if (espacios.Contains(input.Substring(index, 1)))
                {
                    if (enEspacio)
                        continue;
                    else
                        output += " ";

                    enEspacio = true;
                }
                else
                {
                    output += input.Substring(index, 1);
                    enEspacio = false;
                }
            }

            return output;
        }

        private static void LeerPoint(XPathNavigator nav, Elemento elem, String dimension)
        {

            String[] coord = null;
            String valor;
            int dim = 3;
            try
            {
                dim = Convert.ToInt32(dimension, CultureInfo.InvariantCulture);
            }
            catch (FormatException) { dim = 3; }

            nav.MoveToFirstChild();
            valor = NormalizarEspacios(nav.Value).TrimEnd(' ');

            // Comprobamos el formato de geometria
            if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:pos"))
            {
                char[] delimCoord = { ' ' };
                coord = valor.Split(delimCoord);
            }
            else if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:coordinates"))
            {
                char[] delimCoord = { ',' };
                coord = valor.Split(delimCoord);
            }
            if (dim == 2)
            {
                elem.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                    double.Parse(coord[1], CultureInfo.InvariantCulture), 0));
            }
            else
            {
                elem.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                    double.Parse(coord[1], CultureInfo.InvariantCulture),
                                                    double.Parse(coord[2], CultureInfo.InvariantCulture)));
            }
            nav.MoveToParent();
        }

        private static void LeerLineString(XPathNavigator nav, Elemento elem, String dimension)
        {
            String valor;
            int dim = 3;
            try
            {
                dim = Convert.ToInt32(dimension, CultureInfo.InvariantCulture);
            }
            catch (FormatException) { dim = 3; }

            nav.MoveToFirstChild();

            // Comprobamos el formato de geometria
            if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:pos"))
            {
                char[] delimCoord = { ' ' };
                String[] coordenadas;
                do
                {
                    coordenadas = NormalizarEspacios(nav.Value).Split(delimCoord);
                    if (dim == 2)
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[0], CultureInfo.InvariantCulture),
                                                  double.Parse(coordenadas[1], CultureInfo.InvariantCulture),
                                                  0));
                    }
                    else
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[0], CultureInfo.InvariantCulture),
                                                      double.Parse(coordenadas[1], CultureInfo.InvariantCulture),
                                                      double.Parse(coordenadas[2], CultureInfo.InvariantCulture)));
                    }
                } while (nav.MoveToNext());
            }
            else if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:coordinates"))
            {
                valor = NormalizarEspacios(nav.Value).TrimEnd(' ');
                char[] delimPar = { ' ' };
                char[] delimCoord = { ',' };
                String[] pares;
                String[] coord;
                pares = valor.Split(delimPar);

                foreach (String par in pares)
                {
                    coord = par.Split(delimCoord);
                    if (dim == 2)
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                          double.Parse(coord[1], CultureInfo.InvariantCulture),
                                                          0));
                    }
                    else {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                        double.Parse(coord[1], CultureInfo.InvariantCulture),
                                                        double.Parse(coord[2], CultureInfo.InvariantCulture)));
                    }
                    
                }
            }
            else if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:poslist"))
            {
                nav.MoveToFirstChild();
                valor = NormalizarEspacios(nav.Value).TrimEnd(' ');
                char[] delimCoord = { ' ' };
                String[] coordenadas;
                coordenadas = valor.Split(delimCoord);
                if (dim == 2)
                {
                    for (int i = 0; i < coordenadas.Length; i = i + 2) {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[i], CultureInfo.InvariantCulture),
                                                          double.Parse(coordenadas[i + 1], CultureInfo.InvariantCulture),
                                                          0));
                    }
                }
                else {
                    for (int i = 0; i < coordenadas.Length; i = i + 3)
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[i], CultureInfo.InvariantCulture),
                                                          double.Parse(coordenadas[i + 1], CultureInfo.InvariantCulture),
                                                          double.Parse(coordenadas[i + 2], CultureInfo.InvariantCulture)));
                    }
                }
            }

            nav.MoveToParent();

        }

        private static void LeerPolygon(XPathNavigator nav, Elemento elem,String dimension)
        {
            String valor;
            int dim = 3;
            try
            {
                dim = Convert.ToInt32(dimension, CultureInfo.InvariantCulture);
            }
            catch (FormatException) { dim = 3; }

            //ANILLO EXTERIOR
            // Comprobamos el formato de geometria
            nav.MoveToFirstChild();
            nav.MoveToFirstChild();
            nav.MoveToFirstChild();
            if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:pos"))
            {
                char[] delimCoord = { ' ' };
                String[] coordenadas;
                do
                {
                    coordenadas = NormalizarEspacios(nav.Value).Split(delimCoord);
                    if (dim == 2)
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[0], CultureInfo.InvariantCulture),
                                                  double.Parse(coordenadas[1], CultureInfo.InvariantCulture),
                                                  0));
                    }
                    else
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[0], CultureInfo.InvariantCulture),
                                                      double.Parse(coordenadas[1], CultureInfo.InvariantCulture),
                                                      double.Parse(coordenadas[2], CultureInfo.InvariantCulture)));
                    }
                } while (nav.MoveToNext());
            }
            else if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:coordinates"))
            {
                valor = NormalizarEspacios(nav.Value).TrimEnd(' ');
                char[] delimPar = { ' ' };
                char[] delimCoord = { ',' };
                String[] pares;
                String[] coord;
                pares = valor.Split(delimPar);

                foreach (String par in pares)
                {
                    coord = par.Split(delimCoord);
                    if (dim == 2)
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                          double.Parse(coord[1], CultureInfo.InvariantCulture),
                                                          0));
                    }
                    else
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                        double.Parse(coord[1], CultureInfo.InvariantCulture),
                                                        double.Parse(coord[2], CultureInfo.InvariantCulture)));
                    }

                }
            }
            else if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:poslist"))
            {
                valor = NormalizarEspacios(nav.Value).TrimEnd(' ');
                char[] delimCoord = { ' ' };
                String[] coordenadas;
                coordenadas = valor.Split(delimCoord);
                if (dim == 2)
                {
                    for (int i = 0; i < coordenadas.Length; i = i + 2)
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[i], CultureInfo.InvariantCulture),
                                                          double.Parse(coordenadas[i + 1], CultureInfo.InvariantCulture),
                                                          0));
                    }
                }
                else
                {
                    for (int i = 0; i < coordenadas.Length; i = i + 3)
                    {
                        elem.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[i], CultureInfo.InvariantCulture),
                                                          double.Parse(coordenadas[i + 1], CultureInfo.InvariantCulture),
                                                          double.Parse(coordenadas[i + 2], CultureInfo.InvariantCulture)));
                    }
                }
            }
            nav.MoveToParent();
            nav.MoveToParent();

            // ANILLOS INTERIORES
            while (nav.MoveToNext())//.MoveToFollowing("gml:innerBoundaryIs", ""))
            {
                Ring ring = new Ring();

                nav.MoveToFirstChild();
                nav.MoveToFirstChild();

                if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:pos"))
                {
                    char[] delimCoord = { ' ' };
                    String[] coordenadas;
                    do
                    {
                        coordenadas = NormalizarEspacios(nav.Value).Split(delimCoord);
                        if (dim == 2)
                        {
                            ring.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[0], CultureInfo.InvariantCulture),
                                                      double.Parse(coordenadas[1], CultureInfo.InvariantCulture),
                                                      0));
                        }
                        else
                        {
                            ring.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[0], CultureInfo.InvariantCulture),
                                                          double.Parse(coordenadas[1], CultureInfo.InvariantCulture),
                                                          double.Parse(coordenadas[2], CultureInfo.InvariantCulture)));
                        }
                    } while (nav.MoveToNext());

                }
                else if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:coordinates"))
                {
                    valor = NormalizarEspacios(nav.Value).TrimEnd(' ');
                    char[] delimPar = { ' ' };
                    char[] delimCoord = { ',' };
                    String[] pares;
                    String[] coord;
                    pares = valor.Split(delimPar);

                    foreach (String par in pares)
                    {
                        coord = par.Split(delimCoord);
                        if (dim == 2)
                        {
                            ring.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                              double.Parse(coord[1], CultureInfo.InvariantCulture),
                                                              0));
                        }
                        else
                        {
                            ring.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                            double.Parse(coord[1], CultureInfo.InvariantCulture),
                                                            double.Parse(coord[2], CultureInfo.InvariantCulture)));
                        }

                    }
                }
                else if (nav.Name.ToLower(CultureInfo.CurrentCulture).Equals("gml:poslist"))
                {
                    valor = NormalizarEspacios(nav.Value).TrimEnd(' ');
                    char[] delimCoord = { ' ' };
                    String[] coordenadas;
                    coordenadas = valor.Split(delimCoord);
                    if (dim == 2)
                    {
                        for (int i = 0; i < coordenadas.Length; i = i + 2)
                        {
                            ring.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[i], CultureInfo.InvariantCulture),
                                                              double.Parse(coordenadas[i + 1], CultureInfo.InvariantCulture),
                                                              0));
                        }
                    }
                    else
                    {
                        for (int i = 0; i < coordenadas.Length; i = i + 3)
                        {
                            ring.Coordenadas.Add(new Coordenada(double.Parse(coordenadas[i], CultureInfo.InvariantCulture),
                                                              double.Parse(coordenadas[i + 1], CultureInfo.InvariantCulture),
                                                              double.Parse(coordenadas[i + 2], CultureInfo.InvariantCulture)));
                        }
                    }
                }

                nav.MoveToParent();
                nav.MoveToParent();

                elem.Interiores.Add(ring);
            }
            /*
            nav.MoveToFirstChild();

            valor = NormalizarEspacios(nav.value).TrimEnd(' ');

            pares = valor.Split(delimPar);

            foreach (String par in pares)
            {
                coord = par.Split(delimCoord);
                elem.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                    double.Parse(coord[1], CultureInfo.InvariantCulture),
                                                    double.Parse(coord[2], CultureInfo.InvariantCulture)));
            }

            while (nav.MoveToNext())//.MoveToFollowing("gml:innerBoundaryIs", ""))
            {
                Ring ring = new Ring();

                valor = NormalizarEspacios(nav.value).TrimEnd(' ');

                pares = valor.Split(delimPar);

                foreach (String par in pares)
                {
                    coord = par.Split(delimCoord);
                    ring.Coordenadas.Add(new Coordenada(double.Parse(coord[0], CultureInfo.InvariantCulture),
                                                        double.Parse(coord[1], CultureInfo.InvariantCulture),
                                                        double.Parse(coord[2], CultureInfo.InvariantCulture)));
                }
                elem.Interiores.Add(ring);
            }
            */ 
        }

        private static void LeerGeometria(XPathNavigator nav, Elemento elem)
        {
            nav.MoveToFirstChild();
            // Extraemos la dimensión.
            String dimension = nav.GetAttribute("srsDimension", "");
            if (nav.Name == "gml:LineString")
                LeerLineString(nav, elem,(dimension==null)?"3":dimension);
            else if (nav.Name == "gml:Point")
                LeerPoint(nav, elem, (dimension == null) ? "3" : dimension);
            else if (nav.Name == "gml:Polygon")
                LeerPolygon(nav, elem, (dimension == null) ? "3" : dimension);
            else
                throw new ExcepcionGMLC(System.String.Format(CultureInfo.CurrentCulture, Properties.Resources.LaGeometriaNoEstaSoportada, nav.Name));
        }

        private void LeerFeatureMember(XPathNavigator nav)
        {
            if (!nav.MoveToFirstChild())
                throw new ExcepcionGMLC(Properties.Resources.NoSeEncontroUnaEntidadRequerida);

            Elemento elem = new Elemento();

            try
            {
                elem.CodigoEntidad = nav.Name.Replace("app:", "");
            }
            catch (Exception e)
            {
                throw new ExcepcionGMLC(System.String.Format(CultureInfo.CurrentCulture, Properties.Resources.NoPudoLeerElemento, nav.Name), e);
            }

            XPathNodeIterator itr = nav.SelectChildren(XPathNodeType.Element);

            if (itr == null || itr.Count == 0)
                throw new ExcepcionGMLC(System.String.Format(CultureInfo.CurrentCulture, Properties.Resources.NoSeEncontraronAtributosParaEnElFicheroGML, elem.CodigoEntidad));

            while (itr.MoveNext())
            {
                if (itr.Current.Name == "app:the_geom")
                    LeerGeometria(itr.Current.Clone(), elem);
                else
                    LeerAtributo(itr.Current.Clone(), elem);
            }

            //if (!nav.MoveToFirstChild())
            //    throw new ApplicationException("No se encontraron atributos para " + elem.CodigoEntidad + " en el fichero GML.");
            //do
            //{
            //    if (nav.Name == "ccasa:the_geom")
            //        LeerGeometria(nav, elem);
            //    else
            //        LeerAtributo(nav, elem);
            //} while (nav.MoveToNext(XPathNodeType.Element));

            Elementos.Add(elem);
        }

        private void LeerFeatureMembers()
        {
            XPathNavigator nav = gmlDoc.CreateNavigator();
            XPathNodeIterator iterator = nav.Select("/gml:FeatureCollection/gml:featureMember", gmlNSM);

            if (iterator == null || iterator.Count == 0)
                return;

            this.Elementos = new Collection<Elemento>();

            while (iterator.MoveNext())
                LeerFeatureMember(iterator.Current);
        }

        internal void Leer(String fichero)
        {
            this.ficheroOrigen = fichero;
            gmlDoc = new XmlDocument();
            XmlTextReader reader = new XmlTextReader(fichero);
            gmlNSM = new XmlNamespaceManager(reader.NameTable);
            gmlNSM.AddNamespace("gml", "http://www.opengis.net/gml");
            gmlNSM.AddNamespace("app", "http://www.alava.net/gml/infraestructuras/1.0.1");
            gmlDoc.Load(fichero);

            LeerMetadatos();
            LeerFeatureMembers();
        }

        #endregion

        public GML()
        {
            Metadatos = new ListaAtributos(); //Metadatos();
            foreach (Atributo att in ModComun.XSD.Metadata.Atributos)
            {
                Metadatos.Add((Atributo)att.Clone());
            }
        }
    }
}
