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

namespace VisimBox.Scene
{
    /// <summary>
    /// Node containing camera
    /// </summary>
    public class CameraNode : RenderableNode, IResourceConsumer
    {
        Mesh DebugMesh;
        IRenderMaterial DebugMaterial;

        /// <summary>
        /// Camera object
        /// </summary>
        [Description("Camera object in the node (note, also used for normal transformation)")]
        [Category("Camera data")]
        [TypeConverter(typeof(ExpandableObjectConverter))]
        public ICamera Camera
        {
            get
            {
                return Trans as ICamera;
            }
        }

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



        public CameraNode(System.Xml.XmlReader reader) : base(reader)
        {
            SetUp();
        }

        public CameraNode()
            : base()
        {
            SetUp();
        }

        public CameraNode(ICamera camera)
        {
            Trans = camera;
            SetUp();
        }

        protected void SetUp()
        {
            Visible = true;
            BoundingObject = new Sphere(new Vector3(0), 1);
            if (DebugMaterial == null)
            {
                var matman = VisimEngine.Instance.Renderer.MaterialMan;
                if (matman.Contains("CameraDebugMaterial"))
                {
                    DebugMaterial = matman["CameraDebugMaterial"];
                }
                else
                {
                    //DebugMaterial = matman.Add("CameraDebugMaterial", new ColorByte(255, 255, 255, 128));
                    StandardColorValues vals = StandardColorValues.Default();
                    //vals.Diffuse.A = 50;
                    //vals.Alpha = true;
                    //vals.Ambient.A = 50;
                    DebugMaterial = matman.Add("CameraDebugMaterial", ref vals);
                }
                DebugMaterial.AddReference(this);
            }
            
            if (DebugMesh == null)
            {
                var meshman = VisimEngine.Instance.Renderer.MeshMan;
                if (meshman.Contains("CameraDebugObject"))
                {
                    DebugMesh = meshman["CameraDebugObject"];
                }
                else
                {
                    IndexedMeshBuilder<NormalVertex> meshbuilder = new IndexedMeshBuilder<NormalVertex>(40, 1, 28);
                    IndexedMesh box = GeoFactory.CreateBox<NormalVertex>(new Vector3(0), 1, 1, 0.5f);
                    meshbuilder.Add(box);

                    Pyramid pyr = new Pyramid(typeof(NormalVertex), new Vector3(1, 0, 0), new Vector3(0.2f, 0, 0), 0.8f, 0.8f);
                    meshbuilder.Add(pyr);                    
                    //DebugMesh = meshman.Add(meshbuilder.GetMesh().ToLines(), "CameraDebugObject");
                    DebugMesh = meshman.Add(meshbuilder.GetMesh(), "CameraDebugObject");
                }
                DebugMesh.AddReference(this);
            }
        }


        public override void CreateRenderBuffer()
        {
            base.CreateRenderBuffer();
            BufferedItem.Material = DebugMaterial;
        }

        public override void OnRender(RendererEventArgs args)
        {
            if (args.Camera == this.Camera) //never render active camera
                return;
            base.OnRender(args);
        }

        public override void RenderGeometry(RendererEventArgs args)
        {
            if (DebugMesh != null)
                DebugMesh.Render(args.RC);
        }

        protected override bool PickNodeInternal(ref NodeIntersectionInfo intersinfo)
        {
            if (VisimEngine.Instance.Renderer.MainContext.MainCamera == this.Camera)
                return false;
            else 
                return base.PickNodeInternal(ref intersinfo);
        }


        public override void WriteInternalData(System.Xml.XmlWriter writer)
        {
            base.WriteInternalData(writer);
            if (this.Camera == null)
                return;
            writer.WriteStartElement("Camera");
            this.Camera.WriteXml(writer);
            writer.WriteEndElement();
        }

        protected override void CopyData(Node targetNode)
        {
            // no override needed, camera is copied as transformation
            base.CopyData(targetNode);
        }


        public override bool ReadInternalData(System.Xml.XmlReader reader)
        {
            if (reader.Name == "Camera")
            {
                string type = reader["type"];
                switch (type)
                {
                    case "QCamera":
                        StandardCamera cam = new StandardCamera();
                        cam.ReadXml(reader);
                        this.Trans = cam;   //todo - check that it won't get overwritten by badly formed XML
                        break;
                    case "FPSCamera":
                        FPSCamera fpscam = new FPSCamera();
                        fpscam.ReadXml(reader);
                        this.Trans = fpscam;
                        break;
                    default:
                        reader.Skip();
                        Console.DebugLine("Unknown type of camera " + type);
                        return true;
                }

                return true;
            }
            else 
                return base.ReadInternalData(reader);
        }

        #region IResourceConsumer Members

        public void ResourceDisposing(Resources.IResource resource)
        {
            if ((resource as IRenderMaterial) != null)
                DebugMaterial = null;
            if ((resource as Mesh) != null)
                DebugMesh = null;
        }

        public bool ResourceReplaced(Resources.IResource original, Resources.IResource newresource)
        {
            if ((original as IRenderMaterial) == DebugMaterial)
            {
                DebugMaterial = newresource as IRenderMaterial;
                return true;
            }

            if ((original as Mesh) == DebugMesh)
            {
                DebugMesh = newresource as Mesh;
                return true;
            }

            return false;
        }

        #endregion
    }
}
