using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisimBox.Renderer;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics;
using VisiMath;
using System.Drawing;
using System.Drawing.Imaging;
using VisimBox.Renderer.RenderObjects;

namespace VisimRenderGL
{
    partial class GLRenderContext : RenderContext
    {
        IGraphicsContext tkgc;
        OpenTK.GLControl glcontrol;

        ColorByte backColor;

        bool _fog = false;
        ColorByte _fogColor = ColorByte.Black;
        float _fogDensity = 0.35f;
        bool _fogSettings = false;



        #region Rendering Settings

        /// <summary>
        /// Enables / disables fog drawing
        /// </summary>
        public override bool Fog { 
            get
            {
                return _fog;
            }
            set
            {
                if (_fog == value)
                    return;

                _fog = value;

                if (!value)
                {
                    GL.Disable(EnableCap.Fog);
                }
                else
                {
                    if (!_fogSettings)
                    {
                        GL.Fog(FogParameter.FogMode, (int)FogMode.Exp);
                        GL.Fog(FogParameter.FogDensity, _fogDensity);
                        GL.Hint(HintTarget.FogHint, HintMode.Nicest);
                        GL.Fog(FogParameter.FogColor, _fogColor.GetFloats());
                        GL.ShadeModel(ShadingModel.Smooth);
                        _fogSettings = true;
                    }

                    GL.Enable(EnableCap.Fog);
                }

            }
         }

        /// <summary>
        /// Color of the fog
        /// </summary>
        public override ColorByte FogColor { 
            get
            {
                return _fogColor;
            } 
            set
            {
                if (_fogColor == value)
                    return;
                _fogColor = value;
                GL.Fog(FogParameter.FogColor, _fogColor.GetFloats());
            } 
        }

        /// <summary>
        /// Density of the fog
        /// </summary>
        public override float FogDensity { 
            get
            {
                return _fogDensity;
            }
            set
            {
                if (_fogDensity == value)
                    return;
                _fogDensity = value;

                GL.Fog(FogParameter.FogDensity, _fogDensity);
            }
        }

        #endregion



        public override bool ZBufferUsing
        {
            get
            {
                return _zBuffer;
            }
            set
            {
                if (_zBuffer != value)
                {
                    if (value)
                        GL.Enable(EnableCap.DepthTest);
                    else
                        GL.Disable(EnableCap.DepthTest);
                }
                _zBuffer = value;
            }
        }


        public System.Windows.Forms.Control RenderControl
        {
            get
            {
                return glcontrol;
            }
        }

        public override Texture CurrentTexture
        {
            get
            {
                return _CurrentTexture;
            }
            set
            {
                _CurrentTexture = value;
                if (value == null)
                {
                    OpenTK.Graphics.OpenGL.GL.Disable(EnableCap.Texture2D);
                    OpenTK.Graphics.OpenGL.GL.BindTexture(OpenTK.Graphics.OpenGL.TextureTarget.Texture2D, 0);
                }
            }
        }

        public GLRenderContext(RenderPlugin gl, RenderContextSettings set, OpenTK.GLControl glc)
            : base(gl, set)
        {
            tkgc = glc.Context;
            glcontrol = glc;
            glc.MakeCurrent();
            ChangeBackColor(settings.BackgroundColor, true);
            _CurrentFont = gl.FontMan.GetDefault();

            glc.Resize += new EventHandler(glc_Resize);
            StartUp();
        }

        /// <summary>
        /// Window was resized
        /// </summary>
        void glc_Resize(object sender, EventArgs e)
        {
            System.Windows.Forms.Control ctrl = (System.Windows.Forms.Control)sender;

            System.Drawing.Size siz = ctrl.Size;

            if (!settings.Resizable)
            {
                //resize not allowed, return size to previous
                siz = new System.Drawing.Size(settings.Width, settings.Height);
            }
            else
            {
                //check constraints
                if (settings.MinWidth!=0)
                    siz.Width = VisiMath.MathTools.ClampMin(siz.Width, settings.MinWidth);
                if (settings.MaxWidth != 0)
                    siz.Width = VisiMath.MathTools.ClampMax(siz.Width, settings.MaxWidth);
                if (settings.MinHeight != 0)
                    siz.Height = VisiMath.MathTools.ClampMin(siz.Height, settings.MinHeight);
                if (settings.MaxHeight != 0)
                    siz.Height = VisiMath.MathTools.ClampMax(siz.Height, settings.MaxHeight);

                if (settings.KeepAspectRatio)
                {
                    if (settings.Height != 0)
                    {
                        float asp = (float)settings.Width / (float)settings.Height;

                        if (asp > 0.001f)
                        {
                            siz.Height = (int)(siz.Width / asp);
                        }
                    }
                }
            }

            settings.Window.ClientSize = siz;
            //problem!!! how to force parent of the VMcontrol to change its size?
            RecalcMainViewPort(siz.Width, siz.Height);
        }


        void RecalcMainViewPort(int neww, int newh)
        {
            mainViewport.Coordinates = new System.Drawing.Rectangle(0, 0, neww, newh);
        }

        void ChangeBackColor(ColorByte bcol, bool force = false)
        {            
            if ((force) || (backColor != bcol))
            {
                backColor = bcol;
                GL.ClearColor(new Color4(bcol.R, bcol.G, bcol.B, bcol.A));
            }
        }

        public RPYTransformation testobj = null;
        VertexArray<ColoredVertex> testbuffer = null;


        public void StartUp()
        {
            GL.ClearDepth(1);
            GL.DepthFunc(DepthFunction.Lequal);
            GL.Enable(EnableCap.DepthTest);
            GL.Enable(EnableCap.ColorMaterial);
            GL.ShadeModel(ShadingModel.Smooth);
            GL.Enable(EnableCap.CullFace);
            GL.FrontFace(FrontFaceDirection.Cw);
            GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);




            testobj = new RPYTransformation();
            testobj.Move(new Vector3(10, -2, -10));
            //testobj.Roll(1.57f);
            //testobj.Yaw(1.57f);
            if (MainCamera != null)
            {
                //MainCamera.Move(new Vector3(0,
            }

            
            //col.FromSystemColor(Color.FloralWhite);

            ColorByte col = new ColorByte(Color.FloralWhite.ToArgb());

            testbuffer = new VertexArray<ColoredVertex>(12);

            testbuffer[0] = new ColoredVertex(new Vector3(4, 0, 0), col);
            testbuffer[1] = new ColoredVertex(new Vector3(-4, 2, 0), col);
            testbuffer[2] = new ColoredVertex(new Vector3(-4, -2, 0), col);
            testbuffer[3] = new ColoredVertex(new Vector3(-4, 4, 0), col);
            testbuffer[4] = new ColoredVertex(new Vector3(-4, -2, 0), col);
            testbuffer[5] = new ColoredVertex(new Vector3(-3.8f, -2, 0), col);

            col.FromARGB(Color.Yellow.ToArgb());
            testbuffer[6] = new ColoredVertex(new Vector3(-4, 0, 2), col);
            testbuffer[7] = new ColoredVertex(new Vector3(-4, 2, 0), col);
            testbuffer[8] = new ColoredVertex(new Vector3(-4, -2, 0), col);

            col.FromARGB(Color.Violet.ToArgb());

            testbuffer[9] = new ColoredVertex(new Vector3(-4, 0, 0), col);
            testbuffer[10] = new ColoredVertex(new Vector3(-4, 0, 1), col);
            testbuffer[11] = new ColoredVertex(new Vector3(3, 0, 0), col);
        }

        bool startup = true;

        public override void StartFrame()
        {
            glcontrol.MakeCurrent();            
            base.StartFrame();

            CheckWindowNameChange();
            CheckFullScreenChange();
            CheckResChange();


            if (backColor != settings.BackgroundColor)
            {
                ChangeBackColor(settings.BackgroundColor);
            }


            if (!settings.ClearBackground)
                GL.Clear(ClearBufferMask.DepthBufferBit);
            else
                GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            SetupMainViewPort();
            GL.MatrixMode(MatrixMode.Modelview);

            
            ICamera cam = MainCamera;
            if (cam != null)
            {
                if (startup)
                {
                    startup = false;
                }

                Matrix4 mat = cam.GetViewTransformation();
                
                /*
                Vector3 eye = new Vector3(0,0,-10);
                Vector3 targ = testobj.Position;
                Vector3 upv = new Vector3(0,0,1);
                mat = Matrix4.LookAt(ref eye, ref targ, ref upv);*/
                LoadMatrix(ref mat);

                /*
                OpenTK.Matrix4 mm = OpenTK.Matrix4.LookAt(eye.X, eye.Y, eye.Z, targ.X, targ.Y, targ.Z, 0, 0, 1);
                mm = OpenTK.Matrix4.LookAt(0, 0, -10, 0, 0, 0, 0, 0, 1);
                GL.LoadMatrix(ref mm);*/

            }
            else
            {
                GL.LoadIdentity();
            }
            //RenderArray(BeginMode.Triangles, testbuffer, 3);
        }

        void SetupMainViewPort()
        {
            SetViewPort(ref this.mainViewport);
        }

        void SetViewPort(ref Viewport viewp)
        {
            GL.Viewport(viewp.Coordinates);

            float vis = viewp.ViewQual.Visibility;
            if (vis <= 0)
            {
                vis = 10000.0f;
            }
            int w = viewp.Coordinates.Right - viewp.Coordinates.Left;
            int h = viewp.Coordinates.Bottom - viewp.Coordinates.Top;

            GL.MatrixMode(MatrixMode.Projection);

            if (viewp.Camera == null)
            {
                //create default camera
                viewp.Camera = CreateDefaultCamera();
            }
            Matrix4 viewproj = viewp.Camera.GetProjectionTransformation(w, h);
            LoadMatrix(ref viewproj);
            GL.MatrixMode(MatrixMode.Modelview);
        }

        private void LoadMatrix(ref Matrix4 mat)
        {
            GL.LoadMatrix(ref mat.m00);
        }

        private void LoadMatrix(Matrix4 mat)
        {
            GL.LoadMatrix(ref mat.m00);
        }

        private void MultMatrix(ref Matrix4 mat)
        {
            GL.MultMatrix(ref mat.m00);
        }

        private void MultMatrix(Matrix4 mat)
        {
            GL.MultMatrix(ref mat.m00);
        }


        /// <summary>
        /// Check if width / height in the settings was changed - valid only for automatically created windows
        /// </summary>
        private void CheckResChange()
        {
            //not implemented, do later
        }

        private void CheckFullScreenChange()
        {
            //not implemented, do later
        }

        private void CheckWindowNameChange()
        {
            //not implemented, do later
        }

        public override void EndFrame()
        {            
            base.EndFrame();
            Start2DRender();           

            if (_showFPS)
            {
                //todo: use system font?
                SetColor(ColorByte.White);
                DrawText(0, 0, fps.ToString());
            }

            Stop2DRender();

            bool drawzbuffer = false;

            if (drawzbuffer)
            {
                float[] pixels = new float[glcontrol.ClientSize.Width * glcontrol.ClientSize.Height * 4];

                //GL.ReadPixels<float>(0, 0, glcontrol.ClientSize.Width, glcontrol.ClientSize.Height, OpenTK.Graphics.OpenGL.PixelFormat.DepthComponent, PixelType.Float, pixels);
                GL.ReadPixels<float>(0, 0, glcontrol.ClientSize.Width, glcontrol.ClientSize.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgr, PixelType.Float, pixels);
                GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
                
                OpenTK.Vector3 vec = new OpenTK.Vector3(10,10,-10);
                GL.RasterPos3(vec);

                //GL.DrawPixels<float>(glcontrol.ClientSize.Width, glcontrol.ClientSize.Height, OpenTK.Graphics.OpenGL.PixelFormat.Luminance, PixelType.Float, pixels);
                GL.DrawPixels<float>(glcontrol.ClientSize.Width, glcontrol.ClientSize.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgr, PixelType.Float, pixels);

                /*
                var stream = VisimBox.FileSystem.Instance.WriteFile("dakalaka.baka");

                System.IO.BinaryWriter bw = new System.IO.BinaryWriter(stream);
                for (int i = 0; i < pixels.Length; i++)
                    bw.Write((double)pixels[i]);
                bw.Flush();
                bw.Close();
                stream.Close();
                */
            }



            glcontrol.SwapBuffers();    //NOTE!! this might change time for rendering frame if FPS counter will be changed later...
        }



        public override Bitmap GetScreenShot()
        {
            Bitmap bmp = new Bitmap(glcontrol.ClientSize.Width, glcontrol.ClientSize.Height);
            System.Drawing.Imaging.BitmapData data =
                 bmp.LockBits(glcontrol.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            GL.ReadPixels(0, 0, glcontrol.ClientSize.Width, glcontrol.ClientSize.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
            bmp.UnlockBits(data);
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return bmp;
        }

        public override void RenderFrame()
        {
            base.RenderFrame();
        }




    }
}
