/*
 * 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 */

static 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 *this = theOneTrueEngine;
        if (NULL != this) {
            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(&this->mObject);

            // now expose additional interfaces not requested by the earlier engine create
            const struct iid_vtable *x = pCEngine_class->mInterfaces;
            SLuint8 *interfaceStateP = this->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 *) this + 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(&this->mObject);
            // return the shared engine object
            *pEngine = &this->mObject.mItf;
            break;
        }

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

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

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

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

    } while(0);

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

    return result;
}


/** \brief slCreateEngine Function */

SL_API SLresult SLAPIENTRY slCreateEngine(SLObjectItf *pEngine, SLuint32 numOptions,
    const SLEngineOption *pEngineOptions, SLuint32 numInterfaces,
    const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired)
{
    SL_ENTER_GLOBAL

    result = liCreateEngine(pEngine, numOptions, pEngineOptions, numInterfaces, pInterfaceIds,
            pInterfaceRequired, objectIDtoClass(SL_OBJECTID_ENGINE));

    SL_LEAVE_GLOBAL
}


/** Internal function for slQuerySupportedEngineInterfaces and xaQuerySupportedEngineInterfaces */

static SLresult liQueryNumSupportedInterfaces(SLuint32 *pNumSupportedInterfaces,
        const ClassTable *class__)
{
    SLresult result;
    if (NULL == pNumSupportedInterfaces) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        assert(NULL != class__);
        SLuint32 count = 0;
        SLuint32 i;
        for (i = 0; i < class__->mInterfaceCount; ++i) {
            switch (class__->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;
}


/** \brief slQueryNumSupportedEngineInterfaces Function */

SL_API SLresult SLAPIENTRY slQueryNumSupportedEngineInterfaces(SLuint32 *pNumSupportedInterfaces)
{
    SL_ENTER_GLOBAL

    result = liQueryNumSupportedInterfaces(pNumSupportedInterfaces,
            objectIDtoClass(SL_OBJECTID_ENGINE));

    SL_LEAVE_GLOBAL
}


/** Internal function for slQuerySupportedEngineInterfaces and xaQuerySupportedEngineInterfaces */

static SLresult liQuerySupportedInterfaces(SLuint32 index, SLInterfaceID *pInterfaceId,
        const ClassTable *class__)
{
    SLresult result;
    if (NULL == pInterfaceId) {
        result = SL_RESULT_PARAMETER_INVALID;
    } else {
        *pInterfaceId = NULL;
        assert(NULL != class__);
        result = SL_RESULT_PARAMETER_INVALID;   // will be reset later
        SLuint32 i;
        for (i = 0; i < class__->mInterfaceCount; ++i) {
            switch (class__->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[class__->mInterfaces[i].mMPH];
                result = SL_RESULT_SUCCESS;
                break;
            }
            --index;
        }
    }
    return result;
}


/** \brief slQuerySupportedEngineInterfaces Function */

SL_API SLresult SLAPIENTRY slQuerySupportedEngineInterfaces(SLuint32 index,
        SLInterfaceID *pInterfaceId)
{
    SL_ENTER_GLOBAL

    result = liQuerySupportedInterfaces(index, pInterfaceId, objectIDtoClass(SL_OBJECTID_ENGINE));

    SL_LEAVE_GLOBAL
}


/** \brief xaCreateEngine Function */

XA_API XAresult XAAPIENTRY xaCreateEngine(XAObjectItf *pEngine, XAuint32 numOptions,
        const XAEngineOption *pEngineOptions, XAuint32 numInterfaces,
        const XAInterfaceID *pInterfaceIds, const XAboolean *pInterfaceRequired)
{
    XA_ENTER_GLOBAL

    result = liCreateEngine((SLObjectItf *) pEngine, numOptions,
            (const SLEngineOption *) pEngineOptions, numInterfaces,
            (const SLInterfaceID *) pInterfaceIds, (const SLboolean *) pInterfaceRequired,
            objectIDtoClass(XA_OBJECTID_ENGINE));

    XA_LEAVE_GLOBAL
}


/** \brief xaQueryNumSupportedEngineInterfaces Function */

XA_API XAresult XAAPIENTRY xaQueryNumSupportedEngineInterfaces(XAuint32 *pNumSupportedInterfaces)
{
    XA_ENTER_GLOBAL

    result = liQueryNumSupportedInterfaces(pNumSupportedInterfaces,
            objectIDtoClass(XA_OBJECTID_ENGINE));

    XA_LEAVE_GLOBAL
}


/** \brief xaQuerySupportedEngineInterfaces Function */

XA_API XAresult XAAPIENTRY xaQuerySupportedEngineInterfaces(XAuint32 index,
        XAInterfaceID *pInterfaceId)
{
    XA_ENTER_GLOBAL

    result = liQuerySupportedInterfaces(index, (SLInterfaceID *) pInterfaceId,
            objectIDtoClass(XA_OBJECTID_ENGINE));

    XA_LEAVE_GLOBAL
}
