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

/* StreamInformation implementation */

#include "sles_allinclusive.h"

static XAresult IStreamInformation_QueryMediaContainerInformation( XAStreamInformationItf self,
        XAMediaContainerInformation * info /* [out] */)
{
    XA_ENTER_INTERFACE

    if (NULL == info) {
        result = XA_RESULT_PARAMETER_INVALID;
        XA_LEAVE_INTERFACE
    }

#ifdef ANDROID
    IStreamInformation *thiz = (IStreamInformation *) self;
    interface_lock_shared(thiz);
    // always storing container info at index 0, as per spec
    *info = thiz->mStreamInfoTable.itemAt(0).containerInfo;
    interface_unlock_shared(thiz);
    // even though the pointer to the media container info is returned, the values aren't set
    //  for the actual container in this version, they are simply initialized to defaults
    //  (see IStreamInformation_init)
    result = XA_RESULT_SUCCESS;
#else
    SL_LOGE("QueryMediaContainerInformation is unsupported");
    memset(info, 0, sizeof(XAMediaContainerInformation));
    result = XA_RESULT_FEATURE_UNSUPPORTED;
#endif

    XA_LEAVE_INTERFACE
}


static XAresult IStreamInformation_QueryStreamType( XAStreamInformationItf self,
        XAuint32 streamIndex, /* [in] */
        XAuint32 *domain)     /* [out] */
{
    XA_ENTER_INTERFACE

    if (NULL == domain) {
        result = XA_RESULT_PARAMETER_INVALID;
        XA_LEAVE_INTERFACE;
    }

#ifndef ANDROID
    *domain = XA_DOMAINTYPE_UNKNOWN;
#else
    if (0 == streamIndex) {
        // stream 0 is reserved for the container
        result = XA_RESULT_PARAMETER_INVALID;
        *domain = XA_DOMAINTYPE_UNKNOWN;
    } else {
        IStreamInformation *thiz = (IStreamInformation *) self;

        interface_lock_shared(thiz);

        XAuint32 nbStreams = thiz->mStreamInfoTable.itemAt(0).containerInfo.numStreams;
        // streams in the container are numbered 1..nbStreams
        if (streamIndex <= nbStreams) {
            result = XA_RESULT_SUCCESS;
            *domain = thiz->mStreamInfoTable.itemAt(streamIndex).domain;
        } else {
            SL_LOGE("Querying stream type for stream %d, only %d streams available",
                    streamIndex, nbStreams);
            result = XA_RESULT_PARAMETER_INVALID;
        }

        interface_unlock_shared(thiz);
    }
#endif

    XA_LEAVE_INTERFACE
}


static XAresult IStreamInformation_QueryStreamInformation( XAStreamInformationItf self,
        XAuint32 streamIndex, /* [in] */
        void * info)          /* [out] */
{
    XA_ENTER_INTERFACE

    if (NULL == info) {
        result = XA_RESULT_PARAMETER_INVALID;
    } else {

#ifndef ANDROID
        result = XA_RESULT_FEATURE_UNSUPPORTED;
#else

        IStreamInformation *thiz = (IStreamInformation *) self;

        interface_lock_shared(thiz);

        XAuint32 nbStreams = thiz->mStreamInfoTable.itemAt(0).containerInfo.numStreams;
        // stream 0 is the container, and other streams in the container are numbered 1..nbStreams
        if (streamIndex <= nbStreams) {
            result = XA_RESULT_SUCCESS;
            const StreamInfo& streamInfo = thiz->mStreamInfoTable.itemAt((size_t)streamIndex);

            switch (streamInfo.domain) {
            case XA_DOMAINTYPE_CONTAINER:
                *(XAMediaContainerInformation *)info = streamInfo.containerInfo;
                break;
            case XA_DOMAINTYPE_AUDIO:
                *(XAAudioStreamInformation *)info = streamInfo.audioInfo;
                break;
            case XA_DOMAINTYPE_VIDEO:
                *(XAVideoStreamInformation *)info = streamInfo.videoInfo;
                break;
            case XA_DOMAINTYPE_IMAGE:
                *(XAImageStreamInformation *)info = streamInfo.imageInfo;
                break;
            case XA_DOMAINTYPE_TIMEDTEXT:
                *(XATimedTextStreamInformation *)info = streamInfo.textInfo;
                break;
            case XA_DOMAINTYPE_MIDI:
                *(XAMIDIStreamInformation *)info = streamInfo.midiInfo;
                break;
            case XA_DOMAINTYPE_VENDOR:
                *(XAVendorStreamInformation *)info = streamInfo.vendorInfo;
                break;
            default:
                SL_LOGE("StreamInformation::QueryStreamInformation index %u has "
                        "unknown domain %u", streamIndex, streamInfo.domain);
                result = XA_RESULT_INTERNAL_ERROR;
                break;
            }

        } else {
            SL_LOGE("Querying stream type for stream %d, only %d streams available",
                    streamIndex, nbStreams);
            result = XA_RESULT_PARAMETER_INVALID;
        }

        interface_unlock_shared(thiz);
#endif

    }

    XA_LEAVE_INTERFACE
}


static XAresult IStreamInformation_QueryStreamName( XAStreamInformationItf self,
        XAuint32 streamIndex, /* [in] */
        XAuint16 * pNameSize, /* [in/out] */
        XAchar * pName)       /* [out] */
{
    XA_ENTER_INTERFACE

    if (NULL == pNameSize || streamIndex == 0) {
        result = XA_RESULT_PARAMETER_INVALID;
    } else {
#ifdef ANDROID
        IStreamInformation *thiz = (IStreamInformation *) self;
        interface_lock_shared(thiz);

        XAuint32 nbStreams = thiz->mStreamInfoTable.itemAt(0).containerInfo.numStreams;
        // streams in the container are numbered 1..nbStreams
        if (streamIndex <= nbStreams) {
            char streamName[16];        // large enough for the fixed format in next line
            snprintf(streamName, sizeof(streamName), "stream%u", streamIndex);
            size_t actualNameLength = strlen(streamName);
            if (NULL == pName) {
                // application is querying the name length in order to allocate a buffer
                result = XA_RESULT_SUCCESS;
            } else {
                SLuint16 availableNameLength = *pNameSize;
                if (actualNameLength > availableNameLength) {
                    memcpy(pName, streamName, availableNameLength);
                    result = XA_RESULT_BUFFER_INSUFFICIENT;
                } else if (actualNameLength == availableNameLength) {
                    memcpy(pName, streamName, availableNameLength);
                    result = XA_RESULT_SUCCESS;
                } else { // actualNameLength < availableNameLength
                    memcpy(pName, streamName, actualNameLength + 1);
                    result = XA_RESULT_SUCCESS;
                }
            }
            *pNameSize = actualNameLength;
        } else {
            result = XA_RESULT_PARAMETER_INVALID;
        }

        interface_unlock_shared(thiz);
#else
        SL_LOGE("unsupported XAStreamInformationItf function");
        result = XA_RESULT_FEATURE_UNSUPPORTED;
#endif
    }

    XA_LEAVE_INTERFACE
}


static XAresult IStreamInformation_RegisterStreamChangeCallback( XAStreamInformationItf self,
        xaStreamEventChangeCallback callback, /* [in] */
        void * pContext)                      /* [in] */
{
    XA_ENTER_INTERFACE

    IStreamInformation *thiz = (IStreamInformation *) self;

    interface_lock_exclusive(thiz);

    thiz->mCallback = callback;
    thiz->mContext = pContext;
    result = SL_RESULT_SUCCESS;

    interface_unlock_exclusive(thiz);

    XA_LEAVE_INTERFACE
}


static XAresult IStreamInformation_QueryActiveStreams( XAStreamInformationItf self,
        XAuint32 *numStreams,      /* [in/out] */
        XAboolean *activeStreams)  /* [out] */
{
    XA_ENTER_INTERFACE

    if (NULL == numStreams) {
        result = XA_RESULT_PARAMETER_INVALID;
        XA_LEAVE_INTERFACE;
    }

#ifdef ANDROID
    IStreamInformation *thiz = (IStreamInformation *) self;
    interface_lock_shared(thiz);

    result = XA_RESULT_SUCCESS;
    *numStreams = thiz->mStreamInfoTable.itemAt(0).containerInfo.numStreams;
    activeStreams = thiz->mActiveStreams;

    interface_unlock_shared(thiz);
#else
    result = SL_RESULT_FEATURE_UNSUPPORTED;
#endif

    XA_LEAVE_INTERFACE
}


static XAresult IStreamInformation_SetActiveStream( XAStreamInformationItf self,
        XAuint32   streamNum, /* [in] */
        XAboolean  active,    /* [in] */
        XAboolean  commitNow) /* [in] */
{
    XA_ENTER_INTERFACE

    SL_LOGE("unsupported XAStreamInformationItf function");
    result = XA_RESULT_FEATURE_UNSUPPORTED;

    XA_LEAVE_INTERFACE
}


static const struct XAStreamInformationItf_ IStreamInformation_Itf = {
    IStreamInformation_QueryMediaContainerInformation,
    IStreamInformation_QueryStreamType,
    IStreamInformation_QueryStreamInformation,
    IStreamInformation_QueryStreamName,
    IStreamInformation_RegisterStreamChangeCallback,
    IStreamInformation_QueryActiveStreams,
    IStreamInformation_SetActiveStream
};


void IStreamInformation_init(void *self)
{
    SL_LOGV("IStreamInformation_init\n");
    IStreamInformation *thiz = (IStreamInformation *) self;
    thiz->mItf = &IStreamInformation_Itf;

    thiz->mCallback = NULL;
    thiz->mContext = NULL;

    for (int i=0 ; i < NB_SUPPORTED_STREAMS ; i++) {
        thiz->mActiveStreams[i] = XA_BOOLEAN_FALSE;
    }

#ifdef ANDROID
    // placement new constructor for C++ field within C struct
    (void) new (&thiz->mStreamInfoTable) android::Vector<StreamInfo>();
    // initialize container info
    StreamInfo contInf;
    contInf.domain = XA_DOMAINTYPE_CONTAINER;
    contInf.containerInfo.containerType = XA_CONTAINERTYPE_UNSPECIFIED;
    contInf.containerInfo.mediaDuration = XA_TIME_UNKNOWN;
    // FIXME shouldn't this be 1 ?
    contInf.containerInfo.numStreams = 0;
    // always storing container info at index 0, as per spec: here, the table was still empty
    thiz->mStreamInfoTable.add(contInf);
#endif
}


void IStreamInformation_deinit(void *self) {
#ifdef ANDROID
    IStreamInformation *thiz = (IStreamInformation *) self;
    // explicit destructor
    thiz->mStreamInfoTable.~Vector<StreamInfo>();
#endif
}