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

#include "sles_allinclusive.h"


/* Exclusively lock an object */

#ifdef USE_DEBUG
void object_lock_exclusive_(IObject *this, const char *file, int line)
{
    int ok;
    ok = pthread_mutex_trylock(&this->mMutex);
    if (0 != ok) {
        SL_LOGE("object 0x%p was locked by 0x%p at %s:%d\n", this, *(void **)&this->mOwner,
            this->mFile, this->mLine);
        ok = pthread_mutex_lock(&this->mMutex);
        assert(0 == ok);
    }
    pthread_t zero;
    memset(&zero, 0, sizeof(pthread_t));
    assert(0 == memcmp(&zero, &this->mOwner, sizeof(pthread_t)));
    assert(NULL == this->mFile);
    assert(0 == this->mLine);
    this->mOwner = pthread_self();
    this->mFile = file;
    this->mLine = line;
}
#else
void object_lock_exclusive_(IObject *this)
{
    int ok;
    ok = pthread_mutex_lock(&this->mMutex);
    assert(0 == ok);
}
#endif


/* Exclusively unlock an object and do not report any updates */

void object_unlock_exclusive(IObject *this)
{
#ifdef USE_DEBUG
    assert(pthread_equal(pthread_self(), this->mOwner));
    assert(NULL != this->mFile);
    assert(0 != this->mLine);
    memset(&this->mOwner, 0, sizeof(pthread_t));
    this->mFile = NULL;
    this->mLine = 0;
#endif
    int ok;
    ok = pthread_mutex_unlock(&this->mMutex);
    assert(0 == ok);
}


/* Exclusively unlock an object and report updates to the specified bit-mask of attributes */

void object_unlock_exclusive_attributes(IObject *this, unsigned attributes)
{

#ifdef USE_DEBUG
    assert(pthread_equal(pthread_self(), this->mOwner));
    assert(NULL != this->mFile);
    assert(0 != this->mLine);
#endif

    int ok;
    SLuint32 objectID = IObjectToObjectID(this);
    CAudioPlayer *ap;

    // FIXME The endless if statements are getting ugly, should use bit search

    // Android likes to see certain updates synchronously

    if (attributes & ATTR_GAIN) {
        switch (objectID) {
        case SL_OBJECTID_AUDIOPLAYER:
            attributes &= ~ATTR_GAIN;   // no need to process asynchronously also
            ap = (CAudioPlayer *) this;
#ifdef ANDROID
            android_audioPlayer_volumeUpdate(ap);
#else
            audioPlayerGainUpdate(ap);
#endif
            break;
        case SL_OBJECTID_OUTPUTMIX:
            // FIXME update gains on all players attached to this outputmix
            SL_LOGE("[ FIXME: gain update on an SL_OBJECTID_OUTPUTMIX to be implemented ]");
            break;
        case SL_OBJECTID_MIDIPLAYER:
            // MIDI
            SL_LOGE("[ FIXME: gain update on an SL_OBJECTID_MIDIPLAYER to be implemented ]");
            break;
        default:
            break;
        }
    }

    if (attributes & ATTR_POSITION) {
        switch (objectID) {
        case SL_OBJECTID_AUDIOPLAYER:
            ap = (CAudioPlayer *) this;
#ifdef ANDROID
            attributes &= ~ATTR_POSITION;   // no need to process asynchronously also
            android_audioPlayer_seek(ap, ap->mSeek.mPos);
#else
            //audioPlayerTransportUpdate(ap);
#endif
            break;
        case SL_OBJECTID_MIDIPLAYER:
            // MIDI
            SL_LOGE("[ FIXME: position update on an SL_OBJECTID_MIDIPLAYER to be implemented ]");
            break;
        default:
            break;
        }
    }

    if (attributes & ATTR_TRANSPORT) {
        if (SL_OBJECTID_AUDIOPLAYER == objectID) {
#ifdef ANDROID
            attributes &= ~ATTR_TRANSPORT;   // no need to process asynchronously also
            ap = (CAudioPlayer *) this;
            // FIXME should only call when state changes
            android_audioPlayer_setPlayState(ap);
            // FIXME ditto, but for either eventflags or marker position
            android_audioPlayer_useEventMask(ap);
#else
            //audioPlayerTransportUpdate(ap);
#endif
        }
    }

    // ( buffer queue count is non-empty and play state == PLAYING ) became true
    if (attributes & ATTR_ENQUEUE) {
        if (SL_OBJECTID_AUDIOPLAYER == objectID) {
            attributes &= ~ATTR_ENQUEUE;
            ap = (CAudioPlayer *) this;
            if (SL_PLAYSTATE_PLAYING == ap->mPlay.mState) {
#ifdef ANDROID
                android_audioPlayer_bufferQueueRefilled(ap);
#endif
            }
        }
    }

    if (attributes) {
        unsigned oldAttributesMask = this->mAttributesMask;
        this->mAttributesMask = oldAttributesMask | attributes;
        if (oldAttributesMask)
            attributes = ATTR_NONE;
    }
#ifdef USE_DEBUG
    memset(&this->mOwner, 0, sizeof(pthread_t));
    this->mFile = NULL;
    this->mLine = 0;
#endif
    ok = pthread_mutex_unlock(&this->mMutex);
    assert(0 == ok);
    if (attributes) {   // first update to this interface since previous sync
        IEngine *thisEngine = this->mEngine;
        interface_lock_exclusive(thisEngine);
        thisEngine->mChangedMask |= 1 << (this->mInstanceID - 1);
        interface_unlock_exclusive(thisEngine);
    }
}


/* Wait on the condition variable associated with the object; see pthread_cond_wait */

void object_cond_wait(IObject *this)
{
    int ok;
    ok = pthread_cond_wait(&this->mCond, &this->mMutex);
    assert(0 == ok);
}


/* Signal the condition variable associated with the object; see pthread_cond_signal */

void object_cond_signal(IObject *this)
{
    int ok;
    ok = pthread_cond_signal(&this->mCond);
    assert(0 == ok);
}


/* Broadcast the condition variable associated with the object; see pthread_cond_broadcast */

void object_cond_broadcast(IObject *this)
{
    int ok;
    ok = pthread_cond_broadcast(&this->mCond);
    assert(0 == ok);
}
