using OpenTK.Graphics.OpenGL4;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OpenTK_tutorial
{
    /** Framebuffer used for rendering shadows */
    class FrameBuffer
    {
        /** ID */
        public int ID;
        /** Widht and height */
        public int width, height;

        /** Color and depth buffer locations */
        public int colorBuffer, depthBuffer;

        FramebufferStatus framebufferstatus;

        /** Constructor */
        public FrameBuffer(int width, int height)
        {
            this.width = width;
            this.height = height;

            ID = GL.GenFramebuffer();
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, ID);

            // color buffer
            colorBuffer = GL.GenTexture();
            GL.BindTexture(TextureTarget.Texture2D, colorBuffer);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Linear);
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, PixelFormat.Rgba, PixelType.Byte, IntPtr.Zero);
            GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, colorBuffer, 0);

            // bind depth buffer
            depthBuffer = GL.GenTexture();
            GL.BindTexture(TextureTarget.Texture2D, depthBuffer);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureCompareMode, (int)TextureCompareMode.CompareRefToTexture);

            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.DepthComponent32f, width, height, 0, PixelFormat.DepthComponent, PixelType.Float, IntPtr.Zero);
            GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, TextureTarget.Texture2D, depthBuffer, 0);
            
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);

            // write status
            framebufferstatus = GL.CheckNamedFramebufferStatus(ID, FramebufferTarget.Framebuffer);
            Console.WriteLine("Created frambuffer: " + framebufferstatus);

            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
        }

        public FrameProcessor ScreenshotFrame()
        {
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, ID);
            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, colorBuffer, 0);
            GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, depthBuffer, 0);

            // save color
            byte[] pxC = new byte[width * height * 3];
            GL.ReadPixels(0, 0, width, height, PixelFormat.Bgr, PixelType.UnsignedByte, pxC);
            GL.Finish();

            Color[] cs = new Color[width * height];
            int index = 0;
            for (int i = 0; i < pxC.Length; i += 3)
            {
                cs[index] = Color.FromArgb(pxC[i + 2], pxC[i + 1], pxC[i]);
                index++;
            }

            // save depth
            float[] px = new float[width * height];
            GL.ReadPixels(0, 0, width, height, PixelFormat.DepthComponent, PixelType.Float, px);
            GL.Finish();

            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);

            return new FrameProcessor(width, height, px, cs);
        }
    }
}
