#!/usr/bin/env python
## Copyright (c) 2021, Alliance for Open Media. All rights reserved
##
## This source code is subject to the terms of the BSD 3-Clause Clear License and the
## Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear License was
## not distributed with this source code in the LICENSE file, you can obtain it
## at aomedia.org/license/software-license/bsd-3-c-c/.  If the Alliance for Open Media Patent
## License 1.0 was not distributed with this source code in the PATENTS file, you
## can obtain it at aomedia.org/license/patent-license/.
##

__author__ = "maggie.sun@intel.com, ryanlei@meta.com"

import argparse
import os
import subprocess
import sys

import Utils
from CalcQtyWithVmafTool import GetVMAFLogFile
from CalculateQualityMetrics import CalculateQualityMetric, GatherQualityMetrics
from Config import (
    CodecNames,
    EnableMD5,
    EnableParallelGopEncoding,
    EnableTimingInfo,
    GOP_SIZE,
    LoggerName,
    LogLevels,
    Path_RDResults,
    QPs,
    QualityList,
    SUFFIX,
    TEST_CONFIGURATIONS,
    UsePerfUtil,
    WorkPath,
)
from EncDecUpscale import Decode, Encode, GetBitstreamFile
from Utils import (
    Cleanfolder,
    CreateClipList,
    CreateNewSubfolder,
    DeleteFile,
    GatherInstrCycleInfo,
    GatherPerfInfo,
    GatherPerframeStat,
    get_total_frame,
    GetDecLogFile,
    GetEncLogFile,
    GetRDResultCsvFile,
    GetShortContentName,
    GetTestCfgSubfolder,
    md5,
    ParseDecLogFile,
    SetupLogging,
)


###############################################################################
##### Helper Functions ########################################################


def StartJobScript(test_cfg, job_name):
    """Create and open a new shell script file for an individual job"""
    job_file_folder = os.path.join(Path_CmdLog, test_cfg)
    if not os.path.exists(job_file_folder):
        os.makedirs(job_file_folder)
    Utils.CurrentJobFileName = os.path.join(job_file_folder, job_name + ".sh")
    Utils.CurrentJobFile = open(Utils.CurrentJobFileName, "wt")
    Utils.CurrentJobFile.write("#!/bin/bash\n\n")


def WriteToJobScript(cmd):
    """Write a command to the current job shell script"""
    if Utils.CurrentJobFile:
        Utils.CurrentJobFile.write(cmd + "\n")
        Utils.CurrentJobFile.write("wait\n")


def EndJobScript():
    """Close the current job shell script and make it executable"""
    if Utils.CurrentJobFile:
        Utils.CurrentJobFile.close()
        os.chmod(Utils.CurrentJobFileName, 0o755)
        Utils.CurrentJobFile = None
        Utils.CurrentJobFileName = None


def CleanIntermediateFiles():
    folders = [Path_DecodedYuv, Path_CfgFiles]
    for folder in folders:
        Cleanfolder(folder)


def GetBsReconFileName(EncodeMethod, CodecName, EncodePreset, test_cfg, clip, QP):
    basename = GetShortContentName(clip.file_name, False)
    suffix = SUFFIX[CodecName]
    filename = "%s_%s_%s_%s_Preset_%s_QP_%d%s" % (
        basename,
        EncodeMethod,
        CodecName,
        test_cfg,
        EncodePreset,
        QP,
        suffix,
    )
    bs = os.path.join(GetTestCfgSubfolder(Path_Bitstreams, test_cfg), filename)
    filename = "%s_%s_%s_%s_Preset_%s_QP_%d_Decoded.y4m" % (
        basename,
        EncodeMethod,
        CodecName,
        test_cfg,
        EncodePreset,
        QP,
    )
    dec = os.path.join(GetTestCfgSubfolder(Path_DecodedYuv, test_cfg), filename)
    return bs, dec


def setupWorkFolderStructure():
    global Path_Bitstreams, Path_DecodedYuv, Path_QualityLog, Path_TestLog, Path_CfgFiles, Path_TimingLog, Path_EncLog, Path_DecLog, Path_CmdLog, Path_VmafLog
    abs_path = os.path.abspath(WorkPath)
    print(abs_path)
    if os.path.exists(abs_path) == False:
        os.makedirs(abs_path, exist_ok=True)

    Path_Bitstreams = CreateNewSubfolder(WorkPath, "bitstreams")
    Path_DecodedYuv = CreateNewSubfolder(WorkPath, "decodedYUVs")
    Path_QualityLog = CreateNewSubfolder(WorkPath, "qualityLogs")
    Path_TestLog = CreateNewSubfolder(WorkPath, "testLogs")
    Path_CfgFiles = CreateNewSubfolder(WorkPath, "configFiles")
    Path_TimingLog = CreateNewSubfolder(WorkPath, "perfLogs")
    Path_EncLog = CreateNewSubfolder(WorkPath, "encLogs")
    Path_CmdLog = CreateNewSubfolder(WorkPath, "cmdLogs")
    Path_DecLog = CreateNewSubfolder(WorkPath, "decLogs")
    Path_VmafLog = CreateNewSubfolder(WorkPath, "vmafLogs")

    # Create subfolders under cmdLogs for each test configuration
    for test_cfg in TEST_CONFIGURATIONS:
        CreateNewSubfolder(Path_CmdLog, test_cfg)


###############################################################################
######### Major Functions #####################################################
def CleanUp_workfolders():
    folders = [
        Path_Bitstreams,
        Path_DecodedYuv,
        Path_QualityLog,
        Path_TestLog,
        Path_CfgFiles,
        Path_TimingLog,
        Path_EncLog,
        Path_DecLog,
        Path_VmafLog,
    ]
    for folder in folders:
        Cleanfolder(folder)


def Run_Encode_Test(test_cfg, clip, codec, method, preset, LogCmdOnly=False):
    Utils.Logger.info(
        "start running %s encode tests with %s" % (test_cfg, clip.file_name)
    )
    # Get test_cfg-specific paths
    path_bs = GetTestCfgSubfolder(Path_Bitstreams, test_cfg)
    path_dec = GetTestCfgSubfolder(Path_DecodedYuv, test_cfg)
    path_timing = GetTestCfgSubfolder(Path_TimingLog, test_cfg)
    path_enc_log = GetTestCfgSubfolder(Path_EncLog, test_cfg)
    path_dec_log = GetTestCfgSubfolder(Path_DecLog, test_cfg)
    path_quality_log = GetTestCfgSubfolder(Path_QualityLog, test_cfg)
    path_vmaf_log = GetTestCfgSubfolder(Path_VmafLog, test_cfg)

    QPSet = QPs[test_cfg]
    for QP in QPSet:
        Utils.Logger.info("start encode with QP %d" % (QP))
        # encode
        total_frame = get_total_frame(test_cfg, clip)
        JobName = "%s_%s_%s_%s_Preset_%s_QP_%d" % (
            GetShortContentName(clip.file_name, False),
            method,
            codec,
            test_cfg,
            preset,
            QP,
        )
        if LogCmdOnly:
            Utils.CmdLogger.write(
                "============== %s Job Start =================\n" % JobName
            )
            # Start individual shell script for this job
            StartJobScript(test_cfg, JobName)
        bsFile = Encode(
            method,
            codec,
            preset,
            clip,
            test_cfg,
            QP,
            total_frame,
            path_bs,
            path_timing,
            path_enc_log,
            0,
            LogCmdOnly,
        )
        Utils.Logger.info("start decode file %s" % os.path.basename(bsFile))
        # decode
        decodedYUV = Decode(
            clip,
            method,
            test_cfg,
            codec,
            bsFile,
            path_dec,
            path_timing,
            False,
            path_dec_log,
            LogCmdOnly,
        )
        # calculate quality distortion
        Utils.Logger.info("start quality metric calculation")
        CalculateQualityMetric(
            clip.file_path,
            total_frame,
            decodedYUV,
            clip.fmt,
            clip.width,
            clip.height,
            clip.bit_depth,
            path_quality_log,
            path_vmaf_log,
            LogCmdOnly,
        )
        if SaveMemory:
            DeleteFile(decodedYUV, LogCmdOnly)
        Utils.Logger.info("finish running encode with QP %d" % (QP))
        if LogCmdOnly:
            Utils.CmdLogger.write(
                "============== %s Job End ===================\n\n" % JobName
            )
            # End individual shell script for this job
            EndJobScript()


def Run_Parallel_Encode_Test(test_cfg, clip, codec, method, preset, LogCmdOnly=False):
    if EnableParallelGopEncoding:
        Utils.Logger.info(
            "start running %s encode tests with %s" % (test_cfg, clip.file_name)
        )
        # Get test_cfg-specific paths
        path_bs = GetTestCfgSubfolder(Path_Bitstreams, test_cfg)
        path_dec = GetTestCfgSubfolder(Path_DecodedYuv, test_cfg)
        path_timing = GetTestCfgSubfolder(Path_TimingLog, test_cfg)
        path_enc_log = GetTestCfgSubfolder(Path_EncLog, test_cfg)
        path_dec_log = GetTestCfgSubfolder(Path_DecLog, test_cfg)

        QPSet = QPs[test_cfg]
        for QP in QPSet:
            Utils.Logger.info("start encode with QP %d" % (QP))
            total_frame = get_total_frame(test_cfg, clip)

            # Encode
            start_frame = 0
            while start_frame < total_frame:
                # encode
                num_frames = GOP_SIZE
                if (start_frame + num_frames) > total_frame:
                    num_frames = total_frame - start_frame

                JobName = "%s_%s_%s_%s_Preset_%s_QP_%d_start_%d_frames_%d" % (
                    GetShortContentName(clip.file_name, False),
                    method,
                    codec,
                    test_cfg,
                    preset,
                    QP,
                    start_frame,
                    num_frames,
                )
                if LogCmdOnly:
                    Utils.CmdLogger.write(
                        "============== %s Job Start =================\n" % JobName
                    )
                    # Start individual shell script for this job
                    StartJobScript(test_cfg, JobName)

                bsFile = Encode(
                    method,
                    codec,
                    preset,
                    clip,
                    test_cfg,
                    QP,
                    num_frames,
                    path_bs,
                    path_timing,
                    path_enc_log,
                    start_frame,
                    LogCmdOnly,
                )

                decodedYUV = Decode(
                    clip,
                    method,
                    test_cfg,
                    codec,
                    bsFile,
                    path_dec,
                    path_timing,
                    False,
                    path_dec_log,
                    LogCmdOnly,
                )
                if SaveMemory:
                    DeleteFile(decodedYUV, LogCmdOnly)

                start_frame += num_frames

                if LogCmdOnly:
                    Utils.CmdLogger.write(
                        "============== %s Job End ===================\n\n" % JobName
                    )
                    # End individual shell script for this job
                    EndJobScript()
    else:
        Utils.Logger.error(
            "parallel encode function can only be used with Parallel Gop Encoding mode"
        )


def Run_Decode_Test(test_cfg, clip, codec, method, preset, LogCmdOnly=False):
    if EnableParallelGopEncoding:
        Utils.Logger.info(
            "start running %s decode tests with %s" % (test_cfg, clip.file_name)
        )
        # Get test_cfg-specific paths
        path_bs = GetTestCfgSubfolder(Path_Bitstreams, test_cfg)
        path_dec = GetTestCfgSubfolder(Path_DecodedYuv, test_cfg)
        path_timing = GetTestCfgSubfolder(Path_TimingLog, test_cfg)
        path_dec_log = GetTestCfgSubfolder(Path_DecLog, test_cfg)
        path_quality_log = GetTestCfgSubfolder(Path_QualityLog, test_cfg)
        path_vmaf_log = GetTestCfgSubfolder(Path_VmafLog, test_cfg)

        QPSet = QPs[test_cfg]

        for QP in QPSet:
            Utils.Logger.info("start decode  with QP %d" % (QP))
            total_frame = get_total_frame(test_cfg, clip.file_name)

            # decode
            JobName = "%s_%s_%s_%s_Preset_%s_QP_%d" % (
                GetShortContentName(clip.file_name, False),
                method,
                codec,
                test_cfg,
                preset,
                QP,
            )
            if LogCmdOnly:
                Utils.CmdLogger.write(
                    "============== %s Job Start =================\n" % JobName
                )
                # Start individual shell script for this job
                StartJobScript(test_cfg, JobName)

            bsfile = "%s/%s_%s_%s_%s_Preset_%s_QP_%d.obu" % (
                path_bs,
                GetShortContentName(clip.file_name, False),
                method,
                codec,
                test_cfg,
                preset,
                QP,
            )

            decodedYUV = Decode(
                clip,
                method,
                test_cfg,
                codec,
                bsfile,
                path_dec,
                path_timing,
                False,
                path_dec_log,
                LogCmdOnly,
            )
            # calculate quality distortion
            Utils.Logger.info("start quality metric calculation")
            CalculateQualityMetric(
                clip.file_path,
                total_frame,
                decodedYUV,
                clip.fmt,
                clip.width,
                clip.height,
                clip.bit_depth,
                path_quality_log,
                path_vmaf_log,
                LogCmdOnly,
            )
            if SaveMemory:
                DeleteFile(decodedYUV, LogCmdOnly)
            Utils.Logger.info("finish running encode with QP %d" % (QP))
            if LogCmdOnly:
                Utils.CmdLogger.write(
                    "============== %s Job End ===================\n\n" % JobName
                )
                # End individual shell script for this job
                EndJobScript()
    else:
        Utils.Logger.error(
            "decode function can only be used with Parallel Gop Encoding mode"
        )


def Run_Concatenate_Test(test_cfg, clip, codec, method, preset, LogCmdOnly=False):
    if EnableParallelGopEncoding:
        Utils.Logger.info(
            "start running %s concatenate tests with %s" % (test_cfg, clip.file_name)
        )
        # Get test_cfg-specific path
        path_bs = GetTestCfgSubfolder(Path_Bitstreams, test_cfg)

        QPSet = QPs[test_cfg]

        for QP in QPSet:
            Utils.Logger.info("start encode with QP %d" % (QP))
            total_frame = get_total_frame(test_cfg, clip.file_name)

            start_frame = 0
            JobName = "%s_%s_%s_%s_Preset_%s_QP_%d" % (
                GetShortContentName(clip.file_name, False),
                method,
                codec,
                test_cfg,
                preset,
                QP,
            )
            cmd = "cat "
            while start_frame < total_frame:
                num_frames = GOP_SIZE
                if (start_frame + num_frames) > total_frame:
                    num_frames = total_frame - start_frame

                bsfile = GetBitstreamFile(
                    method,
                    codec,
                    test_cfg,
                    preset,
                    clip.file_path,
                    QP,
                    start_frame,
                    num_frames,
                    path_bs,
                )
                cmd += " %s " % bsfile
                start_frame += num_frames

            bsfile = "%s_%s_%s_%s_Preset_%s_QP_%d.obu" % (
                GetShortContentName(clip.file_name, False),
                method,
                codec,
                test_cfg,
                preset,
                QP,
            )

            cmd += " > %s/%s" % (path_bs, bsfile)

            if LogCmdOnly:
                Utils.CmdLogger.write("%s\n" % cmd)
            else:
                subprocess.call(cmd, shell=True)


def GenerateSummaryRDDataFile(
    EncodeMethod, CodecName, EncodePreset, test_cfg, clip_list, log_path, missing
):
    Utils.Logger.info("start saving RD results to excel file.......")
    if not os.path.exists(Path_RDResults):
        os.makedirs(Path_RDResults)

    # Get test_cfg-specific paths
    path_bs = GetTestCfgSubfolder(Path_Bitstreams, test_cfg)
    path_dec = GetTestCfgSubfolder(Path_DecodedYuv, test_cfg)
    path_dec_log = GetTestCfgSubfolder(Path_DecLog, test_cfg)
    path_quality_log = GetTestCfgSubfolder(Path_QualityLog, test_cfg)
    path_timing = GetTestCfgSubfolder(Path_TimingLog, test_cfg)
    path_enc_log = GetTestCfgSubfolder(Path_EncLog, test_cfg)

    csv_file, perframe_csvfile = GetRDResultCsvFile(
        EncodeMethod, CodecName, EncodePreset, test_cfg
    )
    csv = open(csv_file, "wt")
    # "TestCfg,EncodeMethod,CodecName,EncodePreset,Class,OrigRes,Name,FPS,BitDepth,CodedRes,QP,Bitrate(kbps)")
    csv.write(
        "TestCfg,EncodeMethod,CodecName,EncodePreset,Class,Name,OrigRes,FPS,"
        "BitDepth,CodedRes,QP,"
    )
    csv.write("Bitrate(kbps)")
    for qty in QualityList:
        csv.write("," + qty)
    csv.write(",EncT[s],DecT[s]")
    if UsePerfUtil:
        csv.write(",EncInstr,DecInstr,EncCycles,DecCycles")
    if EnableMD5:
        csv.write(",EncMD5,DecMD5")
    csv.write("\n")

    perframe_csv = open(perframe_csvfile, "wt")

    perframe_csv.write(
        "TestCfg,EncodeMethod,CodecName,EncodePreset,Class,Name,Res,FPS,"
        "BitDepth,QP,POC,FrameType,Level,qindex,FrameSize"
    )
    for qty in QualityList:
        if (
            qty != "Overall_PSNR"
            and qty != "Overall_APSNR"
            and not qty.startswith("APSNR")
        ):
            perframe_csv.write("," + qty)
    perframe_csv.write("\n")

    QPSet = QPs[test_cfg]
    for clip in clip_list:
        total_frame = get_total_frame(test_cfg, clip.file_name)
        for qp in QPSet:
            JobName = "%s_%s_%s_%s_Preset_%s_QP_%d" % (
                GetShortContentName(clip.file_name, False),
                EncodeMethod,
                CodecName,
                test_cfg,
                EncodePreset,
                qp,
            )

            bs, dec = GetBsReconFileName(
                EncodeMethod, CodecName, EncodePreset, test_cfg, clip, qp
            )
            if not os.path.exists(bs):
                print(f"'{JobName}',")
                missing.write(f"'{JobName}',\n")
                continue

            dec_log = GetDecLogFile(bs, path_dec_log)
            if not os.path.exists(dec_log):
                print(f"'{JobName}',")
                missing.write(f"'{JobName}',\n")
                continue

            num_dec_frames = ParseDecLogFile(dec_log)
            if num_dec_frames == 0:
                print(f"'{JobName}',")
                missing.write(f"'{JobName}',\n")
                continue

            vmaf_log = GetVMAFLogFile(dec, path_quality_log)
            if not os.path.exists(vmaf_log):
                print(f"'{JobName}',")
                missing.write(f"'{JobName}',\n")
                continue

            quality, perframe_vmaf_log, frame_num = GatherQualityMetrics(
                dec, path_quality_log
            )
            if not quality:
                print(f"'{JobName}',")
                missing.write(f"'{JobName}',\n")
                continue

            if frame_num < total_frame:
                print(f"'{JobName}',")
                missing.write(f"'{JobName}',\n")
                continue

            # print("%s Frame number = %d"%(bs, frame_num))
            filesize = os.path.getsize(bs)
            bitrate = round(
                (filesize * 8 * (clip.fps_num / clip.fps_denom) / frame_num) / 1000.0, 6
            )

            csv.write(
                "%s,%s,%s,%s,%s,%s,%s,%.2f,%d,%s,%d,"
                % (
                    test_cfg,
                    EncodeMethod,
                    CodecName,
                    EncodePreset,
                    clip.file_class,
                    clip.file_name,
                    str(clip.width) + "x" + str(clip.height),
                    clip.fps,
                    clip.bit_depth,
                    str(clip.width) + "x" + str(clip.height),
                    qp,
                )
            )
            if test_cfg == "STILL":
                csv.write("%d" % filesize)
            else:
                csv.write("%f" % bitrate)

            for qty in quality:
                csv.write(",%f" % qty)

            if UsePerfUtil:
                if test_cfg == "RA" and EnableParallelGopEncoding:
                    csv.write(",,,,,,,")
                else:
                    enc_time, dec_time, enc_instr, dec_instr, enc_cycles, dec_cycles = (
                        GatherInstrCycleInfo(bs, path_timing)
                    )
                    csv.write(
                        ",%.2f,%.2f,%s,%s,%s,%s,"
                        % (
                            enc_time,
                            dec_time,
                            enc_instr,
                            dec_instr,
                            enc_cycles,
                            dec_cycles,
                        )
                    )
            elif EnableTimingInfo:
                if test_cfg == "RA" and EnableParallelGopEncoding:
                    csv.write(",,,")
                else:
                    enc_time, dec_time = GatherPerfInfo(bs, path_timing)
                    csv.write(",%.2f,%.2f," % (enc_time, dec_time))
            else:
                csv.write(",,,")

            if EnableMD5:
                enc_md5 = md5(bs)
                dec_md5 = md5(dec)
                csv.write("%s,%s" % (enc_md5, dec_md5))

            csv.write("\n")

            if EncodeMethod == "aom":
                if test_cfg != "RA" or not EnableParallelGopEncoding:
                    enc_log = GetEncLogFile(bs, path_enc_log)
                    GatherPerframeStat(
                        test_cfg,
                        EncodeMethod,
                        CodecName,
                        EncodePreset,
                        clip,
                        clip.file_name,
                        clip.width,
                        clip.height,
                        qp,
                        enc_log,
                        perframe_csv,
                        perframe_vmaf_log,
                    )
    csv.close()
    perframe_csv.close()
    Utils.Logger.info("finish export RD results to file.")
    return


def ParseArguments(raw_args):
    parser = argparse.ArgumentParser(
        prog="AV2CTCTestTest.py", usage="%(prog)s [options]", description=""
    )
    parser.add_argument(
        "-f",
        "--function",
        dest="Function",
        type=str,
        required=True,
        metavar="",
        choices=["clean", "encode", "summary", "concatenate", "decode"],
        help="function to run: clean, encode, summary, concatenate, decode",
    )
    parser.add_argument(
        "-s",
        "--SaveMemory",
        dest="SaveMemory",
        type=bool,
        default=True,
        metavar="",
        help="save memory mode will delete most files in"
        " intermediate steps and keeps only necessary "
        "ones for RD calculation. It is false by default",
    )
    parser.add_argument(
        "-CmdOnly",
        "--LogCmdOnly",
        dest="LogCmdOnly",
        type=bool,
        default=True,
        metavar="",
        help="LogCmdOnly mode will only capture the command sequences"
        "It is true by default",
    )
    parser.add_argument(
        "-l",
        "--LoggingLevel",
        dest="LogLevel",
        type=int,
        default=3,
        choices=range(len(LogLevels)),
        metavar="",
        help="logging level: 0:No Logging, 1: Critical, 2: Error,"
        " 3: Warning, 4: Info, 5: Debug",
    )
    parser.add_argument(
        "-p",
        "--EncodePreset",
        dest="EncodePreset",
        type=str,
        metavar="",
        help="EncodePreset: 0,1,2... for aom",
    )
    parser.add_argument(
        "-c",
        "--CodecName",
        dest="CodecName",
        type=str,
        default="av2",
        choices=CodecNames,
        metavar="",
        help="CodecName: av1, av2, hevc",
    )
    parser.add_argument(
        "-m",
        "--EncodeMethod",
        dest="EncodeMethod",
        type=str,
        metavar="",
        help="EncodeMethod: aom, svt for av1, hm for hevc",
    )
    if len(raw_args) == 1:
        parser.print_help()
        sys.exit(1)
    args = parser.parse_args(raw_args[1:])

    global Function, SaveMemory, LogLevel, EncodePreset, CodecName, EncodeMethod, LogCmdOnly
    Function = args.Function
    SaveMemory = args.SaveMemory
    LogLevel = args.LogLevel
    EncodePreset = args.EncodePreset
    CodecName = args.CodecName
    EncodeMethod = args.EncodeMethod
    LogCmdOnly = args.LogCmdOnly


######################################
# main
######################################
if __name__ == "__main__":
    # sys.argv = ["", "-f", "encode", "-c", "av2", "-m", "aom", "-p", "6", "--LogCmdOnly", "1"]
    # sys.argv = ["", "-f", "summary", "-c", "av2", "-m", "aom", "-p", "6"]
    # sys.argv = ["", "-f", "encode", "-c", "hevc", "-m", "hm", "-p", "0"] #, "--LogCmdOnly", "1"]
    # sys.argv = ["", "-f", "encode", "-c", "av1", "-m", "aom", "-p", "0"]  # , "--LogCmdOnly", "1"]
    ParseArguments(sys.argv)

    # preparation for executing functions
    setupWorkFolderStructure()
    if Function not in ["clean"]:
        # Command log txt file goes to WorkPath folder
        SetupLogging(LogLevel, LogCmdOnly, LoggerName, WorkPath, Path_TestLog)

    # execute functions
    if Function == "clean":
        CleanUp_workfolders()
    elif Function == "encode":
        for test_cfg in TEST_CONFIGURATIONS:
            clip_list = CreateClipList(test_cfg)
            for clip in clip_list:
                total_frame = get_total_frame(test_cfg, clip)

                if test_cfg == "RA" and total_frame <= GOP_SIZE:
                    EnableParallelGopEncoding = False

                if test_cfg == "RA" and EnableParallelGopEncoding:
                    Run_Parallel_Encode_Test(
                        test_cfg,
                        clip,
                        CodecName,
                        EncodeMethod,
                        EncodePreset,
                        LogCmdOnly,
                    )
                else:
                    Run_Encode_Test(
                        test_cfg,
                        clip,
                        CodecName,
                        EncodeMethod,
                        EncodePreset,
                        LogCmdOnly,
                    )
        if SaveMemory:
            Cleanfolder(Path_DecodedYuv)
    elif Function == "concatenate":
        test_cfg = "RA"
        clip_list = CreateClipList(test_cfg)
        for clip in clip_list:
            if EnableParallelGopEncoding:
                Run_Concatenate_Test(
                    test_cfg, clip, CodecName, EncodeMethod, EncodePreset, LogCmdOnly
                )
            else:
                Utils.Logger.error(
                    "concatenate function can only be used with Parallel Gop Encoding mode and RA configuration"
                )
    elif Function == "decode":
        test_cfg = "RA"
        clip_list = CreateClipList(test_cfg)
        for clip in clip_list:
            if EnableParallelGopEncoding:
                Run_Decode_Test(
                    test_cfg, clip, CodecName, EncodeMethod, EncodePreset, LogCmdOnly
                )
            else:
                Utils.Logger.error(
                    "decode function can only be used with Parallel Gop Encoding mode and RA configuration"
                )
    elif Function == "summary":
        Missing = open("Missing.log", "wt")
        for test_cfg in TEST_CONFIGURATIONS:
            clip_list = CreateClipList(test_cfg)
            GenerateSummaryRDDataFile(
                EncodeMethod,
                CodecName,
                EncodePreset,
                test_cfg,
                clip_list,
                Path_EncLog,
                Missing,
            )
        Missing.close()
        Utils.Logger.info("RD data summary file generated")
    else:
        Utils.Logger.error("invalid parameter value of Function")
