// BWF MetaEdit CLI - A Command Line Interface for BWF MetaEdit
//
// This code was created in 2010 for the Library of Congress and the
// other federal government agencies participating in the Federal Agencies
// Digital Guidelines Initiative and it is in the public domain.
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
#include <iostream>
#include <string>
#include <vector>
#include <cctype>
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "CLI/CommandLine_Parser.h"
#include "CLI/CLI_Help.h"
#include "Common/Common_About.h"
#if defined(ENABLE_C2PA)
    #include "ZenLib/File.h"
    #if defined(C2PA_DYNAMIC_LOADING)
        #include "Riff/Riff_C2PA_Helpers.h"
    #endif // defined(ENABLE_C2PA)
#endif // defined(ENABLE_C2PA)
#include "ZenLib/ZtringList.h"
using namespace ZenLib;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
ZtringList In_Core_File_List;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#define UNUSED_PARAMETER(ARG) (void)ARG;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
//Parse Command Line
#define OPTION(_TEXT, _TOLAUNCH) \
    else if (Argument.find(_TEXT)==0)        return CL_##_TOLAUNCH(C, Argument); \
//---------------------------------------------------------------------------

//***************************************************************************
// Helpers
//***************************************************************************

//---------------------------------------------------------------------------
#if defined(ENABLE_C2PA)
static bool ReadWholeFile(const string &FileName, string &Content)
{
    File F;
    if (!F.Open(Ztring().From_UTF8(FileName)))
        return false;

    int64u F_Size=F.Size_Get();
    if (F_Size>((size_t)-1)-1)
        return false;

    int8u* Buffer=new int8u[(size_t)F_Size];
    size_t Buffer_Offset=0;
    while (Buffer_Offset<F_Size)
    {
        size_t BytesRead=F.Read(Buffer+Buffer_Offset, (size_t)F_Size-Buffer_Offset);
        if (BytesRead==0)
            break;
        Buffer_Offset+=BytesRead;
    }

    Content.assign((const char*)Buffer, Buffer_Offset);
    delete[] Buffer;

    return Buffer_Offset==F_Size;
}
#endif // defined(ENABLE_C2PA)

//***************************************************************************
// Main
//***************************************************************************

int Parse(Core &C, string &Argument)
{
    if (0);
    OPTION("--help",                                        Help)
    OPTION("-h",                                            Help)
    OPTION("--version",                                     Version)

    OPTION("--reject-riff2rf64",                            riff2rf64_Reject)
    OPTION("--reject-overwrite",                            Overwrite_Reject)
    OPTION("--reject-c2pa",                                 C2PA_Reject)
    OPTION("--accept-nopadding",                            NoPadding_Accept)
    OPTION("--continue-errors",                             Errors_Continue)

    OPTION("--append",                                      Append)
    OPTION("-a",                                            Append)

    OPTION("--verbose",                                     Log_cout)
    OPTION("-v",                                            Log_cout)

    OPTION("--simulate",                                    Simulate)
    OPTION("-s",                                            Simulate)

    OPTION("--in-cset-remove",                              In_CSET_Remove)
    OPTION("--specialchars",                                SpecialChars)
    OPTION("--ignore-file-encoding",                        Ignore_File_Encoding)
    OPTION("--encoding=",                                   Encoding)
    OPTION("--fallback-encoding=",                          Fallback_Encoding)
    OPTION("--write-encoding=",                             Write_Encoding)
    OPTION("--write-encoding",                              Write_CodePage)
    OPTION("--revert-to-riff",                              RevertToRiff)

    OPTION("--out-xml=",                                    Out_XML_File)
    OPTION("--out-xml",                                     Out_XML_cout)

    OPTION("--out-tech-xml=",                               Out_Tech_XML_File)
    OPTION("--out-tech-xml",                                Out_Tech_XML)
    OPTION("--out-tech=",                                   Out_Tech_File)
    OPTION("--out-tech",                                    Out_Tech_cout)

    OPTION("--in-core=",                                    In_Core_File)
    OPTION("--in-core-remove",                              In_Core_Remove)
//    OPTION("--in-core-xml",                                 In_Core_XML)
    OPTION("--out-core-xml=",                               Out_Core_XML_File)
    OPTION("--out-core-xml",                                Out_Core_XML)
    OPTION("--out-core=",                                   Out_Core_File)
    OPTION("--out-core",                                    Out_Core_cout)

    OPTION("--in-xmp=",                                     In__PMX_File)
    OPTION("--in-xmp-remove",                               In__PMX_Remove)
    OPTION("--in-xmp-xml",                                  In__PMX_XML)
    OPTION("--out-xmp-xml",                                 Out__PMX_XML)
    OPTION("--out-xmp=",                                    Out__PMX_File)
    OPTION("--out-xmp",                                     Out__PMX_cout)

    OPTION("--in-axml=",                                    In_aXML_File)
    OPTION("--in-axml-remove",                              In_aXML_Remove)
    OPTION("--in-axml-xml",                                 In_aXML_XML)
    OPTION("--out-axml-xml",                                Out_aXML_XML)
    OPTION("--out-axml=",                                   Out_aXML_File)
    OPTION("--out-axml",                                    Out_aXML_cout)

    OPTION("--in-ixml=",                                    In_iXML_File)
    OPTION("--in-ixml-remove",                              In_iXML_Remove)
    OPTION("--in-ixml-xml",                                 In_iXML_XML)
    OPTION("--out-ixml-xml",                                Out_iXML_XML)
    OPTION("--out-ixml=",                                   Out_iXML_File)
    OPTION("--out-ixml",                                    Out_iXML_cout)

    OPTION("--in-cue=",                                     In_cue__File)
    OPTION("--in-cue-remove",                               In_cue__Remove)
    OPTION("--in-cue-xml",                                  In_cue__XML)
    OPTION("--out-cue=",                                    Out_cue__File)
    OPTION("--out-cue-xml",                                 Out_cue__XML)
    OPTION("--out-cue",                                     Out_cue__cout)

    OPTION("--md5-generate",                                MD5_Generate)
    OPTION("--md5-verify",                                  MD5_Verify)
    OPTION("--md5-embed-overwrite",                         MD5_Embed_Overwrite)
    OPTION("--md5-embed",                                   MD5_Embed)

    OPTION("--remove-chunks=",                              Chunks_Remove)
    #if defined(ENABLE_C2PA)
    OPTION("--out-c2pa-json",                               Out_C2PA_JSON)
    OPTION("--out-c2pa=",                                   Out_C2PA_File)
    OPTION("--out-c2pa",                                    Out_C2PA_cout)
    OPTION("--c2pa-verify",                                 C2PA_Verify)
    OPTION("--c2pa-sign-manifest=",                         C2PA_Sign_Manifest)
    OPTION("--c2pa-sign-certificate=",                      C2PA_Sign_Certificate)
    OPTION("--c2pa-sign-key=",                              C2PA_Sign_Key)
    OPTION("--c2pa-sign-algorithm=",                        C2PA_Sign_Algorithm)
    OPTION("--c2pa-sign-ta-url=",                           C2PA_Sign_TA_URL)
    #endif // defined(ENABLE_C2PA)
    //Default
    OPTION("--",                                            Default)
    OPTION("-",                                             Default)

    return -1; //This is a file
}

//***************************************************************************
// Options - General
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(Help)
{
    CL_Version(C, Argument);
    std::cout<<Help();

    return 0;
}

//---------------------------------------------------------------------------
CL_OPTION(Version)
{
    UNUSED_PARAMETER(C);
    UNUSED_PARAMETER(Argument);

    std::cout<<NameVersion_Text()<<std::endl;

    return 0;
}

//***************************************************************************
// Options - Configuration
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(riff2rf64_Reject)
{
    UNUSED_PARAMETER(Argument);

    C.riff2rf64_Reject=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Overwrite_Reject)
{
    UNUSED_PARAMETER(Argument);

    C.Overwrite_Reject=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(C2PA_Reject)
{
    UNUSED_PARAMETER(Argument);

    C.C2PA_Reject=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(NoPadding_Accept)
{
    UNUSED_PARAMETER(Argument);

    C.NoPadding_Accept=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Errors_Continue)
{
    UNUSED_PARAMETER(Argument);

    C.Errors_Continue=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Append)
{
    UNUSED_PARAMETER(Argument);

    C.NewChunksAtTheEnd=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Simulate)
{
    UNUSED_PARAMETER(Argument);

    C.Simulation_Enabled=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Encoding)
{
    std::string Value=Argument.substr(11);
    if (Value.size()==5 && std::tolower(Value[0])=='u' && std::tolower(Value[1])=='t' && std::tolower(Value[2])=='f' && Value[3]=='-' && Value[4]=='8')
        C.Encoding=Encoding_UTF8;
    else if (Value.size()==5 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='4' && Value[3]=='3' && Value[4]=='7')
        C.Encoding=Encoding_CP437;
    else if (Value.size()==5 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='8' && Value[3]=='5' && Value[4]=='0')
        C.Encoding=Encoding_CP850;
    else if (Value.size()==5 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='8' && Value[3]=='5' && Value[4]=='8')
        C.Encoding=Encoding_CP858;
    else if (Value.size()==6 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='1' && Value[3]=='2' && Value[4]=='5' && Value[5]=='2')
        C.Encoding=Encoding_CP1252;
    else if (Value=="8859-1")
        C.Encoding=Encoding_8859_1;
    else if (Value=="8859-2")
        C.Encoding=Encoding_8859_2;
    else if (Value.size()==5 && std::tolower(Value[0])=='l' && std::tolower(Value[1])=='o' && std::tolower(Value[2])=='c' && std::tolower(Value[3])=='a' && std::tolower(Value[4])=='l')
        C.Encoding=Encoding_Local;
    else
    {
        std::cout<<Value<<" unknown encoding"<<std::endl;
        return 0;
    }

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Fallback_Encoding)
{
    std::string Value=Argument.substr(20);
    if (Value.size()==5 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='4' && Value[3]=='3' && Value[4]=='7')
        C.Fallback_Encoding=Encoding_CP437;
    else if (Value.size()==5 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='8' && Value[3]=='5' && Value[4]=='0')
        C.Fallback_Encoding=Encoding_CP850;
    else if (Value.size()==5 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='8' && Value[3]=='5' && Value[4]=='8')
        C.Fallback_Encoding=Encoding_CP858;
    else if (Value.size()==6 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='1' && Value[3]=='2' && Value[4]=='5' && Value[5]=='2')
        C.Fallback_Encoding=Encoding_CP1252;
    else if (Value=="8859-1")
        C.Fallback_Encoding=Encoding_8859_1;
    else if (Value=="8859-2")
        C.Fallback_Encoding=Encoding_8859_2;
    else
    {
        std::cout<<Value<<" unknown encoding"<<std::endl;
        return 0;
    }

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Write_Encoding)
{
    std::string Value=Argument.substr(17);
    if (Value.size()==5 && std::tolower(Value[0])=='u' && std::tolower(Value[1])=='t' && std::tolower(Value[2])=='f' && Value[3]=='-' && Value[4]=='8')
        C.Write_Encoding=Encoding_UTF8;
    else if (Value.size()==5 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='4' && Value[3]=='3' && Value[4]=='7')
        C.Write_Encoding=Encoding_CP437;
    else if (Value.size()==5 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='8' && Value[3]=='5' && Value[4]=='0')
        C.Write_Encoding=Encoding_CP850;
    else if (Value.size()==5 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='8' && Value[3]=='5' && Value[4]=='8')
        C.Write_Encoding=Encoding_CP858;
    else if (Value.size()==6 && std::tolower(Value[0])=='c' && std::tolower(Value[1])=='p' && Value[2]=='1' && Value[3]=='2' && Value[4]=='5' && Value[5]=='2')
        C.Write_Encoding=Encoding_CP1252;
    else if (Value=="8859-1")
        C.Write_Encoding=Encoding_8859_1;
    else if (Value=="8859-2")
        C.Write_Encoding=Encoding_8859_2;
    else
    {
        std::cout<<Value<<" unknown encoding"<<std::endl;
        return 0;
    }

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Write_CodePage)
{
    UNUSED_PARAMETER(Argument);

    C.Write_CodePage=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Ignore_File_Encoding)
{
    UNUSED_PARAMETER(Argument);

    C.Ignore_File_Encoding=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(SpecialChars)
{
    UNUSED_PARAMETER(Argument);

    C.SpecialChars_Enabled=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In_CSET_Remove)
{
    UNUSED_PARAMETER(Argument);

    C.In_CSET_Remove=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Log_cout)
{
    UNUSED_PARAMETER(Argument);

    C.Out_Log_cout=true;

    return -2; //Continue
}

//***************************************************************************
// Options - XML
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(Out_XML_cout)
{
    if (C.Cout!=Core::Cout_None)
    {
        std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
        return 1;
    }

    C.Cout=Core::Cout_XML;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_XML_File)
{
    //Form : --out-xml=(FileName)
    string FileName=string().assign(Argument, 10, std::string::npos);

    if (FileName=="-")
        return CL_Out_XML_cout(C, Argument);
    else
        C.Out_XML_FileName.assign(FileName);

    return -2; //Continue
}

//***************************************************************************
// Options - Technical
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(Out_Tech_cout)
{
    if (C.Cout!=Core::Cout_None)
    {
        std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
        return 1;
    }

    C.Cout=Core::Cout_Tech;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_Tech_File)
{
    //Form : --out-Tech=(FileName)
    string FileName=string().assign(Argument, 11, std::string::npos);

    if (FileName=="-")
        return CL_Out_Tech_cout(C, Argument);
    else
        C.Out_Tech_CSV_FileName.assign(FileName);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_Tech_XML)
{
    UNUSED_PARAMETER(Argument);

    C.Out_Tech_XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_Tech_XML_File)
{
    //Form : --out-Core-XML=(FileName)
    string FileName=string().assign(Argument, 15, std::string::npos);

    if (FileName=="-")
    {
        if (C.Cout!=Core::Cout_None)
        {
            std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
            return 1;
        }
        C.Cout=Core::Cout_Tech_XML;
    }
    else
        C.Out_Tech_XML_FileName.assign(FileName);

    return -2; //Continue
}

//***************************************************************************
// Options - Core
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(In_Core_File)
{
    UNUSED_PARAMETER(C);

    //Form : --in-Core=(FileName)
    #ifdef _WIN32
    In_Core_File_List.push_back(Ztring(Ztring().From_UTF8(Argument), 10, std::string::npos));
    #else
    In_Core_File_List.push_back(Ztring(Ztring().From_Local(Argument), 10, std::string::npos));
    #endif

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In_Core_Remove)
{
    UNUSED_PARAMETER(Argument);

    C.In_Core_Remove=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In_Core_XML)
{
    UNUSED_PARAMETER(Argument);

    C.In_Core_XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_Core_cout)
{
    if (C.Cout!=Core::Cout_None)
    {
        std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
        return 1;
    }

    C.Cout=Core::Cout_Core;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_Core_File)
{
    //Form : --out-Core=(FileName)
    //Form : --out-Tech=(FileName)
    string FileName=string().assign(Argument, 11, std::string::npos);

    if (FileName=="-")
        return CL_Out_Core_cout(C, Argument);
    else
        C.Out_Core_CSV_FileName.assign(FileName);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_Core_XML)
{
    UNUSED_PARAMETER(Argument);

    C.Out_Core_XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_Core_XML_File)
{
    //Form : --out-Core-XML=(FileName)
    string FileName=string().assign(Argument, 15, std::string::npos);

    if (FileName=="-")
    {
        if (C.Cout!=Core::Cout_None)
        {
            std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
            return 1;
        }
        C.Cout=Core::Cout_Core_XML;
    }
    else
        C.Out_Core_XML_FileName.assign(FileName);

    return -2; //Continue
}

//***************************************************************************
// Options - XMP
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(In__PMX_File)
{
    //Form : --in-XMP=(FileName)
    C.In__PMX_FileName.assign(Argument, 9, std::string::npos);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In__PMX_Remove)
{
    UNUSED_PARAMETER(Argument);

    C.In__PMX_Remove=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In__PMX_XML)
{
    UNUSED_PARAMETER(Argument);

    C.In__PMX_XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out__PMX_cout)
{
    if (C.Cout!=Core::Cout_None)
    {
        std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
        return 1;
    }

    C.Cout=Core::Cout__PMX;

    return -3; //Continue, one file mode
}

//---------------------------------------------------------------------------
CL_OPTION(Out__PMX_XML)
{
    UNUSED_PARAMETER(Argument);

    C.Out__PMX_XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out__PMX_File)
{
    string FileName=string().assign(Argument, 10, std::string::npos);

    if (FileName=="-")
        return CL_Out__PMX_cout(C, Argument);
    else
        C.Out__PMX_FileName.assign(FileName);

    return -3; //Continue, one file mode
}

//***************************************************************************
// Options - aXML
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(In_aXML_File)
{
    //Form : --in-aXML=(FileName)
    C.In_aXML_FileName.assign(Argument, 10, std::string::npos);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In_aXML_Remove)
{
    UNUSED_PARAMETER(Argument);

    C.In_aXML_Remove=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In_aXML_XML)
{
    UNUSED_PARAMETER(Argument);

    C.In_aXML_XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_aXML_cout)
{
    if (C.Cout!=Core::Cout_None)
    {
        std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
        return 1;
    }

    C.Cout=Core::Cout_aXML;

    return -3; //Continue, one file mode
}

//---------------------------------------------------------------------------
CL_OPTION(Out_aXML_XML)
{
    UNUSED_PARAMETER(Argument);

    C.Out_aXML_XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_aXML_File)
{
    string FileName=string().assign(Argument, 11, std::string::npos);

    if (FileName=="-")
        return CL_Out_aXML_cout(C, Argument);
    else
        C.Out_aXML_FileName.assign(FileName);

    return -3; //Continue, one file mode
}

//***************************************************************************
// Options - iXML
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(In_iXML_File)
{
    //Form : --in-iXML=(FileName)
    C.In_iXML_FileName.assign(Argument, 10, std::string::npos);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In_iXML_Remove)
{
    UNUSED_PARAMETER(Argument);

    C.In_iXML_Remove=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In_iXML_XML)
{
    UNUSED_PARAMETER(Argument);

    C.In_iXML_XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_iXML_cout)
{
    if (C.Cout!=Core::Cout_None)
    {
        std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
        return 1;
    }

    C.Cout=Core::Cout_iXML;

    return -3; //Continue, one file mode
}

//---------------------------------------------------------------------------
CL_OPTION(Out_iXML_XML)
{
    UNUSED_PARAMETER(Argument);

    C.Out_iXML_XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_iXML_File)
{
    string FileName=string().assign(Argument, 11, std::string::npos);

    if (FileName=="-")
        return CL_Out_iXML_cout(C, Argument);
    else
        C.Out_iXML_FileName.assign(FileName);

    return -3; //Continue, one file mode
}

//***************************************************************************
// Options - cue
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(In_cue__File)
{
    //Form : --in-cue=(FileName)
    C.In_cue__FileName.assign(Argument, 9, std::string::npos);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In_cue__Remove)
{
    UNUSED_PARAMETER(Argument);

    C.In_cue__Remove=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(In_cue__XML)
{
    UNUSED_PARAMETER(Argument);

    C.In_cue__XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_cue__cout)
{
    if (C.Cout!=Core::Cout_None)
    {
        std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
        return 1;
    }

    C.Cout=Core::Cout_cue_;

    return -3; //Continue, one file mode
}

//---------------------------------------------------------------------------
CL_OPTION(Out_cue__XML)
{
    UNUSED_PARAMETER(Argument);

    C.Out_cue__XML=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_cue__File)
{
    string FileName=string().assign(Argument, 10, std::string::npos);

    if (FileName=="-")
        return CL_Out_cue__cout(C, Argument);
    else
        C.Out_cue__FileName.assign(FileName);

    return -3; //Continue, one file mode
}

//***************************************************************************
// Options - MD5
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(MD5_Generate)
{
    UNUSED_PARAMETER(Argument);

    C.GenerateMD5=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(MD5_Verify)
{
    UNUSED_PARAMETER(Argument);

    C.VerifyMD5=true;
    C.VerifyMD5_Force=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(MD5_Embed)
{
    UNUSED_PARAMETER(Argument);

    C.EmbedMD5=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(MD5_Embed_Overwrite)
{
    UNUSED_PARAMETER(Argument);

    C.EmbedMD5=true;
    C.EmbedMD5_AuthorizeOverWritting=true;

    return -2; //Continue
}

//***************************************************************************
// Options - Others
//***************************************************************************

//---------------------------------------------------------------------------
CL_OPTION(Chunks_Remove)
{
    ZtringList Fields;
    Fields.Separator_Set(0, __T(","));
    Fields.Write(Ztring().From_Local(string().assign(Argument, 16, std::string::npos)));

    for (auto Field : Fields)
    {
        ZtringList Chunks;
        Chunks.Separator_Set(0, __T("/"));
        Chunks.Write(Field);

        size_t Level=0;
        for (auto Chunk :Chunks)
        {
            if (Chunk.empty()) // handle duplicate slashes
                continue;

            Level++;
            if (Chunk.size()!=4)
            {
                std::cerr<<"Invalid chunk identifier: "<<Chunk.To_UTF8()<<std::endl;
                return 1;
            }
            else if (!Level && (Chunk==__T("data") || Chunk==__T("ds64") || Chunk==__T("fmt ")))
            {
                std::cerr<<"Unable to remove mandatory chunk: "<<Chunk.To_UTF8()<<std::endl;
                return 1;
            }
        }

        C.In_Chunk_Remove(Field.To_UTF8());
    }

    return -2;
}

#if defined(ENABLE_C2PA)
//---------------------------------------------------------------------------
CL_OPTION(Out_C2PA_cout)
{
    if (C.Cout!=Core::Cout_None)
    {
        std::cerr<<"Cannot combine "<<Argument<<" with others display output"<<std::endl;
        return 1;
    }

    C.Cout=Core::Cout_C2PA;

    return -3; //Continue, one file mode
}

//---------------------------------------------------------------------------
CL_OPTION(Out_C2PA_JSON)
{
    UNUSED_PARAMETER(Argument);

    #if defined(C2PA_DYNAMIC_LOADING)
    if (!C2PA_Available())
    {
        std::cerr<<"C2PA support is not available, please install the plugin."<<std::endl;
        return 1;
    }
    #endif // defined(C2PA_DYNAMIC_LOADING)

    C.Out_C2PA_JSON=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(Out_C2PA_File)
{
    #if defined(C2PA_DYNAMIC_LOADING)
    if (!C2PA_Available())
    {
        std::cerr<<"C2PA support is not available, please install the plugin."<<std::endl;
        return 1;
    }
    #endif // defined(C2PA_DYNAMIC_LOADING)

    //Form : --out-c2pa=(FileName)
    string FileName=string().assign(Argument, 11, std::string::npos);

    if (FileName=="-")
        return CL_Out_C2PA_cout(C, Argument);
    else
        C.Out_C2PA_FileName.assign(FileName);

    return -3; //Continue, one file mode
}

//---------------------------------------------------------------------------
CL_OPTION(C2PA_Verify)
{
    UNUSED_PARAMETER(Argument);

    #if defined(C2PA_DYNAMIC_LOADING)
    if (!C2PA_Available())
    {
        std::cerr<<"C2PA support is not available, please install the plugin."<<std::endl;
        return 1;
    }
    #endif // defined(C2PA_DYNAMIC_LOADING)

    C.VerifyC2PA=true;
    C.VerifyC2PA_Force=true;

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(C2PA_Sign_Manifest)
{
    #if defined(C2PA_DYNAMIC_LOADING)
    if (!C2PA_Available())
    {
        std::cerr<<"C2PA support is not available, please install the plugin."<<std::endl;
        return 1;
    }
    #endif // defined(C2PA_DYNAMIC_LOADING)

    //Form : --c2pa-sign-manifest=(FileName)
    string FileName=string().assign(Argument, 21, std::string::npos);

    string Content;
    if (!ReadWholeFile(FileName, Content))
    {
        std::cerr<<"Unable to read the C2PA signing manifest: "<<FileName<<std::endl;
        return 1;
    }
    C.C2PA_SignManifestJson.assign(Content);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(C2PA_Sign_Certificate)
{
    #if defined(C2PA_DYNAMIC_LOADING)
    if (!C2PA_Available())
    {
        std::cerr<<"C2PA support is not available, please install the plugin."<<std::endl;
        return 1;
    }
    #endif // defined(C2PA_DYNAMIC_LOADING)

    //Form : --c2pa-sign-certificate=(FileName)
    string FileName=string().assign(Argument, 24, std::string::npos);

    string Content;
    if (!ReadWholeFile(FileName, Content))
    {
        std::cerr<<"Unable to read the C2PA signing certificate: "<<FileName<<std::endl;
        return 1;
    }
    C.C2PA_SignCertificate.assign(Content);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(C2PA_Sign_Key)
{
    #if defined(C2PA_DYNAMIC_LOADING)
    if (!C2PA_Available())
    {
        std::cerr<<"C2PA support is not available, please install the plugin."<<std::endl;
        return 1;
    }
    #endif // defined(C2PA_DYNAMIC_LOADING)

    //Form : --c2pa-sign-key=(FileName)
    string FileName=string().assign(Argument, 16, std::string::npos);

    string Content;
    if (!ReadWholeFile(FileName, Content))
    {
        std::cerr<<"Unable to read the C2PA signing private key: "<<FileName<<std::endl;
        return 1;
    }
    C.C2PA_SignPrivateKey.assign(Content);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(C2PA_Sign_Algorithm)
{
    #if defined(C2PA_DYNAMIC_LOADING)
    if (!C2PA_Available())
    {
    C.C2PA_SignAlgorithm.assign(Argument, 22, std::string::npos);
        std::cerr<<"C2PA support is not available, please install the plugin."<<std::endl;
        return 1;
    }
    #endif // defined(C2PA_DYNAMIC_LOADING)

    //Form : --c2pa-sign-algorithm=(Value)
    C.C2PA_SignAlgorithm.assign(Argument, 22, std::string::npos);

    return -2; //Continue
}

//---------------------------------------------------------------------------
CL_OPTION(C2PA_Sign_TA_URL)
{
    #if defined(C2PA_DYNAMIC_LOADING)
    if (!C2PA_Available())
    {
        std::cerr<<"C2PA support is not available, please install the plugin."<<std::endl;
        return 1;
    }
    #endif // defined(C2PA_DYNAMIC_LOADING)

    //Form : --c2pa-sign-ta-url=(Value)
    C.C2PA_SignTA_URL.assign(Argument, 19, std::string::npos);

    return -2; //Continue
}
#endif // defined(ENABLE_C2PA)

//---------------------------------------------------------------------------
CL_OPTION(RevertToRiff)
{
    C.RevertToRiff=true;
    C.In_Chunk_Remove("ds64");

    return -2; //Continue
}

//***************************************************************************
// Options - Default
//***************************************************************************

CL_OPTION(Default)
{
    //Form : --(Field)=(Value)
    bool Append=false;
    size_t Egal_Pos;
    if (Argument.size()==2)
        Egal_Pos=string::npos;
    else
        Egal_Pos=Argument.find('=');
    if (Egal_Pos==string::npos)
    {
        std::cerr<<Argument<<" is unknown"<<std::endl;
        return 1;
    }

    if (Egal_Pos && Argument[Egal_Pos-1]=='+')
        Append=true;

    #ifdef _WIN32
    Ztring Field(Ztring().From_UTF8(Argument), 2, Egal_Pos-(Append?3:2)); Field.MakeLowerCase();
    #else
    Ztring Field(Ztring().From_Local(Argument), 2, Egal_Pos-(Append?3:2)); Field.MakeLowerCase();
    #endif
    if (Field!=__T("description")
     && Field!=__T("originator")
     && Field!=__T("originatorreference")
     && Field!=__T("originationdate")
     && Field!=__T("originationtime")
     && Field!=__T("timereference")
     && Field!=__T("bextversion")
     && Field!=__T("loudnessvalue")
     && Field!=__T("loudnessrange")
     && Field!=__T("maxtruepeaklevel")
     && Field!=__T("maxmomentaryloudness")
     && Field!=__T("maxshorttermloudness")
     && Field!=__T("umid")
     && Field!=__T("history")
     && Field!=__T("xmp")
     && Field!=__T("axml")
     && Field!=__T("ixml")
     && Field.size()!=4) //All INFO sub-chunks
    {
        std::cerr<<Argument<<" is unknown"<<std::endl;
        return 1;
    }

    string Value=string(Argument, Egal_Pos+1, std::string::npos);
    if (Append)
        C.In_Core_Append(Field.To_UTF8(), Value);
    else
        C.In_Core_Add(Field.To_UTF8(), Value);

    return -2; //Continue
}

