/*
 * Copyright (C) 2014-2016 Intel Corporation. All rights reserved.
 *
 * 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 <stdio.h>
#include <assert.h>
#include <string.h>
#include <algorithm>
#include "vaapilayerid.h"
#include "common/log.h"

namespace YamiMediaCodec {

const uint8_t Vp8LayerID::m_vp8TempIds[VP8_MAX_TEMPORAL_LAYER_NUM][VP8_MIN_TEMPORAL_GOP]
    = { { 0, 0, 0, 0 },
        { 0, 1, 0, 1 },
        { 0, 2, 1, 2 } };

const uint8_t AvcLayerID::m_avcTempIds[H264_MAX_TEMPORAL_LAYER_NUM][H264_MIN_TEMPORAL_GOP]
    = { { 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 1, 0, 1, 0, 1, 0, 1 },
        { 0, 2, 1, 2, 0, 2, 1, 2 },
        { 0, 3, 2, 3, 1, 3, 2, 3 } };

TemporalLayerID::TemporalLayerID(const VideoFrameRate& frameRate, const VideoTemporalLayerIDs& layerIDs, const uint8_t* defaultIDs, uint8_t defaultIDsLen)
{
    m_miniRefFrameNum = 0;
    if (layerIDs.numIDs) {
        m_idPeriod = layerIDs.numIDs;
        for (uint32_t i = 0; i < layerIDs.numIDs; i++)
            m_ids.push_back(layerIDs.ids[i]);
    }
    else { //use the default temporal IDs
        assert(defaultIDs && defaultIDsLen > 0);
        m_idPeriod = defaultIDsLen;
        for (uint32_t i = 0; i < m_idPeriod; i++)
            m_ids.push_back(defaultIDs[i]);
    }

    calculateFramerate(frameRate);
}

void TemporalLayerID::getLayerIds(LayerIDs& ids) const
{
    ids = m_ids;
}

uint8_t TemporalLayerID::getTemporalLayer(uint32_t frameNumInGOP) const
{
    return m_ids[frameNumInGOP % m_idPeriod];
}

void TemporalLayerID::getLayerFrameRates(LayerFrameRates& frameRates) const
{
    frameRates = m_frameRates;
    return;
}

void TemporalLayerID::calculateFramerate(const VideoFrameRate& frameRate)
{
    uint8_t numberOfLayerIDs[TEMPORAL_LAYERIDS_LENGTH_MAX];
    LayerIDs tempIDs = m_ids;
    uint8_t i;

    std::sort(tempIDs.begin(), tempIDs.end());
    memset(numberOfLayerIDs, 0, sizeof(numberOfLayerIDs));
    for (i = 0; i < tempIDs.size(); i++) {
        numberOfLayerIDs[tempIDs[i]]++;
    }
    m_layerLen = tempIDs[i - 1] + 1;
    assert(m_layerLen < TEMPORAL_LAYERIDS_LENGTH_MAX);

    VideoFrameRate frameRateTemp;
    uint32_t denom = m_ids.size();
    uint32_t num = 0;
    assert(frameRate.frameRateNum && frameRate.frameRateDenom);
    frameRateTemp.frameRateDenom = frameRate.frameRateDenom * denom;
    for (i = 0; i < m_layerLen; i++) {
        num += numberOfLayerIDs[i];
        frameRateTemp.frameRateNum = num * frameRate.frameRateNum;
        m_frameRates.push_back(frameRateTemp);
    }

    return;
}

uint8_t TemporalLayerID::getMiniRefFrameNum() const
{
    return m_miniRefFrameNum;
}

void TemporalLayerID::checkLayerIDs(uint8_t maxLayerLength) const
{
    LayerIDs tempIDs = m_ids;
    const uint8_t LAYERID0 = 0;
    assert(LAYERID0 == tempIDs[0]);
    if (m_idPeriod > TEMPORAL_LAYERIDS_LENGTH_MAX) {
        ERROR("m_idPeriod(%d) should be in (0, %d]", m_idPeriod, TEMPORAL_LAYERIDS_LENGTH_MAX);
        assert(false);
    }
    //check if the layerID is complete
    std::sort(tempIDs.begin(), tempIDs.end());
    for (uint8_t i = 1; i < m_idPeriod; i++) {
        if (tempIDs[i] - tempIDs[i - 1] > 1) {
            ERROR("layer IDs illegal, no layer: %d.\n", (tempIDs[i - 1] + tempIDs[i]) / 2);
            assert(false);
        }
    }
    if ((m_layerLen > maxLayerLength) || (m_layerLen < 2)) {
        ERROR("m_layerLen(%d) should be in [2, %d]", m_layerLen, maxLayerLength);
        assert(false);
    }
    return;
}

Vp8LayerID::Vp8LayerID(const VideoFrameRate& frameRate, const VideoTemporalLayerIDs& layerIDs, uint8_t layerIndex)
    : TemporalLayerID(frameRate, layerIDs, m_vp8TempIds[layerIndex], VP8_MIN_TEMPORAL_GOP)
{
    checkLayerIDs(VP8_MAX_TEMPORAL_LAYER_NUM);
}

AvcLayerID::AvcLayerID(const VideoFrameRate& frameRate, const VideoTemporalLayerIDs& layerIDs, uint8_t layerIndex)
    : TemporalLayerID(frameRate, layerIDs, m_avcTempIds[layerIndex], H264_MIN_TEMPORAL_GOP)
{
    checkLayerIDs(H264_MAX_TEMPORAL_LAYER_NUM);
    calculateMiniRefNum();
}

void AvcLayerID::calculateMiniRefNum()
{
    uint8_t max = 0;
    const uint8_t LAYER0 = 0;
    //The current frame of layer0 should be in the refList
    uint8_t refFrameNum = 1;
    for (uint8_t i = 0; i < m_idPeriod; i++) {
        if (LAYER0 == m_ids[i]) {
            if (max < refFrameNum)
                max = refFrameNum;
            //The current frame of layer0 should be in the refList
            refFrameNum = 1;
        }
        else {
            refFrameNum++;
        }
    }
    m_miniRefFrameNum = max > refFrameNum ? max : refFrameNum;
}
}
