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

namespace VisimBuilder.Gizmos
{
    public class EditingGrid
    {
        protected float _GridSize = 1;
        protected Vector3 _GridMinimum = new Vector3(0), _GridMaximum = new Vector3(10);

        protected Matrix4 MyTrans = Matrix4.Identitity;

        protected ColorByte _GridColor = new ColorByte(255, 255, 255, 180);


        public ColorByte GridColor
        {
            get
            {
                return _GridColor;
            }

            set
            {
                if (value != _GridColor)
                {
                    _GridColor = value;
                    if (GridMaterial != null)
                    {
                        GridMaterial.SetColorElement(MaterialElement.Diffuse, value);
                    }
                }
            }
        }

        public float GridSize
        {
            get { return _GridSize; }
            set
            {
                if (_GridSize != value)
                {
                    _GridSize = value;
                    RecalcGridMesh();
                }
            }
        }


        public Vector3 GridExtentsMinimum
        {
            get
            {
                return _GridMinimum;
            }

            set
            {
                if (_GridMinimum != value)
                {
                    _GridMinimum = MathTools.Min(ref _GridMaximum, ref value);

                    RecalcGridMesh();
                }
            }
        }

        public Vector3 GridExtentsMaximum
        {
            get
            {
                return _GridMaximum;
            }

            set
            {
                if (_GridMaximum != value)
                {
                    _GridMaximum = MathTools.Max(ref _GridMinimum, ref value);
                    RecalcGridMesh();
                }
            }
        }

        public bool Hidden {get;set;}

        public bool AlwaysOnTop { get; set; }


        protected bool _MoveWithCamera, _MoveWithObject;

        public bool MoveWithCamera { get {return _MoveWithCamera;}
            set
            {
                _MoveWithCamera = value;
                if (value)
                    _MoveWithObject = false;
            }
        }

        public bool MoveWithSelectedObject { get {return _MoveWithObject;}
            set
            {
                _MoveWithObject = value;
                if (value)
                    _MoveWithCamera = false;
            } 
        }

        IRenderMaterial GridMaterial;
        Mesh GridMesh;

        protected void RecalcGridMesh()
        {
            var meshman = VisimBox.VisimEngine.Instance.Renderer.MeshMan;

            if (GridMesh != null)
            {
                meshman.Remove("EditingGridMesh");
            }

            int[] counts = new int[3];

            for (int i = 0; i < 3; i++)
            {
                float gridextent = _GridMaximum[i] - _GridMinimum[i];
                counts[i] = (int)(gridextent / _GridSize);
            }            
            
            int vertcount = 4* (counts[0] + counts[1] + counts[2]);

            LineList ll = new LineList(typeof(PositionVertex), vertcount);

            int index = 0;

            //x lines

            float xstart = _GridMinimum.X;

            for (int i = 0; i < counts[0]; i++)
            {
                Vector3 first = new Vector3(xstart, _GridMinimum.Y, 0);
                Vector3 second = new Vector3(xstart, _GridMaximum.Y, 0);
                ll.Vertices.Positions[index] = first;
                ll.Vertices.Positions[index + 1] = second;
                
                first = new Vector3(xstart, 0, _GridMinimum.Z);
                second = new Vector3(xstart, 0, _GridMaximum.Z);

                ll.Vertices.Positions[index + 2] = first;
                ll.Vertices.Positions[index + 3] = second;
                
                
                xstart += GridSize;

                index += 4;
            }


            //y lines

            float ystart = _GridMinimum.Y;

            for (int i = 0; i < counts[1]; i++)
            {
                Vector3 first = new Vector3(_GridMinimum.X, ystart, 0);
                Vector3 second = new Vector3(_GridMaximum.X, ystart, 0);

                ll.Vertices.Positions[index] = first;
                ll.Vertices.Positions[index + 1] = second;

                first = new Vector3(0, ystart, _GridMinimum.Z);
                second = new Vector3(0, ystart, _GridMaximum.Z);

                ll.Vertices.Positions[index + 2] = first;
                ll.Vertices.Positions[index + 3] = second;

                ystart += GridSize;                
                index += 4;
            }

            //z lines

            float zstart = _GridMinimum.Z;

            for (int i = 0; i < counts[2]; i++)
            {
                Vector3 first = new Vector3(_GridMinimum.X, 0, zstart);
                Vector3 second = new Vector3(_GridMaximum.X, 0, zstart);

                ll.Vertices.Positions[index] = first;
                ll.Vertices.Positions[index + 1] = second;

                first = new Vector3(0, _GridMinimum.Y, zstart);
                second = new Vector3(0, _GridMaximum.Y, zstart);


                ll.Vertices.Positions[index + 2] = first;
                ll.Vertices.Positions[index + 3] = second;

                zstart += GridSize;
                index += 4;
            }


            GridMesh = meshman.Add(ll, "EditingGridMesh");
        }

        public virtual void Render(RendererEventArgs args)
        {
            if (Hidden)
                return;

            bool zbuffer = args.RC.ZBufferUsing;

            GridMaterial.Activate(args);

            if (AlwaysOnTop)
            {
                args.RC.ZBufferUsing = false;
            }

            args.RC.SetTransformation(ref MyTrans);

            GridMesh.Render(args.RC);


            if (AlwaysOnTop)
            {
                args.RC.ZBufferUsing = zbuffer;
            }            
        }


        public EditingGrid()
        {
        }

        MainBuilderForm Boss;

        protected void PrepareMaterialsAndMesh()
        {
            //materials

            var matman = VisimBox.VisimEngine.Instance.Renderer.MaterialMan;

            if (matman.Contains("EditingGridMaterial"))
            {
                GridMaterial = matman["EditingGridMaterial"];
            }
            else
            {
                GridMaterial = matman.Add("EditingGridMaterial", GridColor);
            }

            RecalcGridMesh();
        }

        public void Init(MainBuilderForm boss)
        {
            Boss = boss;
            PrepareMaterialsAndMesh();
        }

        public void TimeTick()
        {
            if (MoveWithCamera)
            {
                var cam = VisimBox.VisimEngine.Instance.Renderer.MainContext.MainCamera;

                if (cam != null)
                {
                    Vector3 trans = _GridMaximum - GridExtentsMinimum;
                    trans.Mul(-0.5f);
                    trans.Add(cam.Position);
                    
                    MyTrans = Matrix4.CreateTranslation(ref trans);
                    return;
                }
            }
            
            if (MoveWithSelectedObject)
            {
                if (Boss.SelectedNode != null)
                {
                    Boss.SelectedNode.GetWorldTransformation(ref MyTrans);
                    return;
                }
            }
            
            
            MyTrans.SetToIdentity();
        }

    }
}
