using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisiMath;
using VisiMath.Geometry;
using VisimBox.Renderer;
using VisimBox.Renderer.RenderObjects;

namespace VisimBox.Scene
{
    /// <summary>
    /// Node containing geometric object, by default it's renderable and creates renderer mesh from the object for faster and correct rendering.
    /// Keeps geometry object for correct picking / physics
    /// </summary>
    public class GeometryNode : NodeMesh
    {
        /// <summary>
        /// Geometric object
        /// </summary>
        protected IGeometry GeoObject;
        /// <summary>
        /// Required quality for parametrical objects
        /// </summary>
        protected float RequiredQuality;
        /// <summary>
        /// VertexType of the geometry
        /// </summary>
        protected Type VertexType;

        public override RenderFlags DefaultRenderFlags
        {
            get
            {
                return Scene.RenderFlags.Normal;
            }
        }


        /// <summary>
        /// Do NOT use unless you're really sure
        /// </summary>
        public GeometryNode()
            : base()
        {
        }
        
        /// <summary>
        /// XML reader constructor
        /// </summary>
        /// <param name="reader">XML reader</param>
        public GeometryNode(System.Xml.XmlReader reader) : base(reader)
        {
        }

        /// <summary>
        /// Constructs node with specified parent (and automatically saves itselfs to parents children) and from geoobject
        /// </summary>
        /// <param name="parent">Parent node</param>
        /// <param name="geoobject">Geometry object</param>
        /// <param name="vertexType">Required vertex type</param>
        public GeometryNode(Node parent, IGeometry geoobject, Type vertexType, IRenderMaterial mat, float quality = 0.5f)
            : base(parent, null, mat)
        {
            GeoObject = geoobject;
            RequiredQuality = quality;
            if (vertexType != null)
                VertexType = vertexType;
            else
                VertexType = geoobject.PreferredVertexType;
            DontSave = false;
        }

        /// <summary>
        /// Constructs parentless node from geoobject
        /// </summary>
        /// <param name="parent">Parent node</param>
        /// <param name="geoobject">Geometry object</param>
        /// <param name="vertexType">Required vertex type</param>
        public GeometryNode(IGeometry geoobject, Type vertexType, IRenderMaterial mat, float quality = 0.5f)
            : this(null, geoobject, vertexType, mat, quality)
        {
        }

        /// <summary>
        /// Constructs parentless node from geoobject
        /// </summary>
        /// <param name="parent">Parent node</param>
        /// <param name="geoobject">Geometry object</param>
        public GeometryNode(IGeometry geoobject, IRenderMaterial mat, float quality = 0.5f)
            : this(null, geoobject, null, mat, quality)
        {
        }


        /// <summary>
        /// Constructs node from geoobject with already finished rendering mesh
        /// </summary>
        /// <param name="parent">Parent node</param>
        /// <param name="geoobject">Geometry object</param>
        public GeometryNode(IGeometry geoobject, Mesh mesh, IRenderMaterial mat)
            : this(null, geoobject, null, mat, 0)
        {
            _RenderMesh = mesh;
        }


        /// <summary>
        /// Updates render buffer and if mesh object is not yet created from the geometry
        /// </summary>
        /// <param name="args"></param>
        public override void UpdateRenderBuffer(RendererEventArgs args)
        {
            //note: optimize, create method for mesh creation and let user to call it, there's no need to call it every frame
            base.UpdateRenderBuffer(args);
            if ((GeoObject != null) && (this._RenderMesh == null))
            {
                CreateMesh();
            }
        }

        /// <summary>
        /// Creates new mesh from geometry object
        /// </summary>
        /// <param name="args">Renderer event argumenst (for correct renderer)</param>
        /// <remarks>Does NOT add mesh to mesh manager as it expects that this mesh will be unique and there's no need to store it in the manager.
        /// Might be changed later for more advancer mesh manager with instance counts.</remarks>
        public virtual void CreateMesh()
        {
            if (_RenderMesh != null)
            {
                _RenderMesh.RemoveReference(this);
                _RenderMesh.Dispose();
                //todo, use manager!!!!
            }

            _RenderMesh = VisimEngine.Instance.Renderer.MeshMan.Create(GeoObject, RequiredQuality, VertexType);
            _RenderMesh.AddReference(this);
        }

        protected override bool PickNodeInternal(ref NodeIntersectionInfo intersinfo)
        {
            if (BoundingObject != null)
            {
                IIntersectionInfo info = new IntersectionInfo();
                info.Distance = intersinfo.Distance;
                if (!BoundingObject.GetIntersectionInfo(intersinfo.PickingRay, ref info))
                    return false;
            }

            if (GeoObject != null)
            {
                IIntersectionInfo info = (IIntersectionInfo)intersinfo;
                if (!GeoObject.GetIntersectionInfo(intersinfo.PickingRay, ref info))
                    return false;
            }
            intersinfo.NodeObject = this;
            return true;
        }

        public override bool ReadInternalData(System.Xml.XmlReader reader)
        {
            if (base.ReadInternalData(reader))
                return true;
            switch (reader.Name)
            {
                case "Geometry":
                    GeoObject = GeoFactory.ReadXml(reader);
                    if (GeoObject == null)
                    {
                        Console.DebugLine("GeometryNode loading: loading geometry failed!");
                    }
                    break;
                case "Settings":
                    RequiredQuality = XMLHelper.FloatFromString(reader["Quality"]);
                    //reader["geotype"]
                    string vertype = reader["VertexType"];
                    VertexType = VertexFactory.GetTypeFromName(vertype);
                    break;
                default:
                    return false;
            }
            return true;
        }

        public override void WriteInternalData(System.Xml.XmlWriter writer)
        {
            string geotypename;
            if (GeoObject == null)
                geotypename = "null";
            else geotypename = GeoObject.GetType().Name;            
            base.WriteInternalData(writer);


            writer.WriteStartElement("Settings");
            if (RequiredQuality != 0)
            {
                XMLHelper.WriteFloatAttribute(writer, "Quality", RequiredQuality);
            }
            writer.WriteAttributeString("geotype", geotypename);
            writer.WriteAttributeString("VertexType", VertexType.ToString());
            writer.WriteEndElement();

            writer.WriteStartElement("Geometry");
            if (GeoObject != null)
            {
                GeoObject.WriteXml(writer);
            }
            writer.WriteEndElement();        
        }

        protected override void CopyData(Node targetNode)
        {
            base.CopyData(targetNode);

            GeometryNode mynode = targetNode as GeometryNode;
            if (mynode == null)
            {
                VisimBox.Console.DebugLine(FullName + " asked to do copy to different node type!");
                return;
            }

            if (GeoObject != null)
            {
                mynode.GeoObject = GeoObject.GetCopy();
            }

            mynode.RequiredQuality = this.RequiredQuality;
            mynode.VertexType = this.VertexType;
        }

        /*
        public override string XMLNodeName
        {
            get
            {
                return "GeometryNode";
            }
        }*/
    } //geo node


}
