using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Serialization;

using GMLC01HerramientasCad.Logica;
using COMUN = GMLC01HerramientasCad.Logica;

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;

namespace GMLC01HerramientasCad.Acad
{
    class Tematicos : ITematicos, IDisposable
    {
        #region ITematicos Members

        private Entidad pEntidadSeleccionada;
        private static string myRecordName = "";
        SeleccionElementoForm frmElementos;

        ObjectId[] oIds;
        Entidad[] ents;
        TematicoDatosInput datos;

        #region "Temático discreto"
        public void TematicoDiscreto(CriterioTematicos criterio)
        {
            //Textos y combos

            Collection<string> colCapas = new Collection<string>();

            int indAtt1 = -1;
            int indAtt2 = -1;

            for (int i = 0; i < criterio.Entidad.Atributos.Count; i++)
            {
                if (criterio.Atributo1.Codigo == criterio.Entidad.Atributos[i].Codigo) indAtt1 = i;
                if (criterio.Atributo2!=null)
                        if (criterio.Atributo2.Codigo == criterio.Entidad.Atributos[i].Codigo) indAtt2 = i;
            }

            for (int i = 0; i < oIds.GetLength(0); i++)
            {
                //Recorrido de las entidades para obtener su capa de destino.
                string nomCapa = "TEM_";
                if (string.IsNullOrEmpty(pEntidadSeleccionada.Atributos[indAtt1].NombreDominio))
                {
                    //No tiene un dominio.
                    nomCapa += criterio.Entidad.Codigo + '-' + ents[i].Atributos[indAtt1].Codigo + '-' + GetAttVal4Layer(ents[i].Atributos[indAtt1].Valor);
                }
                else
                {
                    nomCapa += criterio.Entidad.Codigo + '-' + ents[i].Atributos[indAtt1].Codigo + '-' + GetAttVal4Layer(GetName4DomainValue(pEntidadSeleccionada.Atributos[indAtt1].NombreDominio, ents[i].Atributos[indAtt1].Valor));
                }
                if (indAtt2 >= 0)
                {
                    if (string.IsNullOrEmpty(pEntidadSeleccionada.Atributos[indAtt2].NombreDominio))
                    {
                        //No tiene un dominio.
                        nomCapa += '-' + ents[i].Atributos[indAtt2].Codigo + '-' + GetAttVal4Layer(ents[i].Atributos[indAtt2].Valor);
                    }
                    else
                    {
                        nomCapa += '-' + ents[i].Atributos[indAtt2].Codigo + '-' + GetAttVal4Layer(GetName4DomainValue(pEntidadSeleccionada.Atributos[indAtt2].NombreDominio, ents[i].Atributos[indAtt2].Valor));
                  }
                }
                ReviewLayerName(ref nomCapa);

                if (!colCapas.Contains(nomCapa))
                {
                    LayerManagement.PrepareLayer(nomCapa);
                    colCapas.Add(nomCapa);
                }

                CloneEntity(oIds[i], nomCapa);

            }
            colCapas.Clear();
        }

        private static string GetName4DomainValue(string domainName, string domainValue)
        {
            foreach (ValorDominio dl in ModComun.XSD.Dominios[domainName].Valores)
            {
                if (dl.Codigo == domainValue) return dl.Texto;
            }
            return "";
        }

        private static string GetAttVal4Layer(string attVal)
        {
            if (string.IsNullOrEmpty(attVal)) return "null";
            return attVal;
        }
        #endregion

        #region "Funciones comunes"
        private static void ReviewLayerName(ref string layerName)
        {
            foreach (char ch in "<>/\\\":;?*|,=`".ToCharArray()){
                layerName = layerName.Replace(ch, '_');
            }
            if (layerName.Length > 255) layerName = layerName.Substring(0, 254);
        }

        private static void CloneEntity(ObjectId enteID, string layerName)
        {
            Database db = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Database;
            Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
            Transaction tr = tm.StartTransaction();
            using (tr)
            {
                using (DocumentLock docl = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument())
                {
                    Entity enty = null;
                    Entity cloned = null;

                    enty = (Entity)enteID.GetObject(OpenMode.ForWrite);
                    cloned = (Entity)enty.Clone();

                    BlockTable bt = (BlockTable)tm.GetObject(db.BlockTableId, OpenMode.ForRead, false);
                    BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].GetObject(OpenMode.ForWrite);
                    btr.AppendEntity(cloned);
                    tr.AddNewlyCreatedDBObject(cloned, true);
                    cloned.Layer = layerName;
                    tr.Commit();
                }
            }

        }
        #endregion

        #region "Temático continuo"
        public void TematicoContinuo(CriterioTematicos criterio)
        {
            //Rangos de valores
            Collection<string> colCapas = new Collection<string>();

            int indAtt1 = -1;

            // En este caso sólo puede haber un atributo.
            for (int i = 0; i < criterio.Entidad.Atributos.Count; i++)
            {
                if (criterio.Atributo1.Codigo == criterio.Entidad.Atributos[i].Codigo) indAtt1 = i;
            }

            for (int i = 0; i < oIds.GetLength(0); i++)
            {
                //Recorrido de las entidades para obtener su capa de destino.
                string nomCapa = "TEM_" + criterio.Entidad.Codigo + '-' + ents[i].Atributos[indAtt1].Codigo + '-';
                if (string.IsNullOrEmpty(ents[i].Atributos[indAtt1].Valor))
                {
                    nomCapa += "null";
                }
                else
                {
                    double valor = PunComa(ents[i].Atributos[indAtt1].Valor);
                    for (int j = 0; j < criterio.Rangos.Count; j++)
                    {
                        if (valor >= criterio.Rangos[j].Minimo && valor <= criterio.Rangos[j].Maximo)
                        {
                            nomCapa += "(" + criterio.Rangos[j].Minimo.ToString(System.Globalization.CultureInfo.InvariantCulture) + '-' + criterio.Rangos[j].Maximo.ToString(System.Globalization.CultureInfo.InvariantCulture) + ")";
                            break;
                        }
                    }
                }

                ReviewLayerName(ref nomCapa);

                if (!colCapas.Contains(nomCapa))
                {
                    LayerManagement.PrepareLayer(nomCapa);
                    colCapas.Add(nomCapa);
                }

                CloneEntity(oIds[i], nomCapa);
            }
            colCapas.Clear();
        }
        #endregion

        #endregion

        #region "Obtención de datos de las entidades para hacer los temáticos"

        private bool CheckOIds()
        {
            if (oIds == null) return false;
            if (oIds.GetLength(0) == 0) return false;
            return true;
        }

        private TematicoDatosInput ObtenerDatosTematicosEntidad(Entidad entidad)
        {

            pEntidadSeleccionada=frmElementos.EntidadSeleccionada;
            myRecordName = pEntidadSeleccionada.Codigo;

            //Selección de las entidades del dibujo.
            oIds = SeleccionObjetosAutoCAD.GetEntitiesWithXData(myRecordName);

            if (CheckOIds())
            {
                //Recuperación de los atributos de las entidades.
                ents = new Entidad[oIds.GetLength(0)];
                for (int i = 0; i < oIds.GetLength(0); i++)
                {
                    ents[i] = XData.GetXData(oIds[i], myRecordName);
                }

                AtributosNumericos atrNum = new AtributosNumericos();
                AtributosTexto atrTexto = new AtributosTexto();

                int indAtt = 0;
                foreach (Atributo att in pEntidadSeleccionada.Atributos)
                {
                    switch (att.TipoEdicion)
                    {
                        case TiposEdicion.Entero:
                        case TiposEdicion.Doble:
                            {
                                //double minum = double.PositiveInfinity;
                                //double maxium = double.NegativeInfinity;
                                //getExtremes(indAtt, ref minum, ref maxium);
                                //if (minum == double.PositiveInfinity)
                                //    atrNum.Add(new TematicoAtributoNumerico(att, 0, 0));
                                //else
                                //    atrNum.Add(new TematicoAtributoNumerico(att, minum, maxium));
                                atrNum.Add(new TematicoAtributoNumerico(att, GetDoubles(indAtt)));
                                break;
                            }
                        case TiposEdicion.Texto:
                            {
                                atrTexto.Add(new TematicoAtributoTexto(att, GetStrings(indAtt)));
                                break;
                            }
                        case TiposEdicion.Combo:
                            {
                                //En este caso no se necesita hacer nada.
                                //atrTexto.Add(new TematicoAtributoTexto(att, GetStrings(indAtt)));
                                break;
                            }
                    }
                    indAtt++;
                }

                datos = new TematicoDatosInput(entidad, atrNum, atrTexto);
                return datos;
            }
            else
            {
                //System.Windows.Forms.MessageBox.Show(GMLC01HerramientasCAD.Comun.Presentacion.Properties.Resources.ElFicheroXSDNoHaSidoCargado, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
                System.Windows.Forms.MessageBox.Show(COMUN.Properties.Resources.ElDibujoNoContieneEntidadesDelTipoSeleccionado, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
                return null;
            }
        }

        ///// <summary>
        ///// Recupera los valores máximo y mínimo del atributo para las entidades.
        ///// </summary>
        ///// <param name="indAtt"></param>
        ///// <param name="Min"></param>
        ///// <param name="Max"></param>
        //private void getExtremes(int indAtt, ref double Min, ref double Max)
        //{
        //    for (int i = 0; i < ents.GetLength(0); i++)
        //    {
        //        if (!String.IsNullOrEmpty(ents[i].Atributos[indAtt].Valor))
        //        {
        //            double valor = PunComa(ents[i].Atributos[indAtt].Valor);
        //            if (valor < Min) Min = valor;
        //            if (valor > Max) Max = valor;
        //        }
        //    }
        //}

        /// <summary>
        /// Recupera el listado de valores de un atributo de tipo texto.
        /// </summary>
        /// <param name="indAtt"></param>
        /// <returns></returns>
        private Collection<double> GetDoubles(int indAtt)
        {
            Collection<double> doubles = new Collection<double>();
            for (int i = 0; i < ents.GetLength(0); i++)
            {
                if (!String.IsNullOrEmpty(ents[i].Atributos[indAtt].Valor))
                {
                    double valor = PunComa(ents[i].Atributos[indAtt].Valor);
                    if (!doubles.Contains(valor))
                    {
                        doubles.Add(valor);
                    }
                }
            }
            return doubles;
        }

        /// <summary>
        /// Recupera el listado de valores de un atributo de tipo texto.
        /// </summary>
        /// <param name="indAtt"></param>
        /// <returns></returns>
        private Collection<string> GetStrings(int indAtt)
        {
            Collection<String> strings = new Collection<string>();
            for (int i = 0; i < ents.GetLength(0); i++)
            {
                if(!strings.Contains(ents[i].Atributos[indAtt].Valor))
                {
                    strings.Add(ents[i].Atributos[indAtt].Valor);
                }
            }
            return strings;
        }

        #region "Conversión de cadenas de texto a valores numéricos"
        private static bool IsNumeric(string expression)
        {
            bool isNum;
            double retNum;

            isNum = Double.TryParse(Convert.ToString(expression, System.Globalization.CultureInfo.CurrentCulture), System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
            return isNum;
        }

        private static double PunComa(string texNumero)
        {
            double functionReturnValue = 0;
            //Esta función, partiendo de un número en string (con un separador de decimales)
            // la evalúa según la configuración y devuelve el valor como un número de tipo Double.
            if (string.IsNullOrEmpty(texNumero) | !IsNumeric(texNumero))
            {
                functionReturnValue = 0;
                return functionReturnValue;
            }

            string tempStr = null;
            if (texNumero.IndexOf(".", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                if (Double.Parse("1.1", System.Globalization.CultureInfo.CurrentCulture) > Double.Parse("1,1", System.Globalization.CultureInfo.CurrentCulture))
                {
                    tempStr = texNumero.Replace(".", ",");
                    functionReturnValue = Double.Parse(tempStr, System.Globalization.NumberFormatInfo.CurrentInfo);
                }
                else
                {
                    functionReturnValue = Double.Parse(texNumero, System.Globalization.NumberFormatInfo.CurrentInfo);
                }
            }
            else
            {
                if (Double.Parse("1.1", System.Globalization.CultureInfo.CurrentCulture) > Double.Parse("1,1", System.Globalization.CultureInfo.CurrentCulture))
                {
                    functionReturnValue = Double.Parse(texNumero, System.Globalization.NumberFormatInfo.CurrentInfo);
                }
                else
                {
                    tempStr = texNumero.Replace(",", ".");
                    functionReturnValue = Double.Parse(tempStr, System.Globalization.NumberFormatInfo.CurrentInfo);
                }
            }
            return functionReturnValue;
        }
        #endregion
        #endregion

        public void ActivaTematicos()
        {
            if (ModComun.XSD == null)
                throw new GMLCTematicosException(COMUN.Properties.Resources.ElFicheroXSDNoHaSidoCargado);

            if (frmElementos == null)
                frmElementos = new SeleccionElementoForm(ModComun.XSD, false);

            frmElementos.EntidadSeleccionada = null;
            frmElementos.ShowDialog();

            pEntidadSeleccionada = frmElementos.EntidadSeleccionada;
            if (pEntidadSeleccionada != null)
            {
                TematicoDatosInput datos = ObtenerDatosTematicosEntidad(pEntidadSeleccionada);
                if (datos != null)
                    ModComun.ActivarTematicos(this, datos);
                //NativeMethods.SetParent(frm.Handle, NativeMethods.FindWindow("MstnTop", null));
                //NativeMethods.ZOrder(frm.Handle, NativeMethods.HWND_TOPMOST);
            }
        }

        #region Miembros de IDisposable

        public void Dispose()
        {
            GC.SuppressFinalize(this);
            if (this.frmElementos != null)
                this.frmElementos.Dispose();
        }

        #endregion
    }

    [Serializable]
    public class GMLCTematicosException : System.Exception
    {
        public GMLCTematicosException()
        {
        }

        public GMLCTematicosException(string message)
            : base(message)
        {
        }

        public GMLCTematicosException(string message, System.Exception innerException)
            : base(message, innerException)
        {
        }

        protected GMLCTematicosException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }
    }
}
