/*
 * Copyright (C) 2011-2012 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 "../cpu_ref/rsd_cpu.h"

#include "rsdCore.h"
#include "rsdAllocation.h"
#include "rsdBcc.h"
#include "rsdElement.h"
#include "rsdType.h"
#ifndef RS_COMPATIBILITY_LIB
    #include "rsdGL.h"
    #include "rsdProgramStore.h"
    #include "rsdProgramRaster.h"
    #include "rsdProgramVertex.h"
    #include "rsdProgramFragment.h"
    #include "rsdMesh.h"
    #include "rsdFrameBuffer.h"
#else
    #include <dlfcn.h>
#endif
#include "rsdSampler.h"
#include "rsdScriptGroup.h"

#include <malloc.h>
#include "rsContext.h"

#include <sys/types.h>
#include <sys/resource.h>
#include <sched.h>
#include <sys/syscall.h>
#include <string.h>

using namespace android;
using namespace android::renderscript;

static void Shutdown(Context *rsc);
static void SetPriority(const Context *rsc, int32_t priority);

#ifndef RS_COMPATIBILITY_LIB
    #define NATIVE_FUNC(a) a
#else
    #define NATIVE_FUNC(a) nullptr
#endif

extern "C" bool rsdHalQueryHal(RsHalInitEnums entry, void **fnPtr) {
    switch(entry) {
    case RS_HAL_CORE_SHUTDOWN:
        fnPtr[0] = (void *)Shutdown; break;
    case RS_HAL_CORE_SET_PRIORITY:
        fnPtr[0] = (void *)SetPriority; break;
    case RS_HAL_CORE_ALLOC_RUNTIME_MEM:
        fnPtr[0] = (void *)rsdAllocRuntimeMem; break;
    case RS_HAL_CORE_FREE_RUNTIME_MEM:
        fnPtr[0] = (void *)rsdFreeRuntimeMem; break;
    case RS_HAL_CORE_FINISH:
        fnPtr[0] = (void *)nullptr; break;

    case RS_HAL_SCRIPT_INIT:
        fnPtr[0] = (void *)rsdScriptInit; break;
    case RS_HAL_SCRIPT_INIT_INTRINSIC:
        fnPtr[0] = (void *)rsdInitIntrinsic; break;
    case RS_HAL_SCRIPT_INVOKE_FUNCTION:
        fnPtr[0] = (void *)rsdScriptInvokeFunction; break;
    case RS_HAL_SCRIPT_INVOKE_ROOT:
        fnPtr[0] = (void *)rsdScriptInvokeRoot; break;
    case RS_HAL_SCRIPT_INVOKE_FOR_EACH:
        fnPtr[0] = (void *)rsdScriptInvokeForEach; break;
    case RS_HAL_SCRIPT_INVOKE_INIT:
        fnPtr[0] = (void *)rsdScriptInvokeInit; break;
    case RS_HAL_SCRIPT_INVOKE_FREE_CHILDREN:
        fnPtr[0] = (void *)rsdScriptInvokeFreeChildren; break;
    case RS_HAL_SCRIPT_SET_GLOBAL_VAR:
        fnPtr[0] = (void *)rsdScriptSetGlobalVar; break;
    case RS_HAL_SCRIPT_GET_GLOBAL_VAR:
        fnPtr[0] = (void *)rsdScriptGetGlobalVar; break;
    case RS_HAL_SCRIPT_SET_GLOBAL_VAR_WITH_ELEMENT_DIM:
        fnPtr[0] = (void *)rsdScriptSetGlobalVarWithElemDims; break;
    case RS_HAL_SCRIPT_SET_GLOBAL_BIND:
        fnPtr[0] = (void *)rsdScriptSetGlobalBind; break;
    case RS_HAL_SCRIPT_SET_GLOBAL_OBJECT:
        fnPtr[0] = (void *)rsdScriptSetGlobalObj; break;
    case RS_HAL_SCRIPT_DESTROY:
        fnPtr[0] = (void *)rsdScriptDestroy; break;
    case RS_HAL_SCRIPT_INVOKE_FOR_EACH_MULTI:
        fnPtr[0] = (void *)rsdScriptInvokeForEachMulti; break;
    case RS_HAL_SCRIPT_UPDATE_CACHED_OBJECT:
        fnPtr[0] = (void *)rsdScriptUpdateCachedObject; break;
    case RS_HAL_SCRIPT_INVOKE_REDUCE:
        fnPtr[0] = (void *)rsdScriptInvokeReduce; break;

    case RS_HAL_ALLOCATION_INIT:
        fnPtr[0] = (void *)rsdAllocationInit; break;
    case RS_HAL_ALLOCATION_INIT_OEM:
        fnPtr[0] = (void *)nullptr; break;
    case RS_HAL_ALLOCATION_INIT_ADAPTER:
        fnPtr[0] = (void *)rsdAllocationAdapterInit; break;
    case RS_HAL_ALLOCATION_DESTROY:
        fnPtr[0] = (void *)rsdAllocationDestroy; break;
    case RS_HAL_ALLOCATION_GET_GRALLOC_BITS:
        fnPtr[0] = (void *)rsdAllocationGrallocBits; break;
    case RS_HAL_ALLOCATION_RESIZE:
        fnPtr[0] = (void *)rsdAllocationResize; break;
    case RS_HAL_ALLOCATION_SYNC_ALL:
        fnPtr[0] = (void *)rsdAllocationSyncAll; break;
    case RS_HAL_ALLOCATION_MARK_DIRTY:
        fnPtr[0] = (void *)rsdAllocationMarkDirty; break;
    case RS_HAL_ALLOCATION_SET_SURFACE:
        fnPtr[0] = (void *)NATIVE_FUNC(rsdAllocationSetSurface); break;
    case RS_HAL_ALLOCATION_IO_SEND:
        fnPtr[0] = (void *)NATIVE_FUNC(rsdAllocationIoSend); break;
    case RS_HAL_ALLOCATION_IO_RECEIVE:
        fnPtr[0] = (void *)NATIVE_FUNC(rsdAllocationIoReceive); break;
    case RS_HAL_ALLOCATION_DATA_1D:
        fnPtr[0] = (void *)rsdAllocationData1D; break;
    case RS_HAL_ALLOCATION_DATA_2D:
        fnPtr[0] = (void *)rsdAllocationData2D; break;
    case RS_HAL_ALLOCATION_DATA_3D:
        fnPtr[0] = (void *)rsdAllocationData3D; break;
    case RS_HAL_ALLOCATION_READ_1D:
        fnPtr[0] = (void *)rsdAllocationRead1D; break;
    case RS_HAL_ALLOCATION_READ_2D:
        fnPtr[0] = (void *)rsdAllocationRead2D; break;
    case RS_HAL_ALLOCATION_READ_3D:
        fnPtr[0] = (void *)rsdAllocationRead3D; break;
    case RS_HAL_ALLOCATION_LOCK_1D:
        fnPtr[0] = (void *)rsdAllocationLock1D; break;
    case RS_HAL_ALLOCATION_UNLOCK_1D:
        fnPtr[0] = (void *)rsdAllocationUnlock1D; break;
    case RS_HAL_ALLOCATION_COPY_1D:
        fnPtr[0] = (void *)rsdAllocationData1D_alloc; break;
    case RS_HAL_ALLOCATION_COPY_2D:
        fnPtr[0] = (void *)rsdAllocationData2D_alloc; break;
    case RS_HAL_ALLOCATION_COPY_3D:
        fnPtr[0] = (void *)rsdAllocationData3D_alloc; break;
    case RS_HAL_ALLOCATION_ELEMENT_DATA:
        fnPtr[0] = (void *)rsdAllocationElementData; break;
    case RS_HAL_ALLOCATION_ELEMENT_READ:
        fnPtr[0] = (void *)rsdAllocationElementRead; break;
    case RS_HAL_ALLOCATION_GENERATE_MIPMAPS:
        fnPtr[0] = (void *)rsdAllocationGenerateMipmaps; break;
    case RS_HAL_ALLOCATION_UPDATE_CACHED_OBJECT:
        fnPtr[0] = (void *)rsdAllocationUpdateCachedObject; break;
    case RS_HAL_ALLOCATION_ADAPTER_OFFSET:
        fnPtr[0] = (void *)rsdAllocationAdapterOffset; break;
    case RS_HAL_ALLOCATION_GET_POINTER:
        fnPtr[0] = (void *)nullptr; break;
#ifdef RS_COMPATIBILITY_LIB
    case RS_HAL_ALLOCATION_INIT_STRIDED:
        fnPtr[0] = (void *)rsdAllocationInitStrided; break;
#endif

    case RS_HAL_SAMPLER_INIT:
        fnPtr[0] = (void *)rsdSamplerInit; break;
    case RS_HAL_SAMPLER_DESTROY:
        fnPtr[0] = (void *)rsdSamplerDestroy; break;
    case RS_HAL_SAMPLER_UPDATE_CACHED_OBJECT:
        fnPtr[0] = (void *)rsdSamplerUpdateCachedObject; break;

    case RS_HAL_TYPE_INIT:
        fnPtr[0] = (void *)rsdTypeInit; break;
    case RS_HAL_TYPE_DESTROY:
        fnPtr[0] = (void *)rsdTypeDestroy; break;
    case RS_HAL_TYPE_UPDATE_CACHED_OBJECT:
        fnPtr[0] = (void *)rsdTypeUpdateCachedObject; break;

    case RS_HAL_ELEMENT_INIT:
        fnPtr[0] = (void *)rsdElementInit; break;
    case RS_HAL_ELEMENT_DESTROY:
        fnPtr[0] = (void *)rsdElementDestroy; break;
    case RS_HAL_ELEMENT_UPDATE_CACHED_OBJECT:
        fnPtr[0] = (void *)rsdElementUpdateCachedObject; break;

    case RS_HAL_SCRIPT_GROUP_INIT:
        fnPtr[0] = (void *)rsdScriptGroupInit; break;
    case RS_HAL_SCRIPT_GROUP_DESTROY:
        fnPtr[0] = (void *)rsdScriptGroupDestroy; break;
    case RS_HAL_SCRIPT_GROUP_UPDATE_CACHED_OBJECT:
        fnPtr[0] = (void *)nullptr; break;
    case RS_HAL_SCRIPT_GROUP_SET_INPUT:
        fnPtr[0] = (void *)rsdScriptGroupSetInput; break;
    case RS_HAL_SCRIPT_GROUP_SET_OUTPUT:
        fnPtr[0] = (void *)rsdScriptGroupSetOutput; break;
    case RS_HAL_SCRIPT_GROUP_EXECUTE:
        fnPtr[0] = (void *)rsdScriptGroupExecute; break;



    // Functions below this point are for the legacy graphics api,
    // vendor drivers are NOT expected to implement these.  They will never be called
    // for an external driver.
#ifndef RS_COMPATIBILITY_LIB
    case RS_HAL_GRAPHICS_INIT:
        fnPtr[0] = (void *)rsdGLInit; break;
    case RS_HAL_GRAPHICS_SHUTDOWN:
        fnPtr[0] = (void *)rsdGLShutdown; break;
    case RS_HAL_GRAPHICS_SWAP:
        fnPtr[0] = (void *)rsdGLSwap; break;
    case RS_HAL_GRAPHICS_SET_SURFACE:
        fnPtr[0] = (void *)rsdGLSetSurface; break;
    case RS_HAL_GRAPHICS_RASTER_INIT:
        fnPtr[0] = (void *)rsdProgramRasterInit; break;
    case RS_HAL_GRAPHICS_RASTER_SET_ACTIVE:
        fnPtr[0] = (void *)rsdProgramRasterSetActive; break;
    case RS_HAL_GRAPHICS_RASTER_DESTROY:
        fnPtr[0] = (void *)rsdProgramRasterDestroy; break;
    case RS_HAL_GRAPHICS_VERTEX_INIT:
        fnPtr[0] = (void *)rsdProgramVertexInit; break;
    case RS_HAL_GRAPHICS_VERTEX_SET_ACTIVE:
        fnPtr[0] = (void *)rsdProgramVertexSetActive; break;
    case RS_HAL_GRAPHICS_VERTEX_DESTROY:
        fnPtr[0] = (void *)rsdProgramVertexDestroy; break;
    case RS_HAL_GRAPHICS_FRAGMENT_INIT:
        fnPtr[0] = (void *)rsdProgramFragmentInit; break;
    case RS_HAL_GRAPHICS_FRAGMENT_SET_ACTIVE:
        fnPtr[0] = (void *)rsdProgramFragmentSetActive; break;
    case RS_HAL_GRAPHICS_FRAGMENT_DESTROY:
        fnPtr[0] = (void *)rsdProgramFragmentDestroy; break;
    case RS_HAL_GRAPHICS_MESH_INIT:
        fnPtr[0] = (void *)rsdMeshInit; break;
    case RS_HAL_GRAPHICS_MESH_DRAW:
        fnPtr[0] = (void *)rsdMeshDraw; break;
    case RS_HAL_GRAPHICS_MESH_DESTROY:
        fnPtr[0] = (void *)rsdMeshDestroy; break;
    case RS_HAL_GRAPHICS_FB_INIT:
        fnPtr[0] = (void *)rsdFrameBufferInit; break;
    case RS_HAL_GRAPHICS_FB_SET_ACTIVE:
        fnPtr[0] = (void *)rsdFrameBufferSetActive; break;
    case RS_HAL_GRAPHICS_FB_DESTROY:
        fnPtr[0] = (void *)rsdFrameBufferDestroy; break;
    case RS_HAL_GRAPHICS_STORE_INIT:
        fnPtr[0] = (void *)rsdProgramStoreInit; break;
    case RS_HAL_GRAPHICS_STORE_SET_ACTIVE:
        fnPtr[0] = (void *)rsdProgramStoreSetActive; break;
    case RS_HAL_GRAPHICS_STORE_DESTROY:
        fnPtr[0] = (void *)rsdProgramStoreDestroy; break;
#endif

    default:
        ALOGE("ERROR: unknown RenderScript HAL API query, %i", entry);
        return false;
    }

    return true;
}

extern "C" void rsdHalAbort(RsContext) {

}


extern "C" bool rsdHalQueryVersion(uint32_t *major, uint32_t *minor) {
    *major = RS_HAL_VERSION;
    *minor = 0;
    return true;
}



extern const RsdCpuReference::CpuSymbol * rsdLookupRuntimeStub(Context * pContext, char const* name);

static RsdCpuReference::CpuScript * LookupScript(Context *, const Script *s) {
    return (RsdCpuReference::CpuScript *)s->mHal.drv;
}

#ifdef RS_COMPATIBILITY_LIB
typedef void (*sAllocationDestroyFnPtr) (const Context *rsc, Allocation *alloc);
typedef void (*sAllocationIoSendFnPtr) (const Context *rsc, Allocation *alloc);
typedef void (*sAllocationSetSurfaceFnPtr) (const Context *rsc, Allocation *alloc, ANativeWindow *nw);
static sAllocationDestroyFnPtr sAllocationDestroy;
static sAllocationIoSendFnPtr sAllocationIoSend;
static sAllocationSetSurfaceFnPtr sAllocationSetSurface;

static bool loadIOSuppLibSyms() {
    void* handleIO = nullptr;
    handleIO = dlopen("libRSSupportIO.so", RTLD_LAZY | RTLD_LOCAL);
    if (handleIO == nullptr) {
        ALOGE("Couldn't load libRSSupportIO.so");
        return false;
    }
    sAllocationDestroy = (sAllocationDestroyFnPtr)dlsym(handleIO, "rscAllocationDestroy");
    if (sAllocationDestroy==nullptr) {
        ALOGE("Failed to initialize sAllocationDestroy");
        return false;
    }
    sAllocationIoSend = (sAllocationIoSendFnPtr)dlsym(handleIO, "rscAllocationIoSend");
    if (sAllocationIoSend==nullptr) {
        ALOGE("Failed to initialize sAllocationIoSend");
        return false;
    }
    sAllocationSetSurface = (sAllocationSetSurfaceFnPtr)dlsym(handleIO, "rscAllocationSetSurface");
    if (sAllocationSetSurface==nullptr) {
        ALOGE("Failed to initialize sAllocationIoSend");
        return false;
    }
    return true;
}
#endif

extern "C" bool rsdHalInit(RsContext c, uint32_t version_major,
                           uint32_t version_minor) {
    Context *rsc = (Context*) c;
#ifdef RS_COMPATIBILITY_LIB
    if (loadIOSuppLibSyms()) {
        rsc->mHal.funcs.allocation.destroy = sAllocationDestroy;
        rsc->mHal.funcs.allocation.ioSend = sAllocationIoSend;
        rsc->mHal.funcs.allocation.setSurface = sAllocationSetSurface;
    }
#endif

    RsdHal *dc = (RsdHal *)calloc(1, sizeof(RsdHal));
    if (!dc) {
        ALOGE("Calloc for driver hal failed.");
        return false;
    }
    rsc->mHal.drv = dc;

    dc->mCpuRef = RsdCpuReference::create(rsc, version_major, version_minor,
                                          &rsdLookupRuntimeStub, &LookupScript);
    if (!dc->mCpuRef) {
        ALOGE("RsdCpuReference::create for driver hal failed.");
        rsc->mHal.drv = nullptr;
        free(dc);
        return false;
    }

    return true;
}


void SetPriority(const Context *rsc, int32_t priority) {
    RsdHal *dc = (RsdHal *)rsc->mHal.drv;

    dc->mCpuRef->setPriority(priority);

#ifndef RS_COMPATIBILITY_LIB
    if (dc->mHasGraphics) {
        rsdGLSetPriority(rsc, priority);
    }
#endif
}

void Shutdown(Context *rsc) {
    RsdHal *dc = (RsdHal *)rsc->mHal.drv;
    delete dc->mCpuRef;
    free(dc);
    rsc->mHal.drv = nullptr;
}

void* rsdAllocRuntimeMem(size_t size, uint32_t flags) {
    void* buffer = calloc(size, sizeof(char));
    return buffer;
}

void rsdFreeRuntimeMem(void* ptr) {
    free(ptr);
}