using OpenTK.Graphics.OpenGL;
using System;

namespace Ryujinx.Graphics.Gal.OpenGL
{
    class ImageHandler
    {
        //TODO: Use a variable value here
        public const int MaxBpp = 16;

        private static int CopyBuffer = 0;
        private static int CopyBufferSize = 0;

        public GalImage Image { get; private set; }

        public int Width  => Image.Width;
        public int Height => Image.Height;

        public GalImageFormat Format => Image.Format;

        public PixelInternalFormat InternalFormat { get; private set; }
        public PixelFormat         PixelFormat    { get; private set; }
        public PixelType           PixelType      { get; private set; }

        public int Handle { get; private set; }

        private bool Initialized;

        public ImageHandler()
        {
            Handle = GL.GenTexture();
        }

        public ImageHandler(int Handle, GalImage Image)
        {
            this.Handle = Handle;

            this.Image = Image;
        }

        public void EnsureSetup(GalImage Image)
        {
            if (Width  != Image.Width  ||
                Height != Image.Height ||
                Format != Image.Format ||
                !Initialized)
            {
                (PixelInternalFormat InternalFormat, PixelFormat PixelFormat, PixelType PixelType) =
                    OGLEnumConverter.GetImageFormat(Image.Format);

                GL.BindTexture(TextureTarget.Texture2D, Handle);

                if (Initialized)
                {
                    if (CopyBuffer == 0)
                    {
                        CopyBuffer = GL.GenBuffer();
                    }

                    int MaxWidth  = Math.Max(Image.Width, Width);
                    int MaxHeight = Math.Max(Image.Height, Height);

                    int CurrentSize = MaxWidth * MaxHeight * MaxBpp;

                    GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyBuffer);
                    GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyBuffer);

                    if (CopyBufferSize < CurrentSize)
                    {
                        CopyBufferSize = CurrentSize;

                        GL.BufferData(BufferTarget.PixelPackBuffer, CurrentSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
                    }

                    GL.GetTexImage(TextureTarget.Texture2D, 0, this.PixelFormat, this.PixelType, IntPtr.Zero);

                    GL.DeleteTexture(Handle);

                    Handle = GL.GenTexture();

                    GL.BindTexture(TextureTarget.Texture2D, Handle);
                }

                const int MinFilter = (int)TextureMinFilter.Linear;
                const int MagFilter = (int)TextureMagFilter.Linear;

                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);

                const int Level = 0;
                const int Border = 0;

                GL.TexImage2D(
                    TextureTarget.Texture2D,
                    Level,
                    InternalFormat,
                    Image.Width,
                    Image.Height,
                    Border,
                    PixelFormat,
                    PixelType,
                    IntPtr.Zero);

                if (Initialized)
                {
                    GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
                    GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
                }

                this.Image = Image;

                this.InternalFormat = InternalFormat;
                this.PixelFormat = PixelFormat;
                this.PixelType = PixelType;

                Initialized = true;
            }
        }

        public bool HasColor   { get => ImageFormatConverter.HasColor(Format); }
        public bool HasDepth   { get => ImageFormatConverter.HasDepth(Format); }
        public bool HasStencil { get => ImageFormatConverter.HasStencil(Format); }
    }
}