/*
 * Copyright 2024, 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.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "EncoderCapabilities"

#include <android-base/strings.h>

#include <media/CodecCapabilities.h>
#include <media/EncoderCapabilities.h>
#include <media/stagefright/MediaCodecConstants.h>

namespace android {

const Range<int>& EncoderCapabilities::getQualityRange() {
    return mQualityRange;
}

const Range<int>& EncoderCapabilities::getComplexityRange() {
    return mComplexityRange;
}

// static
int EncoderCapabilities::ParseBitrateMode(std::string mode) {
    for (Feature feat: sBitrateModes) {
        if (base::EqualsIgnoreCase(feat.mName, mode)) {
            return feat.mValue;
        }
    }
    return 0;
}

bool EncoderCapabilities::isBitrateModeSupported(int mode) {
    for (Feature feat : sBitrateModes) {
        if (mode == feat.mValue) {
            return (mBitControl & (1 << mode)) != 0;
        }
    }
    return false;
}

// static
std::shared_ptr<EncoderCapabilities> EncoderCapabilities::Create(std::string mediaType,
        std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) {
    std::shared_ptr<EncoderCapabilities> caps(new EncoderCapabilities());
    caps->init(mediaType, profLevs, format);
    return caps;
}

void EncoderCapabilities::init(std::string mediaType, std::vector<ProfileLevel> profLevs,
        const sp<AMessage> &format) {
    // no support for complexity or quality yet
    mMediaType = mediaType;
    mProfileLevels = profLevs;
    mComplexityRange = Range(0, 0);
    mQualityRange = Range(0, 0);
    mBitControl = (1 << BITRATE_MODE_VBR);

    applyLevelLimits();
    parseFromInfo(format);
}

void EncoderCapabilities::applyLevelLimits() {
    if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) {
        mComplexityRange = Range(0, 8);
        mBitControl = (1 << BITRATE_MODE_CQ);
    } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_NB)
            || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_WB)
            || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_ALAW)
            || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_MLAW)
            || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MSGSM)) {
        mBitControl = (1 << BITRATE_MODE_CBR);
    }
}

void EncoderCapabilities::parseFromInfo(const sp<AMessage> &format) {
    AString complexityRangeAStr;
    if (format->findString("complexity-range", &complexityRangeAStr)) {
        std::optional<Range<int>> complexityRangeOpt
                = Range<int32_t>::Parse(std::string(complexityRangeAStr.c_str()));
        mComplexityRange = complexityRangeOpt.value_or(mComplexityRange);
        // TODO should we limit this to level limits?
    }
    AString qualityRangeAStr;
    if (format->findString("quality-range", &qualityRangeAStr)) {
        std::optional<Range<int>> qualityRangeOpt
                = Range<int32_t>::Parse(std::string(qualityRangeAStr.c_str()));
        mQualityRange = qualityRangeOpt.value_or(mQualityRange);
    }
    AString bitrateModesAStr;
    if (format->findString("feature-bitrate-modes", &bitrateModesAStr)) {
        mBitControl = 0;
        for (std::string mode: base::Split(std::string(bitrateModesAStr.c_str()), ",")) {
            mBitControl |= (1 << ParseBitrateMode(mode));
        }
    }
    format->findInt32("complexity-default", &mDefaultComplexity);
    format->findInt32("quality-default", &mDefaultQuality);
    AString qualityScaleAStr;
    if (format->findString("quality-scale", &qualityScaleAStr)) {
        mQualityScale = std::string(qualityScaleAStr.c_str());
    }
}

bool EncoderCapabilities::supports(
        std::optional<int> complexity, std::optional<int> quality, std::optional<int> profile) {
    bool ok = true;
    if (complexity) {
        ok &= mComplexityRange.contains(complexity.value());
    }
    if (quality) {
        ok &= mQualityRange.contains(quality.value());
    }
    if (profile) {
        ok &= std::any_of(mProfileLevels.begin(), mProfileLevels.end(),
                [&profile](ProfileLevel pl){ return pl.mProfile == profile.value(); });
    }
    return ok;
}

void EncoderCapabilities::getDefaultFormat(sp<AMessage> &format) {
    // don't list trivial quality/complexity as default for now
    if (mQualityRange.upper() != mQualityRange.lower()
            && mDefaultQuality != 0) {
        format->setInt32(KEY_QUALITY, mDefaultQuality);
    }
    if (mComplexityRange.upper() != mComplexityRange.lower()
            && mDefaultComplexity != 0) {
        format->setInt32(KEY_COMPLEXITY, mDefaultComplexity);
    }
    // bitrates are listed in order of preference
    for (Feature feat : sBitrateModes) {
        if ((mBitControl & (1 << feat.mValue)) != 0) {
            format->setInt32(KEY_BITRATE_MODE, feat.mValue);
            break;
        }
    }
}

bool EncoderCapabilities::supportsFormat(const sp<AMessage> &format) {
    int32_t mode;
    if (format->findInt32(KEY_BITRATE_MODE, &mode) && !isBitrateModeSupported(mode)) {
        return false;
    }

    int tmp;
    std::optional<int> complexity = std::nullopt;
    if (format->findInt32(KEY_COMPLEXITY, &tmp)) {
        complexity = tmp;
    }

    if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) {
        int flacComplexity;
        if (format->findInt32(KEY_FLAC_COMPRESSION_LEVEL, &flacComplexity)) {
            if (!complexity) {
                complexity = flacComplexity;
            } else if (flacComplexity != complexity.value()) {
                ALOGE("Conflicting values for complexity and flac-compression-level,"
                        " which are %d and %d", complexity.value(), flacComplexity);
                return false;
            }
        }
    }

    // other audio parameters
    std::optional<int> profile = std::nullopt;
    if (format->findInt32(KEY_PROFILE, &tmp)) {
        profile = tmp;
    }

    if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AAC)) {
        int aacProfile;
        if (format->findInt32(KEY_AAC_PROFILE, &aacProfile)) {
            if (!profile) {
                profile = aacProfile;
            } else if (aacProfile != profile.value()) {
                ALOGE("Conflicting values for profile and aac-profile, which are %d and %d",
                        profile.value(), aacProfile);
                return false;
            }
        }
    }

    std::optional<int> quality = std::nullopt;
    if (format->findInt32(KEY_QUALITY, &tmp)) {
        quality = tmp;
    }

    return supports(complexity, quality, profile);
}

}  // namespace android