/*
 * Copyright (C) 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.
 */

#define LOG_TAG "libRS_cpp"

#include <utils/Log.h>
#include <malloc.h>
#include <string.h>

#include "RenderScript.h"
#include "rs.h"

using namespace android;
using namespace renderscriptCpp;

bool RenderScript::gInitialized = false;
pthread_mutex_t RenderScript::gInitMutex = PTHREAD_MUTEX_INITIALIZER;

RenderScript::RenderScript() {
    mDev = NULL;
    mContext = NULL;
    mErrorFunc = NULL;
    mMessageFunc = NULL;
    mMessageRun = false;

    memset(&mElements, 0, sizeof(mElements));
}

RenderScript::~RenderScript() {
    mMessageRun = false;

    rsContextDeinitToClient(mContext);

    void *res = NULL;
    int status = pthread_join(mMessageThreadId, &res);

    rsContextDestroy(mContext);
    mContext = NULL;
    rsDeviceDestroy(mDev);
    mDev = NULL;
}

bool RenderScript::init(int targetApi) {
    mDev = rsDeviceCreate();
    if (mDev == 0) {
        ALOGE("Device creation failed");
        return false;
    }

    mContext = rsContextCreate(mDev, 0, targetApi);
    if (mContext == 0) {
        ALOGE("Context creation failed");
        return false;
    }


    pid_t mNativeMessageThreadId;

    int status = pthread_create(&mMessageThreadId, NULL, threadProc, this);
    if (status) {
        ALOGE("Failed to start RenderScript message thread.");
        return false;
    }
    // Wait for the message thread to be active.
    while (!mMessageRun) {
        usleep(1000);
    }

    return true;
}

void RenderScript::throwError(const char *err) const {
    ALOGE("RS CPP error: %s", err);
    int * v = NULL;
    v[0] = 0;
}


void * RenderScript::threadProc(void *vrsc) {
    RenderScript *rs = static_cast<RenderScript *>(vrsc);
    size_t rbuf_size = 256;
    void * rbuf = malloc(rbuf_size);

    rsContextInitToClient(rs->mContext);
    rs->mMessageRun = true;

    while (rs->mMessageRun) {
        size_t receiveLen = 0;
        uint32_t usrID = 0;
        uint32_t subID = 0;
        RsMessageToClientType r = rsContextPeekMessage(rs->mContext,
                                                       &receiveLen, sizeof(receiveLen),
                                                       &usrID, sizeof(usrID));

        if (receiveLen >= rbuf_size) {
            rbuf_size = receiveLen + 32;
            rbuf = realloc(rbuf, rbuf_size);
        }
        if (!rbuf) {
            ALOGE("RenderScript::message handler realloc error %zu", rbuf_size);
            // No clean way to recover now?
        }
        rsContextGetMessage(rs->mContext, rbuf, rbuf_size, &receiveLen, sizeof(receiveLen),
                            &subID, sizeof(subID));

        switch(r) {
        case RS_MESSAGE_TO_CLIENT_ERROR:
            ALOGE("RS Error %s", (const char *)rbuf);

            if(rs->mMessageFunc != NULL) {
                rs->mErrorFunc(usrID, (const char *)rbuf);
            }
            break;
        case RS_MESSAGE_TO_CLIENT_EXCEPTION:
            // teardown. But we want to avoid starving other threads during
            // teardown by yielding until the next line in the destructor can
            // execute to set mRun = false
            usleep(1000);
            break;
        case RS_MESSAGE_TO_CLIENT_USER:
            if(rs->mMessageFunc != NULL) {
                rs->mMessageFunc(usrID, rbuf, receiveLen);
            } else {
                ALOGE("Received a message from the script with no message handler installed.");
            }
            break;

        default:
            ALOGE("RenderScript unknown message type %i", r);
        }
    }

    if (rbuf) {
        free(rbuf);
    }
    ALOGE("RenderScript Message thread exiting.");
    return NULL;
}

void RenderScript::setErrorHandler(ErrorHandlerFunc_t func) {
    mErrorFunc = func;
}

void RenderScript::setMessageHandler(MessageHandlerFunc_t func) {
    mMessageFunc  = func;
}

void RenderScript::contextDump() {
}

void RenderScript::finish() {

}