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

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

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

namespace GMLC01HerramientasCad.Acad
{
    class DibujarEnts : IDisposable
    {
        //SeleccionElementoForm frmElementos;
        public Elemento ElementoSeleccionado;
        public string BlockName = "";
        /*
        public void Dibujar()
        {
            if (ModComun.XSD == null)
                throw new GMLCDibujarEntsException(COMUN.Properties.Resources.ElFicheroXSDNoHaSidoCargado);

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


            frmElementos.ShowDialog();

            if (frmElementos.EntidadSeleccionada != null)
            {

                InitDibujado(frmElementos.EntidadSeleccionada, frmElementos.NivelCad, frmElementos.SubTipoSeleccionado);

            }

        }
        */
        internal void InitDibujado(Entidad ente, string nivelCAD, SubTipo subTipo)
        {
            //Document doc = Application.DocumentManager.MdiActiveDocument;

            if (ente != null)
            {
                string template = nivelCAD;

                LayerManagement.PrepareLayer(template);

                Elemento elemento = new Elemento();
                elemento.IdInterno = 0;
                elemento.CodigoEntidad = ente.Codigo;

                if (subTipo != null)
                {
                    elemento.Atributos[subTipo.Entidad.CampoSubTipo].Valor = subTipo.Codigo;
                }

                ElementoSeleccionado = elemento;
                BlockName = template;

                switch (ente.TipoGeometria)
                {
                    case TiposGeometria.Punto:
                        Point();
                        break;
                    case TiposGeometria.Linea:
                        Polyline();
                        break;
                    case TiposGeometria.Area:
                        Area();
                        break;
                }
            }
            else
            {
                LayerManagement.PrepareLayer("0");
            }
        }

        #region "Dibujado de Puntos"
        public void Point()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            bool bGotPoint = true;

            while (bGotPoint) {

                try{

                    PromptPointResult ptInsert = doc.Editor.GetPoint(new PromptPointOptions("Seleccione la ubicación(es) o escape para cancelar:"));
                    if (ptInsert.Status == PromptStatus.OK) {
                        InsertBlockOrPoint(ptInsert.Value.X, ptInsert.Value.Y, ptInsert.Value.Z);
                    }
                    else {
                        bGotPoint = false;
                    }

                } catch (Autodesk.AutoCAD.Runtime.Exception) {
                    bGotPoint = false;
                }   
            }

        }

        public ObjectId InsertBlockOrPoint(double x, double y, double z)
        {
            Point3d basept = new Point3d(x, y, z);

            ObjectId pointid = default(ObjectId);
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Autodesk.AutoCAD.ApplicationServices.TransactionManager tm = doc.TransactionManager;
            Database db = doc.Database;
            using (Transaction ta = tm.StartTransaction())
            {
                //Al ser las paletas algo NO MODAL, para evitar un "eLockViolation" se debe bloquear el documento.
                using (DocumentLock docLock = doc.LockDocument())
                {

                    BlockTable bt = (BlockTable)tm.GetObject(db.BlockTableId, OpenMode.ForRead, false);
                    BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].GetObject(OpenMode.ForWrite);

                    if (bt.Has(BlockName))
                    {
                        try
                        {
                            ObjectId oId = bt[BlockName];
                            //BlockReference pBlock = new BlockReference(basept, oId);
                            //Create the block reference 
                            BlockReference br = new BlockReference(basept, oId);
                            //br.Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex(Autodesk.AutoCAD.Colors.ColorMethod.ByLayer, 256);
                            br.ColorIndex = 256;
                            br.Linetype = "ByLayer";
                            br.LineWeight = LineWeight.ByLayer;

                            //Add block reference to model space table
                            pointid = btr.AppendEntity(br);
                            ta.AddNewlyCreatedDBObject(br, true);
                        }
                        finally
                        {
                        }
                    }
                    else
                    {
                        //Si no existe el bloque "toma", se dibujan puntos.
                        try
                        {
                            DBPoint pPoint = new DBPoint(basept);
                            //pPoint.Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex(Autodesk.AutoCAD.Colors.ColorMethod.ByLayer, 256);
                            pPoint.ColorIndex = 256;
                            pPoint.Linetype = "ByLayer";
                            pPoint.LineWeight = LineWeight.ByLayer;
                            pointid = btr.AppendEntity(pPoint);
                            tm.AddNewlyCreatedDBObject(pPoint, true);
                        }
                        finally
                        {
                        }
                    }

                    tm.QueueForGraphicsFlush();
                    tm.FlushGraphics();
                    doc.Editor.UpdateScreen();
                    ta.Commit();

                }
            }

            // Este "if" se debe a que esta función puede ser llamada desde la importación de GMLs (GML.cs => GrabarPuntual).
            if (ElementoSeleccionado != null)
            {
                // Rellenado del atributo "ID"
                foreach (Atributo atributo in ElementoSeleccionado.Atributos)
                {
                    if (atributo.Codigo == ModComun.XSD.Clave) atributo.Valor = "99" + pointid.OldId.ToString(System.Globalization.CultureInfo.InvariantCulture);
                }
                XData.SetXData(pointid, ElementoSeleccionado);
            }
            return pointid;
        }
        #endregion

        #region "Dibujado de Polilíneas"
        public void Polyline()
        {
            ObjectId lineId = Get3DPolyByJig(false);

            if (lineId.OldId != 0)
            {
                // Rellenado del atributo "ID"
                foreach (Atributo atributo in ElementoSeleccionado.Atributos)
                {
                    if (atributo.Codigo == ModComun.XSD.Clave) atributo.Valor = "99" + lineId.OldId.ToString(System.Globalization.CultureInfo.InvariantCulture);
                }
                // Guardado de los atributos asociados a la categoría del elemento dibujado
                XData.SetXData(lineId, ElementoSeleccionado);
                Polyline();
            }
        }
        #endregion

        #region "Dibujado de Áreas"

        public void Area()
        {
            Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nContorno del polígono...");
            double height = 0;
            ObjectId boundary = Get2DPolyByJig(true, null, ref height);
            //Se comprueba que se dibujó una línea.
            if (boundary.OldId != 0)
            {
                Collection<Coordenada> coordenadas = new Collection<Coordenada>();

                Database db = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Database;
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    Polyline poli = (Polyline)boundary.GetObject(OpenMode.ForRead);
                    for (int i = 0; i < poli.NumberOfVertices; i++)
                    {
                        coordenadas.Add(new Coordenada(poli.GetPoint2dAt(i).X, poli.GetPoint2dAt(i).Y, height));
                    }
                    tr.Abort();
                }

                //Se crea la colección de ObjectId-s que almacenará todas las líneas.
                ObjectIdCollection elements = new ObjectIdCollection();
                double height2 = 0;
                bool continua = true;
                while (continua)
                {
                    Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nAgregado de islas...");
                    ObjectId idIsla = Get2DPolyByJig(true, coordenadas, ref height2);
                    if (idIsla.OldId != 0)
                    {
                        elements.Add(idIsla);
                    }
                    else
                    {
                        // Se canceló la isla, por lo que se termina el polígono.
                        continua = false;
                    }
                }
                //Al digujar el Hatch, sólo se tiene en cuenta la altura media del contorno.
                ObjectId idPol = MakeHatch(boundary, elements, height);
                // Se controla, por si no se hubiese podido insertar el MPolygon.
                if (idPol.OldId != 0)
                {
                    // Rellenado del atributo "ID"
                    foreach (Atributo atributo in ElementoSeleccionado.Atributos)
                    {
                        if (atributo.Codigo == ModComun.XSD.Clave) atributo.Valor = "99" + idPol.OldId.ToString(System.Globalization.CultureInfo.InvariantCulture);
                    }
                    // Guardado de los atributos asociados a la categoría del elemento dibujado
                    XData.SetXData(idPol, ElementoSeleccionado);

                }
                Area(); //Para permitir que se dubuje otra área después de terminar con la actual.
            }
        }

        public static ObjectId MakeHatch(ObjectId boundary, ObjectIdCollection innerLoops, double elevation)
        {
            ObjectId retVal = new ObjectId(new System.IntPtr(0));
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Autodesk.AutoCAD.ApplicationServices.TransactionManager tm = doc.TransactionManager;
            Database db = doc.Database;
            using (Transaction tr = tm.StartTransaction())
            {
                //Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
                HatchPatternType hatchType = new Autodesk.AutoCAD.DatabaseServices.HatchPatternType();
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                Hatch hatchObj = new Hatch();
                btr.AppendEntity(hatchObj);
                tr.AddNewlyCreatedDBObject(hatchObj, true);

                hatchObj.UpgradeOpen();
                hatchObj.HatchStyle = Autodesk.AutoCAD.DatabaseServices.HatchStyle.Normal;
                hatchObj.Associative = false;
                // For us, our new polyline will have all properties "By Layer".
                hatchObj.ColorIndex = 256;
                hatchObj.Linetype = "ByLayer";
                hatchObj.LineWeight = LineWeight.ByLayer;
                hatchObj.SetHatchPattern(hatchType, "_SOLID");

                ObjectIdCollection prim = new ObjectIdCollection();
                prim.Add(boundary);
                hatchObj.InsertLoopAt(0, HatchLoopTypes.Outermost, prim);
                foreach (ObjectId obId in innerLoops)
                {
                    ObjectIdCollection segu = new ObjectIdCollection();
                    segu.Add(obId);
                    hatchObj.InsertLoopAt(hatchObj.NumberOfLoops, HatchLoopTypes.Derived, segu);
                }

                hatchObj.EvaluateHatch(true);

                hatchObj.Elevation = elevation;

                retVal = hatchObj.ObjectId;

                Entity entB = (Entity)boundary.GetObject(OpenMode.ForWrite);
                entB.Erase();

                // Eliminación de las líneas.
                foreach (ObjectId objId in innerLoops)
                {
                    Entity ent = (Entity)objId.GetObject(OpenMode.ForWrite);
                    ent.Erase();
                }

                tm.QueueForGraphicsFlush();
                tm.FlushGraphics();
                doc.Editor.UpdateScreen();
                tr.Commit();
            }
            return retVal;
        }

        //public ObjectId MakeHatch_MULTIPOLYGON(ObjectIdCollection oIds)
        //{
        //    ObjectId retVal = new ObjectId(0);
        //    Database db = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Database;
        //    using (Transaction tr = db.TransactionManager.StartTransaction())
        //    {
        //        Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
        //        HatchPatternType hatchType = new Autodesk.AutoCAD.DatabaseServices.HatchPatternType();
        //        BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
        //        Hatch hatchObj = new Hatch();
        //        btr.AppendEntity(hatchObj);
        //        tr.AddNewlyCreatedDBObject(hatchObj, true);

        //        hatchObj.UpgradeOpen();
        //        hatchObj.HatchStyle = Autodesk.AutoCAD.DatabaseServices.HatchStyle.Normal;
        //        hatchObj.Associative = false;
        //        //hatchObj.Layer = "0";
        //        //hatchObj.ColorIndex = 12;
        //        //hatchObj.PatternAngle = 0.0; //Math.PI / 2
        //        //hatchObj.PatternScale = 4.0;
        //        //hatchObj.PatternSpace = 4.0;
        //        // keep this order to set pattern at the end!
        //        hatchObj.SetHatchPattern(hatchType, "_SOLID");

        //        foreach (ObjectId obId in oIds)
        //        {
        //            ObjectIdCollection prim = new ObjectIdCollection();
        //            prim.Add(obId);
        //            hatchObj.InsertLoopAt(0, HatchLoopTypes.Default, prim);
        //        }

        //        hatchObj.EvaluateHatch(true);

        //        retVal = hatchObj.ObjectId;

        //        ed.Rxegen();
        //        tr.Commit();
        //    }
        //    return retVal;
        //}
        #endregion

        private static ObjectId Get2DPolyByJig(bool closed, Collection<Coordenada> coordenadas, ref double height)
        {

            ObjectId retVal = new ObjectId(new System.IntPtr(0));

            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;

            // Get the current UCS, to pass to the Jig
            Matrix3d ucs = ed.CurrentUserCoordinateSystem;

            // Create our Jig object
            PlineJig jig = new PlineJig(ucs, coordenadas);

            // Loop to set the vertices directly on the polyline
            bool bPoint = false;
            bool bKeyword = false;
            bool bComplete = false;
            do
            {
                PromptResult res = ed.Drag(jig);
                bPoint =
                  (res.Status == PromptStatus.OK);
                // A new point was added
                if (bPoint)
                    jig.AddDummyVertex();
                bKeyword =
                  (res.Status == PromptStatus.Keyword);
                if (bKeyword)
                {
                    if (jig.IsUndoing())
                    {
                        jig.RemoveDummyVertex();
                    }
                    else
                    {
                        jig.AdjustSegmentType(jig.IsArcSegment());
                    }
                }
                // Null input terminates the command
                bComplete = (res.Status == PromptStatus.None);
                if (bComplete && ((Polyline)jig.GetEntity()).NumberOfVertices > 1)
                    // Let's clean-up the polyline before adding it
                    jig.RemoveDummyVertex();
            } while ((bPoint || bKeyword) && !bComplete);

            // If the jig completed successfully,
            // add the polyline
            if (bComplete)
            {
                height = jig.GetMediumHeight();
                Polyline pline = jig.GetEntity() as Polyline;
                if (pline.NumberOfVertices > 1)
                {
                    // If requested, the pline will be closed
                    if (closed) pline.Closed = true;
                    // Append entity
                    Database db = doc.Database;
                    Transaction tr =
                      db.TransactionManager.StartTransaction();
                    using (tr)
                    {
                        using (DocumentLock docLoc = doc.LockDocument())
                        {
                            BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead, false);
                            BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite, false); btr.AppendEntity(pline);
                            tr.AddNewlyCreatedDBObject(pline, true);
                            retVal = pline.ObjectId;
                            tr.Commit();
                        }
                    }
                }
            }
            return retVal;
        }

        private static ObjectId Get3DPolyByJig(bool closed)
        {

            ObjectId retVal = new ObjectId(new System.IntPtr(0));

            PLine3DJigger jig = new PLine3DJigger();
            int vertexCount = jig.DoPolyLineDrawJigmojo();

            if (vertexCount > 0)
            {
                retVal = Insert3DPolyline(jig.Coordinates, closed);
            }

            return retVal;
        }

        public static ObjectId Insert3DPolyline(Point3dCollection vertices, bool closed)
        {
            ObjectId retVal = new ObjectId(new System.IntPtr(0));
            try
            {

                Document doc = Application.DocumentManager.MdiActiveDocument;

                Polyline3d pline3D = new Polyline3d(Poly3dType.SimplePoly, vertices, closed);

                using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
                {
                    using (DocumentLock docLoc = doc.LockDocument())
                    {
                        BlockTableRecord currentSpace = tr.GetObject
                            (doc.Database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                        currentSpace.AppendEntity(pline3D);
                        tr.AddNewlyCreatedDBObject(pline3D, true);
                        retVal = pline3D.ObjectId;
                        tr.Commit();
                    }
                }
            }
            finally
            {
            }
            return retVal;
        }

        #region Miembros de IDisposable

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

        #endregion
    }
    [Serializable]
    public class GMLCDibujarEntsException : System.Exception
    {
       public GMLCDibujarEntsException()
       {
       }

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

       public GMLCDibujarEntsException(string message,Exception innerException): base(message, innerException)
       {
       }

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