#include "ogl4_fbo.h"
#include "ogl4_texture_fbo.h"

namespace sleek
{
    namespace driver
    {
        static const GLuint ogl4_fbo_attachment[] = {
            GL_COLOR_ATTACHMENT0_EXT,
            GL_COLOR_ATTACHMENT1_EXT,
            GL_COLOR_ATTACHMENT2_EXT,
            GL_COLOR_ATTACHMENT3_EXT,
            GL_COLOR_ATTACHMENT4_EXT,
            GL_COLOR_ATTACHMENT5_EXT,
            GL_COLOR_ATTACHMENT6_EXT,
            GL_COLOR_ATTACHMENT7_EXT,
            GL_COLOR_ATTACHMENT8_EXT,
            GL_COLOR_ATTACHMENT9_EXT,
            GL_COLOR_ATTACHMENT10_EXT,
            GL_COLOR_ATTACHMENT11_EXT,
            GL_COLOR_ATTACHMENT12_EXT,
            GL_COLOR_ATTACHMENT13_EXT,
            GL_COLOR_ATTACHMENT14_EXT,
            GL_COLOR_ATTACHMENT15_EXT,
            GL_DEPTH_ATTACHMENT_EXT,
            GL_STENCIL_ATTACHMENT_EXT
        };

        template<>
        ogl4_fbo<false>::ogl4_fbo(const math::vec2i &size, const std::vector<TextureFormat> &a) noexcept : fbo(size, a)
        {
            glGenFramebuffersEXT(1, &framebuffer);
                glGenRenderbuffersEXT(1, &depthbuffer);

            bind();

                for(int i = 0; i<attachment.size(); ++i)
                textures.push_back(new ogl4_texture_fbo<false>(this, size, ogl4_fbo_attachment[i], attachment[i]));

                glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);
                glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, size.x, size.y);

            unbind();

                status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
            glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

                if(status == GL_FRAMEBUFFER_UNSUPPORTED_EXT)
                {
                printf(">> GL_FRAMEBUFFER_UNSUPPORTED_EXT!\n");
                return;
                }
                if(status != GL_FRAMEBUFFER_COMPLETE)
                {
                printf(">> GL_FRAMEBUFFER_COMPLETE!\n");
                return;
                }
        }

        template<>
        ogl4_fbo<true>::ogl4_fbo(const math::vec2i &size, const std::vector<TextureFormat> &a) noexcept : fbo(size, a)
        {
            glGenFramebuffersEXT(1, &framebuffer);
                glGenRenderbuffersEXT(1, &depthbuffer);

                for(int i = 0; i<attachment.size(); ++i)
                textures.push_back(new ogl4_texture_fbo<true>(this, size, ogl4_fbo_attachment[i], attachment[i]));

                glNamedRenderbufferStorageEXT(framebuffer, GL_DEPTH_COMPONENT24, size.x, size.y);
        }

        template<bool dsa>
        ogl4_fbo<dsa>::~ogl4_fbo() noexcept
        {
            glDeleteRenderbuffersEXT(1, &depthbuffer);
            glDeleteFramebuffersEXT(1, &framebuffer);

                for(int i = 0; i<textures.size(); ++i)
                delete textures[i];
        }

        template<bool dsa>
        void* ogl4_fbo<dsa>::getHardwareLink() const noexcept
        {
            return (GLuint*)&framebuffer;
        }

        template<bool dsa>
        void ogl4_fbo<dsa>::resize(const math::vec2i &s) noexcept
        {
                for(int i = 0; i<textures.size(); ++i)
                ((ogl4_texture_fbo<true>*)textures[i])->resize(s);
            fbo::resize(s);
            update();
        }

        template<>
        void ogl4_fbo<false>::update() noexcept
        {
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer);
                glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, size.x, size.y);

                for(int i = 0; i<textures.size(); ++i)
                textures[i]->update();

                status = glCheckNamedFramebufferStatusEXT(framebuffer, GL_FRAMEBUFFER_EXT);

                if(status == GL_FRAMEBUFFER_UNSUPPORTED_EXT)
                {
                printf(">> GL_FRAMEBUFFER_UNSUPPORTED_EXT!\n");
                return;
                }
                if(status != GL_FRAMEBUFFER_COMPLETE)
                {
                printf(">> GL_FRAMEBUFFER_COMPLETE!\n");
                return;
                }
        }

        template<>
        void ogl4_fbo<true>::update() noexcept
        {
            glNamedFramebufferRenderbufferEXT(framebuffer, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer);
                glNamedRenderbufferStorageEXT(framebuffer, GL_DEPTH_COMPONENT24, size.x, size.y);

                for(int i = 0; i<textures.size(); ++i)
                textures[i]->update();

                status = glCheckNamedFramebufferStatusEXT(framebuffer, GL_FRAMEBUFFER_EXT);

                if(status == GL_FRAMEBUFFER_UNSUPPORTED_EXT)
                printf(">> GL_FRAMEBUFFER_UNSUPPORTED_EXT!\n");
                if(status != GL_FRAMEBUFFER_COMPLETE)
                printf(">> GL_FRAMEBUFFER_COMPLETE!\n");
        }

        template<bool dsa>
        void ogl4_fbo<dsa>::bind() noexcept
        {
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
            glDrawBuffersARB(textures.size(), ogl4_fbo_attachment);
            glClearColor(
                f32(color.red/255.f),
                f32(color.green/255.f),
                f32(color.blue/255.f),
                f32(color.alpha/255.f)
            );
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        }


        template<bool dsa>
        void ogl4_fbo<dsa>::bind(int i) noexcept
        {
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
            glDrawBuffersARB(1, ogl4_fbo_attachment+i);
            glClearColor(
                f32(color.red/255.f),
                f32(color.green/255.f),
                f32(color.blue/255.f),
                f32(color.alpha/255.f)
            );
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        }

        template<bool dsa>
        void ogl4_fbo<dsa>::unbind() noexcept
        {
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
        }
    }
}