/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "rsdCore.h"
#include "rsdProgramStore.h"

#include "rsContext.h"
#include "rsProgramStore.h"

#include <GLES/gl.h>
#include <GLES/glext.h>

using android::renderscript::Context;
using android::renderscript::ProgramStore;

struct DrvProgramStore {
    GLenum blendSrc;
    GLenum blendDst;
    bool blendEnable;

    GLenum depthFunc;
    bool depthTestEnable;
};

bool rsdProgramStoreInit(const Context *rsc, const ProgramStore *ps) {
    DrvProgramStore *drv = (DrvProgramStore *)calloc(1, sizeof(DrvProgramStore));
    if (drv == nullptr) {
        return false;
    }

    ps->mHal.drv = drv;
    drv->depthTestEnable = true;

    switch (ps->mHal.state.depthFunc) {
    case RS_DEPTH_FUNC_ALWAYS:
        drv->depthTestEnable = false;
        drv->depthFunc = GL_ALWAYS;
        break;
    case RS_DEPTH_FUNC_LESS:
        drv->depthFunc = GL_LESS;
        break;
    case RS_DEPTH_FUNC_LEQUAL:
        drv->depthFunc = GL_LEQUAL;
        break;
    case RS_DEPTH_FUNC_GREATER:
        drv->depthFunc = GL_GREATER;
        break;
    case RS_DEPTH_FUNC_GEQUAL:
        drv->depthFunc = GL_GEQUAL;
        break;
    case RS_DEPTH_FUNC_EQUAL:
        drv->depthFunc = GL_EQUAL;
        break;
    case RS_DEPTH_FUNC_NOTEQUAL:
        drv->depthFunc = GL_NOTEQUAL;
        break;
    default:
        ALOGE("Unknown depth function.");
        goto error;
    }



    drv->blendEnable = true;
    if ((ps->mHal.state.blendSrc == RS_BLEND_SRC_ONE) &&
        (ps->mHal.state.blendDst == RS_BLEND_DST_ZERO)) {
        drv->blendEnable = false;
    }

    switch (ps->mHal.state.blendSrc) {
    case RS_BLEND_SRC_ZERO:
        drv->blendSrc = GL_ZERO;
        break;
    case RS_BLEND_SRC_ONE:
        drv->blendSrc = GL_ONE;
        break;
    case RS_BLEND_SRC_DST_COLOR:
        drv->blendSrc = GL_DST_COLOR;
        break;
    case RS_BLEND_SRC_ONE_MINUS_DST_COLOR:
        drv->blendSrc = GL_ONE_MINUS_DST_COLOR;
        break;
    case RS_BLEND_SRC_SRC_ALPHA:
        drv->blendSrc = GL_SRC_ALPHA;
        break;
    case RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA:
        drv->blendSrc = GL_ONE_MINUS_SRC_ALPHA;
        break;
    case RS_BLEND_SRC_DST_ALPHA:
        drv->blendSrc = GL_DST_ALPHA;
        break;
    case RS_BLEND_SRC_ONE_MINUS_DST_ALPHA:
        drv->blendSrc = GL_ONE_MINUS_DST_ALPHA;
        break;
    case RS_BLEND_SRC_SRC_ALPHA_SATURATE:
        drv->blendSrc = GL_SRC_ALPHA_SATURATE;
        break;
    default:
        rsc->setError(RS_ERROR_FATAL_DRIVER, "Unknown blend src mode.");
        goto error;
    }

    switch (ps->mHal.state.blendDst) {
    case RS_BLEND_DST_ZERO:
        drv->blendDst = GL_ZERO;
        break;
    case RS_BLEND_DST_ONE:
        drv->blendDst = GL_ONE;
        break;
    case RS_BLEND_DST_SRC_COLOR:
        drv->blendDst = GL_SRC_COLOR;
        break;
    case RS_BLEND_DST_ONE_MINUS_SRC_COLOR:
        drv->blendDst = GL_ONE_MINUS_SRC_COLOR;
        break;
    case RS_BLEND_DST_SRC_ALPHA:
        drv->blendDst = GL_SRC_ALPHA;
        break;
    case RS_BLEND_DST_ONE_MINUS_SRC_ALPHA:
        drv->blendDst = GL_ONE_MINUS_SRC_ALPHA;
        break;
    case RS_BLEND_DST_DST_ALPHA:
        drv->blendDst = GL_DST_ALPHA;
        break;
    case RS_BLEND_DST_ONE_MINUS_DST_ALPHA:
        drv->blendDst = GL_ONE_MINUS_DST_ALPHA;
        break;
    default:
        rsc->setError(RS_ERROR_FATAL_DRIVER, "Unknown blend dst mode.");
        goto error;
    }

    return true;

error:
    free(drv);
    ps->mHal.drv = nullptr;
    return false;
}

void rsdProgramStoreSetActive(const Context *rsc, const ProgramStore *ps) {
    DrvProgramStore *drv = (DrvProgramStore *)ps->mHal.drv;

    RSD_CALL_GL(glColorMask, ps->mHal.state.colorRWriteEnable,
                ps->mHal.state.colorGWriteEnable,
                ps->mHal.state.colorBWriteEnable,
                ps->mHal.state.colorAWriteEnable);

    if (drv->blendEnable) {
        RSD_CALL_GL(glEnable, GL_BLEND);
        RSD_CALL_GL(glBlendFunc, drv->blendSrc, drv->blendDst);
    } else {
        RSD_CALL_GL(glDisable, GL_BLEND);
    }

    if (rsc->mUserSurfaceConfig.depthMin > 0) {
        RSD_CALL_GL(glDepthMask, ps->mHal.state.depthWriteEnable);
        if (drv->depthTestEnable || ps->mHal.state.depthWriteEnable) {
            RSD_CALL_GL(glEnable, GL_DEPTH_TEST);
            RSD_CALL_GL(glDepthFunc, drv->depthFunc);
        } else {
            RSD_CALL_GL(glDisable, GL_DEPTH_TEST);
        }
    } else {
        RSD_CALL_GL(glDepthMask, false);
        RSD_CALL_GL(glDisable, GL_DEPTH_TEST);
    }

    /*
    if (rsc->mUserSurfaceConfig.stencilMin > 0) {
    } else {
        glStencilMask(0);
        glDisable(GL_STENCIL_TEST);
    }
    */

    if (ps->mHal.state.ditherEnable) {
        RSD_CALL_GL(glEnable, GL_DITHER);
    } else {
        RSD_CALL_GL(glDisable, GL_DITHER);
    }
}

void rsdProgramStoreDestroy(const Context *rsc, const ProgramStore *ps) {
    free(ps->mHal.drv);
    ps->mHal.drv = nullptr;
}