#include <extra2d/graphics/backends/opengl/gl_pipeline.h>
#include <extra2d/utils/logger.h>

namespace extra2d {

// ============================================================================
// GLPipeline 实现
// ============================================================================

GLPipeline::GLPipeline() = default;

GLPipeline::~GLPipeline() {
    shutdown();
}

bool GLPipeline::init(const PipelineDesc& desc) {
    if (initialized_) {
        shutdown();
    }

    blendMode_ = desc.blendMode;
    blendEnabled_ = desc.blendEnabled;
    depthTest_ = desc.depthTest;
    depthWrite_ = desc.depthWrite;
    depthFunc_ = desc.depthFunc;
    cullMode_ = desc.cullMode;

    initialized_ = true;

    E2D_LOG_DEBUG("GLPipeline initialized: blendMode={}, depthTest={}, cullMode={}",
                  static_cast<int>(blendMode_), depthTest_, static_cast<int>(cullMode_));

    return true;
}

void GLPipeline::shutdown() {
    initialized_ = false;
}

void GLPipeline::bind() {
    if (!initialized_) {
        return;
    }

    applyAllStates();
}

void GLPipeline::unbind() {
    // OpenGL 不需要显式解绑管线
}

void GLPipeline::setBlendMode(BlendMode mode) {
    blendMode_ = mode;
    applyBlendState();
}

void GLPipeline::setDepthTest(bool enabled) {
    depthTest_ = enabled;
    applyDepthState();
}

void GLPipeline::setDepthWrite(bool enabled) {
    depthWrite_ = enabled;
    applyDepthState();
}

void GLPipeline::setDepthFunc(DepthFunc func) {
    depthFunc_ = func;
    applyDepthState();
}

void GLPipeline::setCullMode(CullMode mode) {
    cullMode_ = mode;
    applyCullState();
}

void GLPipeline::setViewport(int x, int y, int width, int height) {
    viewportX_ = x;
    viewportY_ = y;
    viewportWidth_ = width;
    viewportHeight_ = height;

    // 检查缓存，避免冗余调用
    if (x != cachedViewportX_ || y != cachedViewportY_ ||
        width != cachedViewportWidth_ || height != cachedViewportHeight_) {
        glViewport(x, y, width, height);
        cachedViewportX_ = x;
        cachedViewportY_ = y;
        cachedViewportWidth_ = width;
        cachedViewportHeight_ = height;
    }
}

void GLPipeline::getViewport(int& x, int& y, int& width, int& height) const {
    x = viewportX_;
    y = viewportY_;
    width = viewportWidth_;
    height = viewportHeight_;
}

void GLPipeline::applyAllStates() {
    applyBlendState();
    applyDepthState();
    applyCullState();

    // 应用视口
    if (viewportWidth_ > 0 && viewportHeight_ > 0) {
        setViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
    }
}

void GLPipeline::applyBlendState() {
    // 检查是否需要启用/禁用混合
    if (blendEnabled_ != cachedBlendEnabled_) {
        if (blendEnabled_) {
            glEnable(GL_BLEND);
        } else {
            glDisable(GL_BLEND);
        }
        cachedBlendEnabled_ = blendEnabled_;
    }

    // 如果禁用了混合，不需要设置混合函数
    if (!blendEnabled_) {
        return;
    }

    // 检查混合模式是否改变
    if (blendMode_ != cachedBlendMode_) {
        GLenum srcFactor, dstFactor;
        getBlendFactors(blendMode_, srcFactor, dstFactor);
        glBlendFunc(srcFactor, dstFactor);
        cachedBlendMode_ = blendMode_;
    }
}

void GLPipeline::applyDepthState() {
    // 深度测试
    if (depthTest_ != cachedDepthTest_) {
        if (depthTest_) {
            glEnable(GL_DEPTH_TEST);
        } else {
            glDisable(GL_DEPTH_TEST);
        }
        cachedDepthTest_ = depthTest_;
    }

    // 深度写入
    if (depthWrite_ != cachedDepthWrite_) {
        glDepthMask(depthWrite_ ? GL_TRUE : GL_FALSE);
        cachedDepthWrite_ = depthWrite_;
    }

    // 深度函数
    if (depthFunc_ != cachedDepthFunc_) {
        glDepthFunc(convertDepthFunc(depthFunc_));
        cachedDepthFunc_ = depthFunc_;
    }
}

void GLPipeline::applyCullState() {
    // 检查裁剪模式是否改变
    if (cullMode_ != cachedCullMode_) {
        if (cullMode_ == CullMode::None) {
            glDisable(GL_CULL_FACE);
        } else {
            glEnable(GL_CULL_FACE);
            glCullFace(convertCullMode(cullMode_));
        }
        cachedCullMode_ = cullMode_;
    }
}

void GLPipeline::getBlendFactors(BlendMode mode, GLenum& srcFactor, GLenum& dstFactor) {
    switch (mode) {
        case BlendMode::None:
            srcFactor = GL_ONE;
            dstFactor = GL_ZERO;
            break;
        case BlendMode::Alpha:
            srcFactor = GL_SRC_ALPHA;
            dstFactor = GL_ONE_MINUS_SRC_ALPHA;
            break;
        case BlendMode::Additive:
            srcFactor = GL_SRC_ALPHA;
            dstFactor = GL_ONE;
            break;
        case BlendMode::Multiply:
            srcFactor = GL_DST_COLOR;
            dstFactor = GL_ONE_MINUS_SRC_ALPHA;
            break;
        default:
            srcFactor = GL_SRC_ALPHA;
            dstFactor = GL_ONE_MINUS_SRC_ALPHA;
            break;
    }
}

GLenum GLPipeline::convertDepthFunc(DepthFunc func) {
    switch (func) {
        case DepthFunc::Never:        return GL_NEVER;
        case DepthFunc::Less:         return GL_LESS;
        case DepthFunc::Equal:        return GL_EQUAL;
        case DepthFunc::LessEqual:    return GL_LEQUAL;
        case DepthFunc::Greater:      return GL_GREATER;
        case DepthFunc::NotEqual:     return GL_NOTEQUAL;
        case DepthFunc::GreaterEqual: return GL_GEQUAL;
        case DepthFunc::Always:       return GL_ALWAYS;
        default:                      return GL_LESS;
    }
}

GLenum GLPipeline::convertCullMode(CullMode mode) {
    switch (mode) {
        case CullMode::Front: return GL_FRONT;
        case CullMode::Back:  return GL_BACK;
        case CullMode::Both:  return GL_FRONT_AND_BACK;
        default:              return GL_BACK;
    }
}

} // namespace extra2d
