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

#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/Utils.h>

#include <media/MediaTrack.h>
#include <media/MediaExtractorPluginApi.h>
#include <media/NdkMediaErrorPriv.h>
#include <media/NdkMediaFormatPriv.h>

namespace android {

MediaTrack::MediaTrack() {}

MediaTrack::~MediaTrack() {}

////////////////////////////////////////////////////////////////////////////////

void MediaTrack::ReadOptions::setNonBlocking() {
    mNonBlocking = true;
}

void MediaTrack::ReadOptions::clearNonBlocking() {
    mNonBlocking = false;
}

bool MediaTrack::ReadOptions::getNonBlocking() const {
    return mNonBlocking;
}

void MediaTrack::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) {
    mOptions |= kSeekTo_Option;
    mSeekTimeUs = time_us;
    mSeekMode = mode;
}

bool MediaTrack::ReadOptions::getSeekTo(
        int64_t *time_us, SeekMode *mode) const {
    *time_us = mSeekTimeUs;
    *mode = mSeekMode;
    return (mOptions & kSeekTo_Option) != 0;
}

/* -------------- unwrapper --------------- */

MediaTrackCUnwrapper::MediaTrackCUnwrapper(CMediaTrack *cmediatrack) {
    wrapper = cmediatrack;
    bufferGroup = nullptr;
}

MediaTrackCUnwrapper *MediaTrackCUnwrapper::create(CMediaTrack *cmediatrack) {
    if (cmediatrack == nullptr) {
        return nullptr;
    }
    return new MediaTrackCUnwrapper(cmediatrack);
}

MediaTrackCUnwrapper::~MediaTrackCUnwrapper() {
    wrapper->free(wrapper->data);
    free(wrapper);
    delete bufferGroup;
}

status_t MediaTrackCUnwrapper::start() {
    if (bufferGroup == nullptr) {
        bufferGroup = new MediaBufferGroup();
    }
    return reverse_translate_error(wrapper->start(wrapper->data, bufferGroup->wrap()));
}

status_t MediaTrackCUnwrapper::stop() {
    return reverse_translate_error(wrapper->stop(wrapper->data));
}

status_t MediaTrackCUnwrapper::getFormat(MetaDataBase& format) {
    sp<AMessage> msg = new AMessage();
    AMediaFormat *tmpFormat =  AMediaFormat_fromMsg(&msg);
    media_status_t ret = wrapper->getFormat(wrapper->data, tmpFormat);
    sp<MetaData> newMeta = new MetaData();
    convertMessageToMetaData(msg, newMeta);
    delete tmpFormat;
    format = *newMeta;
    return reverse_translate_error(ret);
}

status_t MediaTrackCUnwrapper::read(MediaBufferBase **buffer, const ReadOptions *options) {

    uint32_t opts = 0;

    if (options && options->getNonBlocking()) {
        opts |= CMediaTrackReadOptions::NONBLOCKING;
    }

    int64_t seekPosition = 0;
    MediaTrack::ReadOptions::SeekMode seekMode;
    if (options && options->getSeekTo(&seekPosition, &seekMode)) {
        opts |= SEEK;
        opts |= (uint32_t) seekMode;
    }
    CMediaBuffer *buf = nullptr;
    media_status_t ret = wrapper->read(wrapper->data, &buf, opts, seekPosition);
    if (ret == AMEDIA_OK && buf != nullptr) {
        *buffer = (MediaBufferBase*)buf->handle;
        MetaDataBase &meta = (*buffer)->meta_data();
        AMediaFormat *format = buf->meta_data(buf->handle);
        // only convert the keys we're actually expecting, as doing
        // the full convertMessageToMetadata() for every buffer is
        // too expensive
        int64_t val64;
        if (format->mFormat->findInt64("timeUs", &val64)) {
            meta.setInt64(kKeyTime, val64);
        }
        if (format->mFormat->findInt64("durationUs", &val64)) {
            meta.setInt64(kKeyDuration, val64);
        }
        if (format->mFormat->findInt64("target-time", &val64)) {
            meta.setInt64(kKeyTargetTime, val64);
        }
        if (format->mFormat->findInt64("sample-file-offset", &val64)) {
            meta.setInt64(kKeySampleFileOffset, val64);
        }
        if (format->mFormat->findInt64(
                    "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
                    &val64)) {
            meta.setInt64(kKeyLastSampleIndexInChunk, val64);
        }
        int32_t val32;
        if (format->mFormat->findInt32("is-sync-frame", &val32)) {
            meta.setInt32(kKeyIsSyncFrame, val32);
        }
        if (format->mFormat->findInt32("temporal-layer-id", &val32)) {
            meta.setInt32(kKeyTemporalLayerId, val32);
        }
        if (format->mFormat->findInt32("temporal-layer-count", &val32)) {
            meta.setInt32(kKeyTemporalLayerCount, val32);
        }
        if (format->mFormat->findInt32("crypto-default-iv-size", &val32)) {
            meta.setInt32(kKeyCryptoDefaultIVSize, val32);
        }
        if (format->mFormat->findInt32("crypto-mode", &val32)) {
            meta.setInt32(kKeyCryptoMode, val32);
        }
        if (format->mFormat->findInt32("crypto-encrypted-byte-block", &val32)) {
            meta.setInt32(kKeyEncryptedByteBlock, val32);
        }
        if (format->mFormat->findInt32("crypto-skip-byte-block", &val32)) {
            meta.setInt32(kKeySkipByteBlock, val32);
        }
        if (format->mFormat->findInt32("valid-samples", &val32)) {
            meta.setInt32(kKeyValidSamples, val32);
        }
        sp<ABuffer> valbuf;
        if (format->mFormat->findBuffer("crypto-plain-sizes", &valbuf)) {
            meta.setData(kKeyPlainSizes,
                    MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size());
        }
        if (format->mFormat->findBuffer("crypto-encrypted-sizes", &valbuf)) {
            meta.setData(kKeyEncryptedSizes,
                    MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size());
        }
        if (format->mFormat->findBuffer("crypto-key", &valbuf)) {
            meta.setData(kKeyCryptoKey,
                    MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size());
        }
        if (format->mFormat->findBuffer("crypto-iv", &valbuf)) {
            meta.setData(kKeyCryptoIV,
                    MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size());
        }
        if (format->mFormat->findBuffer("sei", &valbuf)) {
            meta.setData(kKeySEI,
                    MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size());
        }
        if (format->mFormat->findBuffer("audio-presentation-info", &valbuf)) {
            meta.setData(kKeyAudioPresentationInfo,
                    MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size());
        }
        if (format->mFormat->findBuffer("csd-0", &valbuf)) {
            meta.setData(kKeyOpaqueCSD0,
                    MetaDataBase::Type::TYPE_NONE, valbuf->data(), valbuf->size());
        }

    } else {
        *buffer = nullptr;
    }

    return reverse_translate_error(ret);
}

bool MediaTrackCUnwrapper::supportNonblockingRead() {
    return wrapper->supportsNonBlockingRead(wrapper->data);
}

}  // namespace android
