package tv.danmaku.ijk.media.player;

import android.annotation.TargetApi;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

public class IjkMediaCodecInfo {
    private final static String TAG = "IjkMediaCodecInfo";

    public static final int RANK_MAX = 1000;
    public static final int RANK_TESTED = 800;
    public static final int RANK_ACCEPTABLE = 700;
    public static final int RANK_LAST_CHANCE = 600;
    public static final int RANK_SECURE = 300;
    public static final int RANK_SOFTWARE = 200;
    public static final int RANK_NON_STANDARD = 100;
    public static final int RANK_NO_SENSE = 0;

    public MediaCodecInfo mCodecInfo;
    public int mRank = 0;
    public String mMimeType;

    private static Map<String, Integer> sKnownCodecList;

    private static synchronized Map<String, Integer> getKnownCodecList() {
        if (sKnownCodecList != null)
            return sKnownCodecList;

        sKnownCodecList = new TreeMap<String, Integer>(
                String.CASE_INSENSITIVE_ORDER);

        // ----- Nvidia -----
        // Tegra3
        // Nexus 7 (2012)
        // Tegra K1
        // Nexus 9
        sKnownCodecList.put("OMX.Nvidia.h264.decode", RANK_TESTED);
        sKnownCodecList.put("OMX.Nvidia.h264.decode.secure", RANK_SECURE);

        // ----- Intel -----
        // Atom Z3735
        // Teclast X98 Air
        sKnownCodecList.put("OMX.Intel.hw_vd.h264", RANK_TESTED + 1);
        // Atom Z2560
        // Dell Venue 7 3730
        sKnownCodecList.put("OMX.Intel.VideoDecoder.AVC", RANK_TESTED);

        // ----- Qualcomm -----
        // MSM8260
        // Xiaomi MI 1S
        sKnownCodecList.put("OMX.qcom.video.decoder.avc", RANK_TESTED);
        sKnownCodecList.put("OMX.ittiam.video.decoder.avc", RANK_NO_SENSE);

        // ----- Samsung -----
        // Exynos 3110
        // Nexus S
        sKnownCodecList.put("OMX.SEC.avc.dec", RANK_TESTED);
        sKnownCodecList.put("OMX.SEC.AVC.Decoder", RANK_TESTED - 1);
        // OMX.SEC.avcdec doesn't reorder output pictures on GT-9100
        sKnownCodecList.put("OMX.SEC.avcdec", RANK_TESTED - 2);
        sKnownCodecList.put("OMX.SEC.avc.sw.dec", RANK_SOFTWARE);
        // Exynos 5 ?
        sKnownCodecList.put("OMX.Exynos.avc.dec", RANK_TESTED);
        sKnownCodecList.put("OMX.Exynos.AVC.Decoder", RANK_TESTED - 1);

        // ------ Huawei hisilicon ------
        // Kirin 910, Mali 450 MP
        // Huawei HONOR 3C (H30-L01)
        sKnownCodecList.put("OMX.k3.video.decoder.avc", RANK_TESTED);
        // Kirin 920, Mali T624
        // Huawei HONOR 6
        sKnownCodecList.put("OMX.IMG.MSVDX.Decoder.AVC", RANK_TESTED);

        // ----- TI -----
        // TI OMAP4460
        // Galaxy Nexus
        sKnownCodecList.put("OMX.TI.DUCATI1.VIDEO.DECODER", RANK_TESTED);

        // ------ RockChip ------
        // Youku TVBox
        sKnownCodecList.put("OMX.rk.video_decoder.avc", RANK_TESTED);

        // ------ AMLogic -----
        // MiBox1, 1s, 2
        sKnownCodecList.put("OMX.amlogic.avc.decoder.awesome", RANK_TESTED);

        // ------ Marvell ------
        // Lenovo A788t
        sKnownCodecList.put("OMX.MARVELL.VIDEO.HW.CODA7542DECODER", RANK_TESTED);
        sKnownCodecList.put("OMX.MARVELL.VIDEO.H264DECODER", RANK_SOFTWARE);

        // ----- TODO: need test -----
        sKnownCodecList.remove("OMX.Action.Video.Decoder");
        sKnownCodecList.remove("OMX.allwinner.video.decoder.avc");
        sKnownCodecList.remove("OMX.BRCM.vc4.decoder.avc");
        sKnownCodecList.remove("OMX.brcm.video.h264.hw.decoder");
        sKnownCodecList.remove("OMX.brcm.video.h264.decoder");
        sKnownCodecList.remove("OMX.cosmo.video.decoder.avc");
        sKnownCodecList.remove("OMX.duos.h264.decoder");
        sKnownCodecList.remove("OMX.hantro.81x0.video.decoder");
        sKnownCodecList.remove("OMX.hantro.G1.video.decoder");
        sKnownCodecList.remove("OMX.hisi.video.decoder");
        sKnownCodecList.remove("OMX.LG.decoder.video.avc");
        sKnownCodecList.remove("OMX.MS.AVC.Decoder");
        sKnownCodecList.remove("OMX.RENESAS.VIDEO.DECODER.H264");
        sKnownCodecList.remove("OMX.RTK.video.decoder");
        sKnownCodecList.remove("OMX.sprd.h264.decoder");
        sKnownCodecList.remove("OMX.ST.VFM.H264Dec");
        sKnownCodecList.remove("OMX.vpu.video_decoder.avc");
        sKnownCodecList.remove("OMX.WMT.decoder.avc");

        // Really ?
        sKnownCodecList.remove("OMX.bluestacks.hw.decoder");

        // ---------------
        // Useless codec
        // ----- google -----
        sKnownCodecList.put("OMX.google.h264.decoder", RANK_SOFTWARE);
        sKnownCodecList.put("OMX.google.h264.lc.decoder", RANK_SOFTWARE);
        // ----- huawei k920 -----
        sKnownCodecList.put("OMX.k3.ffmpeg.decoder", RANK_SOFTWARE);
        sKnownCodecList.put("OMX.ffmpeg.video.decoder", RANK_SOFTWARE);
        // ----- unknown -----
        sKnownCodecList.put("OMX.sprd.soft.h264.decoder", RANK_SOFTWARE);

        return sKnownCodecList;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public static IjkMediaCodecInfo setupCandidate(MediaCodecInfo codecInfo,
            String mimeType) {
        if (codecInfo == null
                || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
            return null;

        String name = codecInfo.getName();
        if (TextUtils.isEmpty(name))
            return null;

        name = name.toLowerCase(Locale.US);
        int rank = RANK_NO_SENSE;
        if (!name.startsWith("omx.")) {
            rank = RANK_NON_STANDARD;
        } else if (name.startsWith("omx.pv")) {
            rank = RANK_SOFTWARE;
        } else if (name.startsWith("omx.google.")) {
            rank = RANK_SOFTWARE;
        } else if (name.startsWith("omx.ffmpeg.")) {
            rank = RANK_SOFTWARE;
        } else if (name.startsWith("omx.k3.ffmpeg.")) {
            rank = RANK_SOFTWARE;
        } else if (name.startsWith("omx.avcodec.")) {
            rank = RANK_SOFTWARE;
        } else if (name.startsWith("omx.ittiam.")) {
            // unknown codec in qualcomm SoC
            rank = RANK_NO_SENSE;
        } else if (name.startsWith("omx.mtk.")) {
            // 1. MTK only works on 4.3 and above
            // 2. MTK works on MIUI 6 (4.2.1)
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
                rank = RANK_NO_SENSE;
            else
                rank = RANK_TESTED;
        } else {
            Integer knownRank = getKnownCodecList().get(name);
            if (knownRank != null) {
                rank = knownRank;
            } else {
                try {
                    CodecCapabilities cap = codecInfo
                            .getCapabilitiesForType(mimeType);
                    if (cap != null)
                        rank = RANK_ACCEPTABLE;
                    else
                        rank = RANK_LAST_CHANCE;
                } catch (Throwable e) {
                    rank = RANK_LAST_CHANCE;
                }
            }
        }

        IjkMediaCodecInfo candidate = new IjkMediaCodecInfo();
        candidate.mCodecInfo = codecInfo;
        candidate.mRank = rank;
        candidate.mMimeType = mimeType;
        return candidate;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public void dumpProfileLevels(String mimeType) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
            return;

        try {
            CodecCapabilities caps = mCodecInfo
                    .getCapabilitiesForType(mimeType);
            int maxProfile = 0;
            int maxLevel = 0;
            if (caps != null) {
                if (caps.profileLevels != null) {
                    for (CodecProfileLevel profileLevel : caps.profileLevels) {
                        if (profileLevel == null)
                            continue;

                        maxProfile = Math.max(maxProfile, profileLevel.profile);
                        maxLevel = Math.max(maxLevel, profileLevel.level);
                    }
                }
            }

            Log.i(TAG,
                    String.format(Locale.US, "%s",
                            getProfileLevelName(maxProfile, maxLevel)));
        } catch (Throwable e) {
            Log.i(TAG, "profile-level: exception");
        }
    }

    public static String getProfileLevelName(int profile, int level) {
        return String.format(Locale.US, " %s Profile Level %s (%d,%d)",
                getProfileName(profile), getLevelName(level), profile, level);
    }

    public static String getProfileName(int profile) {
        switch (profile) {
        case CodecProfileLevel.AVCProfileBaseline:
            return "Baseline";
        case CodecProfileLevel.AVCProfileMain:
            return "Main";
        case CodecProfileLevel.AVCProfileExtended:
            return "Extends";
        case CodecProfileLevel.AVCProfileHigh:
            return "High";
        case CodecProfileLevel.AVCProfileHigh10:
            return "High10";
        case CodecProfileLevel.AVCProfileHigh422:
            return "High422";
        case CodecProfileLevel.AVCProfileHigh444:
            return "High444";
        default:
            return "Unknown";
        }
    }

    public static String getLevelName(int level) {
        switch (level) {
        case CodecProfileLevel.AVCLevel1:
            return "1";
        case CodecProfileLevel.AVCLevel1b:
            return "1b";
        case CodecProfileLevel.AVCLevel11:
            return "11";
        case CodecProfileLevel.AVCLevel12:
            return "12";
        case CodecProfileLevel.AVCLevel13:
            return "13";
        case CodecProfileLevel.AVCLevel2:
            return "2";
        case CodecProfileLevel.AVCLevel21:
            return "21";
        case CodecProfileLevel.AVCLevel22:
            return "22";
        case CodecProfileLevel.AVCLevel3:
            return "3";
        case CodecProfileLevel.AVCLevel31:
            return "31";
        case CodecProfileLevel.AVCLevel32:
            return "32";
        case CodecProfileLevel.AVCLevel4:
            return "4";
        case CodecProfileLevel.AVCLevel41:
            return "41";
        case CodecProfileLevel.AVCLevel42:
            return "42";
        case CodecProfileLevel.AVCLevel5:
            return "5";
        case CodecProfileLevel.AVCLevel51:
            return "51";
        case 65536: // CodecProfileLevel.AVCLevel52:
            return "52";
        default:
            return "0";
        }
    }
}
