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

/* Initial global entry points */

#include "sles_allinclusive.h"


/** \brief Internal code shared by slCreateEngine and xaCreateEngine */

LI_API SLresult liCreateEngine(SLObjectItf *pEngine, SLuint32 numOptions,
    const SLEngineOption *pEngineOptions, SLuint32 numInterfaces,
    const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired,
    const ClassTable *pCEngine_class)
{
    SLresult result;

    int ok;
    ok = pthread_mutex_lock(&theOneTrueMutex);
    assert(0 == ok);
    bool needToUnlockTheOneTrueMutex = true;

    do {

        if (NULL == pEngine) {
            result = SL_RESULT_PARAMETER_INVALID;
            break;
        }
        *pEngine = NULL;

        if ((0 < numOptions) && (NULL == pEngineOptions)) {
            SL_LOGE("numOptions=%lu and pEngineOptions=NULL", numOptions);
            result = SL_RESULT_PARAMETER_INVALID;
            break;
        }

        // default values
        SLboolean threadSafe = SL_BOOLEAN_TRUE;
        SLboolean lossOfControlGlobal = SL_BOOLEAN_FALSE;

        // process engine options
        SLuint32 i;
        const SLEngineOption *option = pEngineOptions;
        result = SL_RESULT_SUCCESS;
        for (i = 0; i < numOptions; ++i, ++option) {
            switch (option->feature) {
            case SL_ENGINEOPTION_THREADSAFE:
                threadSafe = SL_BOOLEAN_FALSE != (SLboolean) option->data; // normalize
                break;
            case SL_ENGINEOPTION_LOSSOFCONTROL:
                lossOfControlGlobal = SL_BOOLEAN_FALSE != (SLboolean) option->data; // normalize
                break;
            default:
                SL_LOGE("unknown engine option: feature=%lu data=%lu",
                    option->feature, option->data);
                result = SL_RESULT_PARAMETER_INVALID;
                break;
            }
        }
        if (SL_RESULT_SUCCESS != result) {
            break;
        }

        unsigned exposedMask;
        assert(NULL != pCEngine_class);
        result = checkInterfaces(pCEngine_class, numInterfaces,
            pInterfaceIds, pInterfaceRequired, &exposedMask);
        if (SL_RESULT_SUCCESS != result) {
            break;
        }

        // if an engine already exists, then increment its ref count
        CEngine *thiz = theOneTrueEngine;
        if (NULL != thiz) {
            assert(0 < theOneTrueRefCount);
            ++theOneTrueRefCount;

            // In order to update the engine object, we need to lock it,
            // but that would violate the lock order and potentially deadlock.
            // So we unlock now and note that it should not be unlocked later.
            ok = pthread_mutex_unlock(&theOneTrueMutex);
            assert(0 == ok);
            needToUnlockTheOneTrueMutex = false;
            object_lock_exclusive(&thiz->mObject);

            // now expose additional interfaces not requested by the earlier engine create
            const struct iid_vtable *x = pCEngine_class->mInterfaces;
            SLuint8 *interfaceStateP = thiz->mObject.mInterfaceStates;
            SLuint32 index;
            for (index = 0; index < pCEngine_class->mInterfaceCount; ++index, ++x,
                    exposedMask >>= 1, ++interfaceStateP) {
                switch (*interfaceStateP) {
                case INTERFACE_EXPOSED:         // previously exposed
                    break;
                case INTERFACE_INITIALIZED:     // not exposed during the earlier create
                    if (exposedMask & 1) {
                        const struct MPH_init *mi = &MPH_init_table[x->mMPH];
                        BoolHook expose = mi->mExpose;
                        if ((NULL == expose) || (*expose)((char *) thiz + x->mOffset)) {
                            *interfaceStateP = INTERFACE_EXPOSED;
                        }
                        // FIXME log or report to application that expose hook failed
                    }
                    break;
                case INTERFACE_UNINITIALIZED:   // no init hook
                    break;
                default:                        // impossible
                    assert(false);
                    break;
                }
            }
            object_unlock_exclusive(&thiz->mObject);
            // return the shared engine object
            *pEngine = &thiz->mObject.mItf;
            break;
        }

        // here when creating the first engine reference
        assert(0 == theOneTrueRefCount);

#ifdef ANDROID
        android::ProcessState::self()->startThreadPool();
        android::DataSource::RegisterDefaultSniffers();
#endif

        thiz = (CEngine *) construct(pCEngine_class, exposedMask, NULL);
        if (NULL == thiz) {
            result = SL_RESULT_MEMORY_FAILURE;
            break;
        }

        // initialize fields not associated with an interface
        // mThreadPool is initialized in CEngine_Realize
        memset(&thiz->mThreadPool, 0, sizeof(ThreadPool));
        memset(&thiz->mSyncThread, 0, sizeof(pthread_t));
#if defined(ANDROID)
        thiz->mEqNumPresets = 0;
        thiz->mEqPresetNames = NULL;
#endif
        // initialize fields related to an interface
        thiz->mObject.mLossOfControlMask = lossOfControlGlobal ? ~0 : 0;
        thiz->mEngine.mLossOfControlGlobal = lossOfControlGlobal;
        thiz->mEngineCapabilities.mThreadSafe = threadSafe;
        IObject_Publish(&thiz->mObject);
        theOneTrueEngine = thiz;
        theOneTrueRefCount = 1;
        // return the new engine object
        *pEngine = &thiz->mObject.mItf;

    } while(0);

    if (needToUnlockTheOneTrueMutex) {
        ok = pthread_mutex_unlock(&theOneTrueMutex);
        assert(0 == ok);
        needToUnlockTheOneTrueMutex = false;
    }

    return result;
}


/** Internal function for slQuerySupportedEngineInterfaces and xaQuerySupportedEngineInterfaces */

LI_API SLresult liQueryNumSupportedInterfaces(SLuint32 *pNumSupportedInterfaces,
        const ClassTable *clazz)
{
    SLresult result;
    if (NULL == pNumSupportedInterfaces) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        assert(NULL != clazz);
        SLuint32 count = 0;
        SLuint32 i;
        for (i = 0; i < clazz->mInterfaceCount; ++i) {
            switch (clazz->mInterfaces[i].mInterface) {
            case INTERFACE_IMPLICIT:
            case INTERFACE_IMPLICIT_PREREALIZE:
            case INTERFACE_EXPLICIT:
            case INTERFACE_EXPLICIT_PREREALIZE:
            case INTERFACE_DYNAMIC:
                ++count;
                break;
            case INTERFACE_UNAVAILABLE:
                break;
            default:
                assert(false);
                break;
            }
        }
        *pNumSupportedInterfaces = count;
        result = SL_RESULT_SUCCESS;
    }
    return result;
}


/** Internal function for slQuerySupportedEngineInterfaces and xaQuerySupportedEngineInterfaces */

LI_API SLresult liQuerySupportedInterfaces(SLuint32 index, SLInterfaceID *pInterfaceId,
        const ClassTable *clazz)
{
    SLresult result;
    if (NULL == pInterfaceId) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pInterfaceId = NULL;
        assert(NULL != clazz);
        result = SL_RESULT_PARAMETER_INVALID;   // will be reset later
        SLuint32 i;
        for (i = 0; i < clazz->mInterfaceCount; ++i) {
            switch (clazz->mInterfaces[i].mInterface) {
            case INTERFACE_IMPLICIT:
            case INTERFACE_IMPLICIT_PREREALIZE:
            case INTERFACE_EXPLICIT:
            case INTERFACE_EXPLICIT_PREREALIZE:
            case INTERFACE_DYNAMIC:
                break;
            case INTERFACE_UNAVAILABLE:
                continue;
            default:
                assert(false);
                break;
            }
            if (index == 0) {
                *pInterfaceId = &SL_IID_array[clazz->mInterfaces[i].mMPH];
                result = SL_RESULT_SUCCESS;
                break;
            }
            --index;
        }
    }
    return result;
}
