﻿using Nemesis.Rendering;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;

namespace Nemesis.Rendering.OpenGL.Shader
{
    public abstract class MeshProgram : ShaderProgram
    {
        protected MeshProgram(MeshProgramVertexAttributes attributes, ShaderSource vertexShader, ShaderSource fragmentShader)
            : base(attributes, vertexShader, fragmentShader)
        { }

        public abstract void EnableTexture(ITexture texture, int textureUnit = 0);
        public abstract void DisableTexture();

        public abstract void SetMaterialColor(Color4 diffuse, Color4 specular, Color4 ambient);

        public abstract void SetModelMatrix(Matrix4 modelMatrix);
        public abstract void SetViewMatrix(Matrix4 viewMatrix);
        public abstract void SetProjectionMatrix(Matrix4 projectionMatrix);

        public void EnableVertexAttributes()
        {
            GL.EnableVertexAttribArray(MeshProgramVertexAttributes.VertexPositionLocation);
            GL.EnableVertexAttribArray(MeshProgramVertexAttributes.VertexNormalLocation);
            GL.EnableVertexAttribArray(MeshProgramVertexAttributes.VertexUVLocation);
        }

        public void DisableVertexAttributes()
        {
            GL.DisableVertexAttribArray(MeshProgramVertexAttributes.VertexPositionLocation);
            GL.DisableVertexAttribArray(MeshProgramVertexAttributes.VertexNormalLocation);
            GL.DisableVertexAttribArray(MeshProgramVertexAttributes.VertexUVLocation);
        }

        public void SetVertexPositionAttribPointer(int size, int stride, int offset)
        {
            GL.VertexAttribPointer(
                MeshProgramVertexAttributes.VertexPositionLocation,
                size, 
                VertexAttribPointerType.Float,
                false,
                stride,
                offset
            );
        }

        public void SetVertexNormalAttribPointer(int size, int stride, int offset)
        {
            GL.VertexAttribPointer(
                MeshProgramVertexAttributes.VertexNormalLocation,
                size,
                VertexAttribPointerType.Float,
                false,
                stride,
                offset
            );
        }

        public void SetVertexUVAttribPointer(int size, int stride, int offset)
        {
            GL.VertexAttribPointer(
                MeshProgramVertexAttributes.VertexUVLocation,
                size,
                VertexAttribPointerType.Float,
                false,
                stride,
                offset
            );
        }

        public sealed class MeshProgramVertexAttributes : VertexAttributes
        {
            public MeshProgramVertexAttributes(
                string vertexPositionAttributeName,
                string vertexNormalAttributeName,
                string vertexUVAttributeName
               )
            {
                RegisterAttribute(vertexPositionAttributeName, VertexPositionLocation);
                RegisterAttribute(vertexNormalAttributeName, VertexNormalLocation);
                RegisterAttribute(vertexUVAttributeName, VertexUVLocation);
            }

            public static int VertexPositionLocation
            {
                get
                {
                    return 0;
                }
            }

            public static int VertexNormalLocation
            {
                get
                {
                    return 1;
                }
            }

            public static int VertexUVLocation
            {
                get
                {
                    return 2;
                }
            }
        }
    }
}
