#include "opengl_backend.h"
#include <logger.h>

bool OpenGLTextureBackend::InitializeGLAD()
{
    gladLoaderLoadEGL(EGL_NO_DISPLAY);
    int egl_version = gladLoadEGL(eglGetCurrentDisplay(), eglGetProcAddress);
    if (egl_version == 0)
    {
        Logger::Log("OpenGLTextureBackend: Failed to load EGL");
        return false;
    }

    int gl_version = gladLoaderLoadGLES2();
    if (gl_version == 0)
    {
        Logger::Log("OpenGLTextureBackend: Failed to load GLES2");
        return false;
    }

    Logger::Log("OpenGLTextureBackend: GLAD initialized successfully");
    return true;
}

bool OpenGLTextureBackend::Create(AHardwareBuffer* buffer)
{
    if (m_Created)
    {
        Destroy();
    }

    // Get buffer dimensions
    AHardwareBuffer_Desc desc{};
    AHardwareBuffer_describe(buffer, &desc);
    m_Width = static_cast<GLint>(desc.width);
    m_Height = static_cast<GLint>(desc.height);

    // Create EGL image from hardware buffer
    EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(buffer);
    EGLImageKHR eglImage = eglCreateImageKHR(
        eglGetCurrentDisplay(),
        EGL_NO_CONTEXT,
        EGL_NATIVE_BUFFER_ANDROID,
        clientBuffer,
        nullptr
    );

    if (eglImage == EGL_NO_IMAGE_KHR)
    {
        Logger::Log("OpenGLTextureBackend: Failed to create EGL image");
        return false;
    }

    // Create source texture (external OES format from hardware buffer)
    glGenTextures(1, &m_SourceTexture);
    glBindTexture(GL_TEXTURE_EXTERNAL_OES, m_SourceTexture);
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
    eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);

    // Create source framebuffer
    glGenFramebuffers(1, &m_SourceFramebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, m_SourceFramebuffer);
    glFramebufferTexture2D(
        GL_FRAMEBUFFER,
        GL_COLOR_ATTACHMENT0,
        GL_TEXTURE_EXTERNAL_OES,
        m_SourceTexture,
        0
    );

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        Logger::Log("OpenGLTextureBackend: Source framebuffer incomplete");
        Destroy();
        return false;
    }

    // Create destination texture (standard 2D texture for Unity)
    glGenTextures(1, &m_DestTexture);
    glBindTexture(GL_TEXTURE_2D, m_DestTexture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(
        GL_TEXTURE_2D,
        0,
        GL_RGB,
        m_Width,
        m_Height,
        0,
        GL_RGB,
        GL_UNSIGNED_BYTE,
        nullptr
    );

    // Create destination framebuffer
    glGenFramebuffers(1, &m_DestFramebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, m_DestFramebuffer);
    glFramebufferTexture2D(
        GL_FRAMEBUFFER,
        GL_COLOR_ATTACHMENT0,
        GL_TEXTURE_2D,
        m_DestTexture,
        0
    );

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        Logger::Log("OpenGLTextureBackend: Dest framebuffer incomplete");
        Destroy();
        return false;
    }

    m_Created = true;

    // Initial blit
    Blit();

    Logger::Log("OpenGLTextureBackend: Created successfully");
    return true;
}

void OpenGLTextureBackend::Update()
{
    if (m_Created)
    {
        Blit();
    }
}

void OpenGLTextureBackend::Blit()
{
    glBindFramebuffer(GL_READ_FRAMEBUFFER, m_SourceFramebuffer);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_DestFramebuffer);
    glBlitFramebuffer(
        0, 0, m_Width, m_Height,
        0, 0, m_Width, m_Height,
        GL_COLOR_BUFFER_BIT,
        GL_NEAREST
    );
}

void OpenGLTextureBackend::Destroy()
{
    if (m_DestFramebuffer)
    {
        glDeleteFramebuffers(1, &m_DestFramebuffer);
        m_DestFramebuffer = 0;
    }
    if (m_SourceFramebuffer)
    {
        glDeleteFramebuffers(1, &m_SourceFramebuffer);
        m_SourceFramebuffer = 0;
    }
    if (m_DestTexture)
    {
        glDeleteTextures(1, &m_DestTexture);
        m_DestTexture = 0;
    }
    if (m_SourceTexture)
    {
        glDeleteTextures(1, &m_SourceTexture);
        m_SourceTexture = 0;
    }
    m_Width = 0;
    m_Height = 0;
    m_Created = false;
}

void* OpenGLTextureBackend::GetNativeTexturePtr()
{
    return reinterpret_cast<void*>(static_cast<uintptr_t>(m_DestTexture));
}
