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

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

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System.Globalization;

namespace GMLC01HerramientasCad.Acad
{
    class ConsultarElementos : IConsultar
    {

        #region IConsultar Members

        private static ObjectId[] originalOIDs;
        private Extents3d originalExtent;

        public void Guardar(Collection<Elemento> elementos)
        {
            foreach (Elemento elemento in elementos)
            {
                GuardarAtributos(elemento);
            }
        }

        public void Centrar(Elemento elemento)
        {
            if (elemento != null)
            {
                ObjectId[] finalOids = new ObjectId[1];
                finalOids[0] = new ObjectId(new System.IntPtr(elemento.IdInterno));
                ZoomToObjects(finalOids);
            }
            else
            {
                ZoomToExtent(originalExtent);
            }

        }

        public static void ZoomToObjects(ObjectId[] oIds)
        {
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            using (Transaction trx = doc.Database.TransactionManager.StartTransaction())
            {
                int cvId = Convert.ToInt32((Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("CVPORT")), NumberFormatInfo.CurrentInfo);
                using (Autodesk.AutoCAD.GraphicsSystem.Manager gm = doc.GraphicsManager)
                using (Autodesk.AutoCAD.GraphicsSystem.View vw = gm.GetGsView(cvId, true))
                {
                    Extents3d ext = new Extents3d(new Point3d(0, 0, 0), new Point3d(0, 0, 0)); // = ent.GeometricExtents;
                    for (int i = 0; i <= oIds.GetUpperBound(0); i++)
                    {
                        Entity ent = (Entity)trx.GetObject(oIds[i], OpenMode.ForRead);
                        if (ent.GetRXClass().DxfName == "POINT")
                        {
                            DBPoint myent = (DBPoint)ent;
                            Point3d puto1 = new Point3d(myent.Position.X - 0.5, myent.Position.Y - 0.5, myent.Position.Z);
                            Point3d puto2 = new Point3d(myent.Position.X + 0.5, myent.Position.Y + 0.5, myent.Position.Z);
                            if (i == 0)
                                ext = new Extents3d(puto1, puto2);
                            else
                                ext.AddExtents(new Extents3d(puto1, puto2));
                        }
                        else if (ent.GetRXClass().DxfName == "INSERT")
                        {
                            BlockReference myent = (BlockReference)ent;
                            Point3d puto1 = new Point3d(myent.Position.X - 0.5, myent.Position.Y - 0.5, myent.Position.Z);
                            Point3d puto2 = new Point3d(myent.Position.X + 0.5, myent.Position.Y + 0.5, myent.Position.Z);
                            if (i == 0)
                                ext = new Extents3d(puto1, puto2);
                            else
                                ext.AddExtents(new Extents3d(puto1, puto2));
                        }
                        else if (ent.GetRXClass().DxfName == "HATCH")
                        {
                            Hatch htch = (Hatch)ent;

                            double xMin = double.PositiveInfinity;
                            double yMin = double.PositiveInfinity;
                            double xMax = double.NegativeInfinity;
                            double yMax = double.NegativeInfinity;

                            for (int loo = 0; loo < htch.NumberOfLoops; loo++)
                            {
                                for (int j = 0; j < htch.GetLoopAt(loo).Curves.Count; j++)
                                {
                                    if (xMin > htch.GetLoopAt(loo).Curves[j].StartPoint.X) xMin = htch.GetLoopAt(loo).Curves[j].StartPoint.X;
                                    if (xMax < htch.GetLoopAt(loo).Curves[j].StartPoint.X) xMax = htch.GetLoopAt(loo).Curves[j].StartPoint.X;
                                    if (yMin > htch.GetLoopAt(loo).Curves[j].StartPoint.Y) yMin = htch.GetLoopAt(loo).Curves[j].StartPoint.Y;
                                    if (yMax < htch.GetLoopAt(loo).Curves[j].StartPoint.Y) yMax = htch.GetLoopAt(loo).Curves[j].StartPoint.Y;
                                }
                            }
                            if (i == 0)
                                ext = new Extents3d(new Point3d(xMin, yMin, 0), new Point3d(xMax, yMax, 0));
                            else
                                ext.AddExtents(new Extents3d(new Point3d(xMin, yMin, 0), new Point3d(xMax, yMax, 0)));
                        }
                        else
                        {
                            Polyline3d poli = (Polyline3d)ent;
                            double xMin = double.PositiveInfinity;
                            double yMin = double.PositiveInfinity;
                            double xMax = double.NegativeInfinity;
                            double yMax = double.NegativeInfinity;

                            // Use foreach to get each contained vertex
                            foreach (ObjectId vId in poli)
                            {
                                PolylineVertex3d v3d = (PolylineVertex3d)vId.GetObject(OpenMode.ForRead);
                                if (xMin > v3d.Position.X) xMin = v3d.Position.X;
                                if (xMax < v3d.Position.X) xMax = v3d.Position.X;
                                if (yMin > v3d.Position.Y) yMin = v3d.Position.Y;
                                if (yMax < v3d.Position.Y) yMax = v3d.Position.Y;
                            }

                            if (i == 0)
                                ext = new Extents3d(new Point3d(xMin, yMin, 0), new Point3d(xMax, yMax, 0));
                            else
                                ext.AddExtents(new Extents3d(new Point3d(xMin, yMin, 0), new Point3d(xMax, yMax, 0)));
                        }
                    }

                    vw.ZoomExtents(ext.MinPoint, ext.MaxPoint);
                    // Se aleja un poco el zoom del objeto.
                    if (oIds.GetUpperBound(0) > 0)
                    {
                        //vw.Zoom(0.8);
                    }
                    else
                    {
                        Entity ent = (Entity)trx.GetObject(oIds[0], OpenMode.ForRead);
                        if (ent.GetRXClass().DxfName == "POINT" || ent.GetRXClass().DxfName == "INSERT")
                        {
                            // En el caso de entodades puntuales el zoom será más alejado.
                            vw.Zoom(0.05);
                        }
                        else
                        {
                            vw.Zoom(0.8);
                        }
                    }

                    gm.SetViewportFromView(cvId, vw, true, true, false);
                }
                trx.Commit();
            }
        }

        public static void ZoomToExtent(Extents3d extent)
        {
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            using (Transaction trx = doc.Database.TransactionManager.StartTransaction())
            {
                int cvId = Convert.ToInt32((Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("CVPORT")), NumberFormatInfo.CurrentInfo);
                using (Autodesk.AutoCAD.GraphicsSystem.Manager gm = doc.GraphicsManager)
                using (Autodesk.AutoCAD.GraphicsSystem.View vw = gm.GetGsView(cvId, true))
                {
                    vw.ZoomExtents(extent.MinPoint, extent.MaxPoint);
                    gm.SetViewportFromView(cvId, vw, true, true, false);
                }
                trx.Commit();
            }

        }

        public void Resaltar(Elemento elemento)
        {
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Autodesk.AutoCAD.ApplicationServices.TransactionManager tm = doc.TransactionManager;
            using (Transaction trx = doc.Database.TransactionManager.StartTransaction())
            {
                ObjectId[] finalOids;
                if (elemento != null)
                {
                    finalOids = new ObjectId[1];
                    finalOids[0] = new ObjectId(new System.IntPtr(elemento.IdInterno));
                }
                else
                {
                    finalOids = new ObjectId[0];
                }
                doc.Editor.SetImpliedSelection(finalOids);
                tm.QueueForGraphicsFlush();
                tm.FlushGraphics();
                doc.Editor.UpdateScreen();
                trx.Commit();
            }
        }
        #endregion

        private static bool CheckGeomType(Entidad entidad, string entType)
        {
            bool retVal = false;
            switch (entType)
            {
                case "LINE":
                case "LWPOLYLINE":
                case "POLYLINE":
                    {
                        //Entidades lineales: "LINE", "POLYLINE", "LWPOLYLINE".
                        retVal = (entidad.TipoGeometria == TiposGeometria.Linea);
                        break;
                    }
                case "POINT":
                case "INSERT":
                    {
                        //Entidades puntuales: "POINT", "INSERT".
                        retVal = (entidad.TipoGeometria == TiposGeometria.Punto);
                        break;
                    }
                case "HATCH":
                    {
                        //Poligonos: "HATCH".
                        retVal = (entidad.TipoGeometria == TiposGeometria.Area);
                        break;
                    }
                default:
                    {
                        // Al desconocesrse el tipo de entidad, no se considerará.
                        break;
                    }
            }
            return retVal;
        }


        public void Init()
        {
            // Recuperación del conjunto de selección previo.
            originalOIDs = SeleccionObjetosAutoCAD.GetSelectedEntities();

            // Si hay elementos seleccionados, se procede con la consulta.
            if (originalOIDs != null)
            {
                int curOID = 0;
                int numAddeds = 0;
                Collection<Elemento> elementos = new Collection<Elemento>();
                byte addNewEnts = 0;
                for (int i = 0; i < originalOIDs.GetLength(0); i++)
                {
                    curOID += 1;
                    string entLayer;
                    string entType;
                    Transaction tr = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.TransactionManager.StartTransaction();
                    using (tr)
                    {
                        Entity ent = null;
                        ent = originalOIDs[i].GetObject(OpenMode.ForWrite) as Entity; //tr.GetObject(originalOIDs[i], OpenMode.ForWrite) as Entity;
                        if (ent == null)
                            continue;
                        entLayer = ent.Layer;
                        entType = ent.GetRXClass().DxfName;
                    }

                    string enTip = GetEntTyp(entLayer);

                    // Si no está en un nivel reconocido como entidad se desestima
                    if (!MyPlugin.modelLayers.Contains(enTip.ToUpper(CultureInfo.CurrentCulture)))
                        continue;

                    Elemento elemento = new Elemento();
                    elemento.IdInterno = long.Parse(originalOIDs[i].OldIdPtr.ToString(), System.Globalization.CultureInfo.InvariantCulture);
                    elemento.CodigoEntidad = enTip; // entLayer.Split('.')[0];

                    Entidad testEnt = XData.GetXData(originalOIDs[i], elemento.CodigoEntidad);

                    // Si la entidad no tiene atributos...
                    if (testEnt == null)
                    {

                        // Si no está en un nivel reconocido como entidad se desestima
                        if (!MyPlugin.modelLayers.Contains(entLayer.ToUpper(CultureInfo.CurrentCulture)))
                            continue;

                        if (CheckGeomType(ModComun.XSD.Entidades[elemento.CodigoEntidad], entType))
                        {
                            if (addNewEnts == 0)
                            {
                                // No se preguntó si se desea agregar estas entidades al modelo.
                                if (MessageBox.Show(COMUN.Properties.Resources.UnaOVariasDeLasEntidadesSeleccionadas, COMUN.Properties.Resources.Inventario, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1, 0) == DialogResult.Yes)
                                    addNewEnts = 1;
                                else
                                    addNewEnts = 255;
                            }
                            if (addNewEnts == 1)
                            {
                                // Se deben agregar las entidades.
                                Entidad ente = ModComun.XSD.Entidades[entLayer.Split('.')[0]];

                                // Rellenado del atributo "ID" y el subtipo (si es menester).
                                foreach (Atributo atributo in elemento.Atributos)
                                {
                                    if (atributo.Codigo == ModComun.XSD.Clave) atributo.Valor = "99" + originalOIDs[i].OldIdPtr.ToString();
                                    if (!string.IsNullOrEmpty(ente.CampoSubTipo) && entLayer.Split('.').GetUpperBound(0) == 2)
                                    {
                                        if (atributo.Codigo == ente.CampoSubTipo) atributo.Valor = entLayer.Split('.')[2];
                                    }
                                }

                                //¡¡¡PODRÍA SER NECESARIO TRANSFORMAR EL TIPO DE ENTIDAD DE POLILINEA A POLILINEA3D!!!
                                //ent.GetType().Name    "Polyline3d"
                                //ent.GetRXClass().Name"AcDb3dPolyline"

                                originalOIDs[i] = CheckEntity(elemento, entLayer, true);
                                elemento.IdInterno = originalOIDs[i].OldIdPtr.ToInt32();

                                // Guardado de los atributos asociados a la categoría del elemento dibujado
                                XData.SetXData(originalOIDs[i], elemento);

                            }
                            else if (addNewEnts == 255)
                            {
                                // Se deben omitir la entidades que no son del modelo.
                                continue;
                            }
                        }
                        else
                        {
                            // Al no ser del tipo de geometría esperado, se omite la entidad.
                            continue;
                        }
                    }
                    else
                    {
                        foreach (Atributo att in testEnt.Atributos)
                        {
                            foreach (Atributo att2 in elemento.Atributos)
                            {
                                if (att.Codigo == att2.Codigo)
                                {
                                    att2.Valor = att.Valor;
                                    break;
                                }
                            }
                        }
                    }
                    elementos.Add(elemento);
                    numAddeds += 1;
                }

                if (addNewEnts == 1)
                {
                    Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.SetImpliedSelection(originalOIDs);
                    Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
                    Autodesk.AutoCAD.ApplicationServices.TransactionManager tm = doc.TransactionManager;
                    tm.QueueForGraphicsFlush();
                    tm.FlushGraphics();
                    doc.Editor.UpdateScreen();
                }

                //Si hay elementos que mostrar, se abre la ventana
                if (elementos.Count > 0)
                {
                    //Recuperación de la extensión inicial de la ventana.
                    Point3d center = (Point3d)Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable(@"VIEWCTR");
                    double vhsize = (double)Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable(@"VIEWSIZE");
                    Autodesk.AutoCAD.Geometry.Point2d scsize = (Autodesk.AutoCAD.Geometry.Point2d)Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable(@"SCREENSIZE");

                    Point2d screenRes = new Point2d(scsize[0], scsize[1]);
                    double wd = 0.5 * vhsize * (screenRes.X / screenRes.Y);
                    double hg = 0.5 * vhsize;

                    originalExtent = new Extents3d(new Point3d(center.X - wd, center.Y - hg, 0), new Point3d(center.X + wd, center.Y + hg, 0));

                    // Apertura de la ventana de propiedades
                    ModComun.ActivarConsulta(this, elementos);

                    Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.SetImpliedSelection(originalOIDs);
                    
                    // Recuperación de la vista inicial
                    ZoomToExtent(originalExtent);
                }
                else
                    MessageBox.Show(COMUN.Properties.Resources.LasEntidadesSeleccionadasNoPertenecenAlInventario, COMUN.Properties.Resources.Inventario, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, 0);
            }
            else
            {
                MessageBox.Show(COMUN.Properties.Resources.NoSeSeleccionaronEntidadesQueConsultar, COMUN.Properties.Resources.Inventario, MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, 0);
            }
        }

        private static string GetEntTyp(string entLayer)
        {
            //if (entLayer.StartsWith("TEM_", StringComparison.OrdinalIgnoreCase))
            //{
            //    return entLayer.Substring(4).Split('-')[0];
            //}
            //else
            //{
            //    return entLayer.Split('.')[0];
            //}
            return entLayer.Split('.')[0];
        }

        private static void GuardarAtributos(Elemento elemento)
        {
            ObjectId oId = new ObjectId(new System.IntPtr(elemento.IdInterno));

            // REVISAR LA CAPA DEL OBJETO
            string newLayName = "";

            Entidad ent = ModComun.XSD.Entidades[elemento.CodigoEntidad];

            if (!String.IsNullOrEmpty(ent.CampoSubTipo))
            {
                if (!String.IsNullOrEmpty(elemento.Atributos[ent.CampoSubTipo].Valor))
                {
                    newLayName = ent.Codigo + "." + ent.CampoSubTipo + "." + elemento.Atributos[ent.CampoSubTipo].Valor; //GetName4DomainValue(elemento.Atributos[ent.CampoSubTipo].NombreDominio, elemento.Atributos[ent.CampoSubTipo].Valor);
                }
                else
                {
                    newLayName = ent.Codigo;
                }
            }
            else
            {
                newLayName = ent.Codigo;
            }

            if (ent.TipoGeometria == TiposGeometria.Punto)
            {
                oId = CheckEntity(elemento, newLayName, false);
            }
            else
            {
                Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
                Autodesk.AutoCAD.ApplicationServices.TransactionManager tm = doc.TransactionManager;
                using (Transaction tr = doc.TransactionManager.StartTransaction())
                {
                    using (DocumentLock docLoc = doc.LockDocument())
                    {
                        Entity enty = null;
                        enty = tr.GetObject(new ObjectId(new System.IntPtr(elemento.IdInterno)), OpenMode.ForWrite) as Entity;
                        enty.UpgradeOpen();
                        if (enty.Layer != newLayName)
                        {
                            LayerManagement.PrepareLayer(newLayName);
                            enty.Layer = newLayName;
                            tm.QueueForGraphicsFlush();
                            tm.FlushGraphics();
                            doc.Editor.UpdateScreen();
                        }
                        tr.Commit();
                    }
                }
            }

            // Por si no se pudo chequear el punto.
            if (long.Parse(oId.OldIdPtr.ToString(), System.Globalization.CultureInfo.InvariantCulture) == 0) return;
            elemento.IdInterno = long.Parse(oId.OldIdPtr.ToString(), System.Globalization.CultureInfo.InvariantCulture);
            XData.SetXData(oId, elemento);
        }

        private static ObjectId CheckEntity(Elemento elemento, string newLayName, bool omitLayerChack)
        {
            ObjectId oId = new ObjectId(new System.IntPtr(elemento.IdInterno));
            ObjectId oldId = new ObjectId(new System.IntPtr(elemento.IdInterno));
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Autodesk.AutoCAD.ApplicationServices.TransactionManager tm = doc.TransactionManager;
            using (Transaction tr = tm.StartTransaction())
            {
                using (DocumentLock docl = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument())
                {
                    Entidad ent = ModComun.XSD.Entidades[elemento.CodigoEntidad];
                    Entity enty = null;
                    enty = tr.GetObject(new ObjectId(new System.IntPtr(elemento.IdInterno)), OpenMode.ForWrite) as Entity;
                    enty.UpgradeOpen();
                    if (omitLayerChack || enty.Layer != newLayName)
                    {
                        // En el caso de los bloques, se eliminan y se vuelven a insertar cuando cambian de capa.
                        if (ent.TipoGeometria == TiposGeometria.Punto)
                        {
                            LayerManagement.PrepareLayer(newLayName);
                            DibujarEnts dibi = new DibujarEnts();
                            dibi.ElementoSeleccionado = elemento;
                            dibi.BlockName = newLayName;
                            double x = 0;
                            double y = 0;
                            double z = 0;
                            switch (enty.GetRXClass().DxfName)
                            {
                                case "POINT":
                                    {
                                        DBPoint punto = (DBPoint)enty;
                                        x = punto.Position.X;
                                        y = punto.Position.Y;
                                        z = punto.Position.Z;
                                        break;
                                    }
                                case "INSERT":
                                    {
                                        BlockReference bloque = (BlockReference)enty;
                                        x = bloque.Position.X;
                                        y = bloque.Position.Y;
                                        z = bloque.Position.Z;
                                        break;
                                    }
                            }
                            enty.Erase();
                            oId = dibi.InsertBlockOrPoint(x, y, z);
                        }
                        else if (ent.TipoGeometria == TiposGeometria.Linea)
                        {
                            switch (enty.GetRXClass().DxfName)
                            {
                                case "LINE":
                                    {
                                        LayerManagement.PrepareLayer(newLayName);
                                        Line linea = (Line)enty;
                                        Point3dCollection p3dCol = new Point3dCollection();
                                        p3dCol.Add(linea.StartPoint);
                                        p3dCol.Add(linea.EndPoint);
                                        oId = DibujarEnts.Insert3DPolyline(p3dCol, false);
                                        enty.Erase();
                                        break;
                                    }
                                case "LWPOLYLINE":
                                    {
                                        LayerManagement.PrepareLayer(newLayName);
                                        Polyline poli = (Polyline)enty;
                                        Point3dCollection p3dCol = new Point3dCollection();
                                        for (int i = 0; i < poli.NumberOfVertices; i++)
                                        {
                                            p3dCol.Add(new Point3d(poli.GetPoint2dAt(i).X, poli.GetPoint2dAt(i).Y, 0));
                                        }
                                        oId = DibujarEnts.Insert3DPolyline(p3dCol, false);
                                        enty.Erase();
                                        break;
                                    }
                                case "POLYLINE":
                                    {
                                        //En este caso no se hace nada.
                                        break;
                                    }
                            }
                        }
                        else
                        {
                            enty.Layer = newLayName;
                        }
                        //tm.QueueForGraphicsFlush();
                        //tm.FlushGraphics();
                        //doc.Editor.UpdateScreen();
                        tr.Commit();
                    }
                }
            }
            if (!oldId.OldIdPtr.ToString().Equals(oId.OldIdPtr.ToString(), StringComparison.OrdinalIgnoreCase))
            {
                for (int i = 0; i <= originalOIDs.GetUpperBound(0); i++)
                {
                    if (originalOIDs[i].OldIdPtr.ToString().Equals(oldId.OldIdPtr.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        originalOIDs[i] = oId;
                    }
                }
                elemento.IdInterno = long.Parse(oId.OldIdPtr.ToString(), System.Globalization.CultureInfo.InvariantCulture);
            }
            return oId;
        }

    }
}
