//
// Created by sebastian on 29.07.26.
//

#include "GLStateCache.h"

#include <GL/gl.h>

namespace engine {
    void GLStateCache::init() {
        glDisable(GL_CULL_FACE);
        glDisable(GL_MULTISAMPLE);
        glDisable(GL_BLEND);
        glDisable(GL_DEPTH_TEST);
        glDepthMask(GL_TRUE);
        glDisable(GL_SCISSOR_TEST);
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }

    void GLStateCache::setFaceCulling(bool enabled, bool frontFace) {
        if (enabled) {
            if (!s_cullingFaces) {
                glEnable(GL_CULL_FACE);
                s_cullingFaces = true;
            }
            if (frontFace != s_frontFaceCulling) {
                glCullFace(frontFace ? GL_FRONT : GL_BACK);
                s_frontFaceCulling = frontFace;
            }
        } else if (s_cullingFaces) {
            glDisable(GL_CULL_FACE);
            s_cullingFaces = false;
        }
    }

    void GLStateCache::setAntialiasing(bool enabled) {
        if (enabled == s_antialiasing) return;
        enabled ? glEnable(GL_MULTISAMPLE) : glDisable(GL_MULTISAMPLE);
        s_antialiasing = enabled;
    }

    void GLStateCache::setAlphaBlending(bool enabled) {
        if (enabled == s_alphaBlending && !s_additiveBlending) return;
        if (enabled) {
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            s_alphaBlending = true;
            s_additiveBlending = false;
        } else if (s_alphaBlending) {
            glDisable(GL_BLEND);
            s_alphaBlending = false;
        }
    }

    void GLStateCache::setAdditiveBlending(bool enabled) {
        if (enabled && !s_additiveBlending) {
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE);
            s_additiveBlending = true;
            s_alphaBlending = false;
        } else if (!enabled && s_additiveBlending) {
            glDisable(GL_BLEND);
            s_additiveBlending = false;
        }
    }

    void GLStateCache::setDepthTest(bool enabled) {
        if (enabled == s_depthTest) return;
        enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
        s_depthTest = enabled;
    }

    void GLStateCache::setDepthWriting(bool enabled) {
        if (enabled == s_depthWriting) return;
        glDepthMask(enabled ? GL_TRUE : GL_FALSE);
        s_depthWriting = enabled;
    }

    void GLStateCache::setScissorTest(bool enabled, int x, int y, int width, int height) {
        if (enabled && !s_scissorTest) {
            glEnable(GL_SCISSOR_TEST);
            s_scissorTest = true;
        } else if (!enabled && s_scissorTest) {
            glDisable(GL_SCISSOR_TEST);
            s_scissorTest = false;
            return;
        }
        if (enabled && (x != s_scissorBounds[0] || y != s_scissorBounds[1] ||
                        width != s_scissorBounds[2] || height != s_scissorBounds[3])) {
            glScissor(x, y, width, height);
            s_scissorBounds[0] = x; s_scissorBounds[1] = y;
            s_scissorBounds[2] = width; s_scissorBounds[3] = height;
                        }
    }

    void GLStateCache::setWireframe(bool enabled) {
        if (enabled == s_wireframe) return;
        glPolygonMode(GL_FRONT_AND_BACK, enabled ? GL_LINE : GL_FILL);
        s_wireframe = enabled;
    }

    void GLStateCache::bindTexture2D(unsigned int textureId, int slot) {
        glActiveTexture(GL_TEXTURE0 + slot);
        glBindTexture(GL_TEXTURE_2D, textureId);
    }

    void GLStateCache::clearColorBuffer() { glClear(GL_COLOR_BUFFER_BIT); }
    void GLStateCache::clearDepthBuffer() { glClear(GL_DEPTH_BUFFER_BIT); }
    void GLStateCache::clearFrameBuffer() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); }

    void GLStateCache::clearFrameBuffer(glm::vec4 color) {
        glClearColor(color.r, color.g, color.b, color.a);
        clearFrameBuffer();
    }
}
