//
// C++ Implementation: ADM_vidForcedPP
//
// Description:
//
//	Force postprocessing assuming constant quant & image type
//	Uselefull on some badly authored DVD for example
//
// Author: mean <fixounet@free.fr>, (C) 2004
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include "ADM_default.h"
#include "DIA_factory.h"
#include "audiofilter_conf.h"
#include <cmath>
/**
    \fn DIA_getAudioFilter
    \brief Dialog to manage audio filters
*/
int DIA_getAudioFilter(ADM_AUDIOFILTER_CONFIG *config, double tempoHint)
{
    double defaultConvTempo = tempoHint; // auto value (original duration / filtered duration)

    uint32_t vChan = config->mixerConf;
    uint32_t vFilm = config->filmConv;
    ELEM_TYPE_FLOAT vConvTempo = config->filmConvTempo;
    ELEM_TYPE_FLOAT vConvPitch = config->filmConvPitch;
    uint32_t vGainMode = (uint32_t)config->gainParam.mode;
    int32_t vShift = config->shiftInMs;
    uint32_t bShiftEnabled = config->shiftEnabled;
    ELEM_TYPE_FLOAT vGainValue = config->gainParam.gain10 / 10.;
    ELEM_TYPE_FLOAT vGainMaxLevel = config->gainParam.maxlevel10 / 10.;

    bool drcUseGain = (config->drcConf.mUseGain != 0);
    ELEM_TYPE_FLOAT drcFloorDB =
        10.0 * std::log10(config->drcConf.mFloor); // convert to DB; factor of 10 because it's power
    ELEM_TYPE_FLOAT drcAttackTime = config->drcConf.mAttackTime;
    ELEM_TYPE_FLOAT drcDecayTime = config->drcConf.mDecayTime;
    ELEM_TYPE_FLOAT drcRatio = config->drcConf.mRatio;
    ELEM_TYPE_FLOAT drcThresholdDB = config->drcConf.mThresholdDB;

    ELEM_TYPE_FLOAT chGainDB[ADM_CH_LAST];
    int chDelayMS[ADM_CH_LAST];
    for (int i = 0; i < ADM_CH_LAST; i++)
    {
        chGainDB[i] = config->chansConf.chGainDB[i];
        chDelayMS[i] = config->chansConf.chDelayMS[i];
    }

    bool bChRemapEnabled = config->chansConf.enableRemap;
    uint32_t vChRemap[9];
    for (int i = 0; i < 9; i++)
        vChRemap[i] = config->chansConf.remap[i];

    bool vEqEnable = config->eqConf.enable;
    ELEM_TYPE_FLOAT eqLowDB = config->eqConf.lowDB;
    ELEM_TYPE_FLOAT eqMidDB = config->eqConf.midDB;
    ELEM_TYPE_FLOAT eqHighDB = config->eqConf.highDB;
    ELEM_TYPE_FLOAT eqCutLM = config->eqConf.cutOffLM;
    ELEM_TYPE_FLOAT eqCutMH = config->eqConf.cutOffMH;

#define PX(x) (&(config->x))
    diaElemToggleUint eResample(PX(resamplerEnabled), QT_TRANSLATE_NOOP("adm", "R_esampling (Hz):"),
                                PX(resamplerFrequency), QT_TRANSLATE_NOOP("adm", "Resampling frequency (Hz)"), 6000,
                                64000);

    //**********************************
    diaMenuEntry menuFPS[] = {{FILMCONV_NONE, QT_TRANSLATE_NOOP("adm", "None"), NULL},
                              {FILMCONV_FILM2PAL, QT_TRANSLATE_NOOP("adm", "Film to PAL"), NULL},
                              {FILMCONV_PAL2FILM, QT_TRANSLATE_NOOP("adm", "PAL to Film"), NULL},
                              {FILMCONV_CUSTOM, QT_TRANSLATE_NOOP("adm", "Custom"), NULL}};
#define NB_ITEMS(x) sizeof(x) / sizeof(diaMenuEntry)
    diaElemMenu eFPS(&vFilm, QT_TRANSLATE_NOOP("adm", "_Frame rate change:"), NB_ITEMS(menuFPS), menuFPS);

    if (vFilm != FILMCONV_CUSTOM)
    {
        vConvTempo = defaultConvTempo;
    }
    diaElemFloatResettable eCustomTempo(&vConvTempo, QT_TRANSLATE_NOOP("adm", "Tempo multiplier:"),
                                        AUDIO_FILTER_STRETCH_MIN,
                                        AUDIO_FILTER_STRETCH_MAX,
                                        defaultConvTempo,
                                        QT_TRANSLATE_NOOP("adm", "A value of 2.0 means twice as fast playback."), 9);
    diaElemFloatResettable eCustomPitch(&vConvPitch, QT_TRANSLATE_NOOP("adm", "Pitch multiplier:"),
                                        AUDIO_FILTER_STRETCH_MIN,
                                        AUDIO_FILTER_STRETCH_MAX,
                                        1.0,
                                        QT_TRANSLATE_NOOP("adm", "Every doubling means one octave upshift"), 9);
    eFPS.link(menuFPS + 3, 1, &eCustomTempo);
    eFPS.link(menuFPS + 3, 1, &eCustomPitch);

    diaElemFrame frameFPS(QT_TRANSLATE_NOOP("adm", "Frame rate change"));
    frameFPS.swallow(&eFPS);
    frameFPS.swallow(&eCustomTempo);
    frameFPS.swallow(&eCustomPitch);

    //**********************************
    diaMenuEntry menuMixer[] = {{CHANNEL_INVALID, QT_TRANSLATE_NOOP("adm", "No change"), NULL},
                                {CHANNEL_MONO, QT_TRANSLATE_NOOP("adm", "Mono"), NULL},
                                {CHANNEL_STEREO, QT_TRANSLATE_NOOP("adm", "Stereo"), NULL},
                                {CHANNEL_STEREO_HEADPHONES, QT_TRANSLATE_NOOP("adm", "Stereo headphones"), NULL},
                                {CHANNEL_2F_1R, QT_TRANSLATE_NOOP("adm", "Stereo+surround"), NULL},
                                {CHANNEL_3F, QT_TRANSLATE_NOOP("adm", "Stereo+center"), NULL},
                                {CHANNEL_3F_1R, QT_TRANSLATE_NOOP("adm", "Stereo+center+surround"), NULL},
                                {CHANNEL_2F_2R, QT_TRANSLATE_NOOP("adm", "Stereo front+stereo rear"), NULL},
                                {CHANNEL_3F_2R, QT_TRANSLATE_NOOP("adm", "5 channels"), NULL},
                                {CHANNEL_3F_2R_LFE, QT_TRANSLATE_NOOP("adm", "5.1"), NULL},
                                {CHANNEL_DOLBY_PROLOGIC, QT_TRANSLATE_NOOP("adm", "Dolby Pro Logic"), NULL},
                                {CHANNEL_DOLBY_PROLOGIC2, QT_TRANSLATE_NOOP("adm", "Dolby Pro Logic II"), NULL},
                                {CHANNEL_SURROUND_HEADPHONES, QT_TRANSLATE_NOOP("adm", "Surround headphones"), NULL}};
    //*************************
    diaMenuEntry menuGain[] = {{ADM_NO_GAIN, QT_TRANSLATE_NOOP("adm", "None"), NULL},
                               {ADM_GAIN_AUTOMATIC, QT_TRANSLATE_NOOP("adm", "Automatic"), NULL},
                               {ADM_GAIN_MANUAL, QT_TRANSLATE_NOOP("adm", "Manual (dB)"), NULL}};

    diaElemFrame frameGain(QT_TRANSLATE_NOOP("adm", "Gain"));
    diaElemMenu eGain(&vGainMode, QT_TRANSLATE_NOOP("adm", "_Gain mode:"), NB_ITEMS(menuGain), menuGain);
    diaElemFloat eGainValue(&vGainValue, QT_TRANSLATE_NOOP("adm", "G_ain value:"), -10, 40);
    diaElemFloat eGainMaxLevel(&vGainMaxLevel, QT_TRANSLATE_NOOP("adm", "_Maximum value:"), -10, 0);

    eGain.link(&(menuGain[2]), 1, &eGainValue);
    eGain.link(&(menuGain[1]), 1, &eGainMaxLevel);
    frameGain.swallow(&eGain);
    frameGain.swallow(&eGainValue);
    frameGain.swallow(&eGainMaxLevel);
    //****************************

    diaElemMenu eMixer(&vChan, QT_TRANSLATE_NOOP("adm", "_Mixer:"), NB_ITEMS(menuMixer), menuMixer);
    bool bMixer = config->mixerEnabled;
    diaElemToggle tMixer(&bMixer, QT_TRANSLATE_NOOP("adm", "Remix"));
    tMixer.link(1, &eMixer);

    diaElemFrame frameMixer(QT_TRANSLATE_NOOP("adm", "Mixer"));
    frameMixer.swallow(&tMixer);
    frameMixer.swallow(&eMixer);

    //****************************
    diaElemToggleInt eShift(&bShiftEnabled, QT_TRANSLATE_NOOP("adm", "Shift audio (ms):"), &vShift,
                            QT_TRANSLATE_NOOP("adm", "Shift Value (ms):"), -30000, 30000);
/************************************/
#define NB_ELEM(x) sizeof(x) / sizeof(diaElem *)
    diaElem *mainElems[] = {&frameFPS, &eResample, &eShift, &frameMixer, &frameGain};
    diaElemTabs tabMain(QT_TRANSLATE_NOOP("adm", "Main"), NB_ELEM(mainElems), mainElems);

    //*** DRC tab *****
    diaElemToggle tDRC(PX(drcEnabled), QT_TRANSLATE_NOOP("adm", "Enable Compressor"));
    diaElemToggle eDrcNorm(&drcUseGain, QT_TRANSLATE_NOOP("adm", "Normalize"));
    diaElemFloatResettable eDrcThres(&drcThresholdDB, QT_TRANSLATE_NOOP("adm", "Threshold (dB):"), -60, -1, -12);
    diaElemFloatResettable eDrcNFloor(&drcFloorDB, QT_TRANSLATE_NOOP("adm", "Noise floor (dB):"), -80, -20, -30);
    diaElemFloatResettable eDrcRatio(&drcRatio, QT_TRANSLATE_NOOP("adm", "Ratio:"), 1.1, 10, 2);
    diaElemFloatResettable eDrcAttack(&drcAttackTime, QT_TRANSLATE_NOOP("adm", "Attack time (sec):"), 0.1, 5, 0.2);
    diaElemFloatResettable eDrcDecay(&drcDecayTime, QT_TRANSLATE_NOOP("adm", "Release time (sec):"), 1, 30, 1);
    tDRC.link(1, &eDrcNorm);
    tDRC.link(1, &eDrcThres);
    tDRC.link(1, &eDrcNFloor);
    tDRC.link(1, &eDrcRatio);
    tDRC.link(1, &eDrcAttack);
    tDRC.link(1, &eDrcDecay);
    diaElem *drcElems[] = {&tDRC, &eDrcNorm, &eDrcThres, &eDrcNFloor, &eDrcRatio, &eDrcAttack, &eDrcDecay};
    diaElemTabs tabDRC(QT_TRANSLATE_NOOP("adm", "DRC"), NB_ELEM(drcElems), drcElems);

    //*** Equalizer tab ******
    diaElemToggle tEQ(&vEqEnable, QT_TRANSLATE_NOOP("adm", "Enable Equalizer"));
    diaElemFloatResettable eEqLow(&eqLowDB, QT_TRANSLATE_NOOP("adm", "Bass (dB):"), -30, +30, 0);
    diaElemFloatResettable eEqCutLM(&eqCutLM, QT_TRANSLATE_NOOP("adm", "Bass/Mid cut-off (Hz):"), 100, 1000, 880);
    diaElemFloatResettable eEqMid(&eqMidDB, QT_TRANSLATE_NOOP("adm", "Mid (dB):"), -30, +30, 0);
    diaElemFloatResettable eEqCutMH(&eqCutMH, QT_TRANSLATE_NOOP("adm", "Mid/Treble cut-off (Hz):"), 2000, 10000, 5000);
    diaElemFloatResettable eEqHigh(&eqHighDB, QT_TRANSLATE_NOOP("adm", "Treble (dB):"), -30, +30, 0);
    tEQ.link(1, &eEqLow);
    tEQ.link(1, &eEqCutLM);
    tEQ.link(1, &eEqMid);
    tEQ.link(1, &eEqCutMH);
    tEQ.link(1, &eEqHigh);

    char *gainTxt = ADM_strdup(QT_TRANSLATE_NOOP(
        "adm", "<hr><p>It is highly recommended to enable normalization when using positive gain values</p>"));

    diaElemReadOnlyText noteEq(NULL, gainTxt, NULL);

    diaElem *eqElems[] = {&tEQ, &eEqLow, &eEqMid, &eEqHigh, &eEqCutLM, &eEqCutMH, &noteEq};
    diaElemTabs tabEq(QT_TRANSLATE_NOOP("adm", "Equalizer"), NB_ELEM(eqElems), eqElems);

    //*** Channel gains tab ******
    diaElemFloat eChGainFLValue(chGainDB + ADM_CH_FRONT_LEFT, QT_TRANSLATE_NOOP("adm", "Front left (dB):"), -30, +30);
    diaElemFloat eChGainFRValue(chGainDB + ADM_CH_FRONT_RIGHT, QT_TRANSLATE_NOOP("adm", "Front right (dB):"), -30, +30);
    diaElemFloat eChGainFCValue(chGainDB + ADM_CH_FRONT_CENTER, QT_TRANSLATE_NOOP("adm", "Front center (dB):"), -30,
                                +30);
    diaElemFloat eChGainRLValue(chGainDB + ADM_CH_REAR_LEFT, QT_TRANSLATE_NOOP("adm", "Rear left (dB):"), -30, +30);
    diaElemFloat eChGainRRValue(chGainDB + ADM_CH_REAR_RIGHT, QT_TRANSLATE_NOOP("adm", "Rear right (dB):"), -30, +30);
    diaElemFloat eChGainRCValue(chGainDB + ADM_CH_REAR_CENTER, QT_TRANSLATE_NOOP("adm", "Rear center (dB):"), -30, +30);
    diaElemFloat eChGainSLValue(chGainDB + ADM_CH_SIDE_LEFT, QT_TRANSLATE_NOOP("adm", "Side left (dB):"), -30, +30);
    diaElemFloat eChGainSRValue(chGainDB + ADM_CH_SIDE_RIGHT, QT_TRANSLATE_NOOP("adm", "Side right (dB):"), -30, +30);
    diaElemFloat eChGainLFEValue(chGainDB + ADM_CH_LFE, QT_TRANSLATE_NOOP("adm", "Low-frequency effects (LFE) (dB):"),
                                 -30, +30);

    diaElemReadOnlyText noteChGain(NULL, gainTxt, NULL);

    ADM_dealloc(gainTxt);

    diaElem *chanGainsElems[] = {&eChGainFLValue, &eChGainFRValue, &eChGainFCValue, &eChGainSLValue,  &eChGainSRValue,
                                 &eChGainRLValue, &eChGainRRValue, &eChGainRCValue, &eChGainLFEValue, &noteChGain};
    diaElemTabs tabChanGains(QT_TRANSLATE_NOOP("adm", "Channel gains"), NB_ELEM(chanGainsElems), chanGainsElems);

    //*** Channel delays tab ******
    diaElemInteger eChDelayFLValue(chDelayMS + ADM_CH_FRONT_LEFT, QT_TRANSLATE_NOOP("adm", "Front left (ms):"), 0,
                                   10000);
    diaElemInteger eChDelayFRValue(chDelayMS + ADM_CH_FRONT_RIGHT, QT_TRANSLATE_NOOP("adm", "Front right (ms):"), 0,
                                   10000);
    diaElemInteger eChDelayFCValue(chDelayMS + ADM_CH_FRONT_CENTER, QT_TRANSLATE_NOOP("adm", "Front center (ms):"), 0,
                                   10000);
    diaElemInteger eChDelayRLValue(chDelayMS + ADM_CH_REAR_LEFT, QT_TRANSLATE_NOOP("adm", "Rear left (ms):"), 0, 10000);
    diaElemInteger eChDelayRRValue(chDelayMS + ADM_CH_REAR_RIGHT, QT_TRANSLATE_NOOP("adm", "Rear right (ms):"), 0,
                                   10000);
    diaElemInteger eChDelayRCValue(chDelayMS + ADM_CH_REAR_CENTER, QT_TRANSLATE_NOOP("adm", "Rear center (ms):"), 0,
                                   10000);
    diaElemInteger eChDelaySLValue(chDelayMS + ADM_CH_SIDE_LEFT, QT_TRANSLATE_NOOP("adm", "Side left (ms):"), 0, 10000);
    diaElemInteger eChDelaySRValue(chDelayMS + ADM_CH_SIDE_RIGHT, QT_TRANSLATE_NOOP("adm", "Side right (ms):"), 0,
                                   10000);
    diaElemInteger eChDelayLFEValue(chDelayMS + ADM_CH_LFE,
                                    QT_TRANSLATE_NOOP("adm", "Low-frequency effects (LFE) (ms):"), 0, 10000);

    diaElemReadOnlyText noteChDelay(
        NULL,
        QT_TRANSLATE_NOOP("adm", "<hr><p>The final delay will be the sum of a value above and the \"Shift audio\" "
                                 "value provided on the Main tab</p>"),
        NULL);

    diaElem *chanDelaysElems[] = {&eChDelayFLValue,  &eChDelayFRValue, &eChDelayFCValue, &eChDelaySLValue,
                                  &eChDelaySRValue,  &eChDelayRLValue, &eChDelayRRValue, &eChDelayRCValue,
                                  &eChDelayLFEValue, &noteChDelay};
    diaElemTabs tabChanDelays(QT_TRANSLATE_NOOP("adm", "Channel delays"), NB_ELEM(chanDelaysElems), chanDelaysElems);

    //*** Channel remap tab ******
    diaElemToggle eChRemap(&bChRemapEnabled, QT_TRANSLATE_NOOP("adm", "Enable Remap"));
    diaMenuEntry menuRemap[] = {{0, QT_TRANSLATE_NOOP("adm", "Front left"), NULL},
                                {1, QT_TRANSLATE_NOOP("adm", "Front right"), NULL},
                                {2, QT_TRANSLATE_NOOP("adm", "Front center"), NULL},
                                {3, QT_TRANSLATE_NOOP("adm", "Side left"), NULL},
                                {4, QT_TRANSLATE_NOOP("adm", "Side right"), NULL},
                                {5, QT_TRANSLATE_NOOP("adm", "Rear left"), NULL},
                                {6, QT_TRANSLATE_NOOP("adm", "Rear right"), NULL},
                                {7, QT_TRANSLATE_NOOP("adm", "Rear center"), NULL},
                                {8, QT_TRANSLATE_NOOP("adm", "Low-frequency effects (LFE)"), NULL}};
    diaElemMenu eRemapFL(vChRemap + 0, QT_TRANSLATE_NOOP("adm", "Front left to:"), NB_ITEMS(menuRemap), menuRemap);
    diaElemMenu eRemapFR(vChRemap + 1, QT_TRANSLATE_NOOP("adm", "Front right to:"), NB_ITEMS(menuRemap), menuRemap);
    diaElemMenu eRemapFC(vChRemap + 2, QT_TRANSLATE_NOOP("adm", "Front center to:"), NB_ITEMS(menuRemap), menuRemap);
    diaElemMenu eRemapSL(vChRemap + 3, QT_TRANSLATE_NOOP("adm", "Side left to:"), NB_ITEMS(menuRemap), menuRemap);
    diaElemMenu eRemapSR(vChRemap + 4, QT_TRANSLATE_NOOP("adm", "Side right to:"), NB_ITEMS(menuRemap), menuRemap);
    diaElemMenu eRemapRL(vChRemap + 5, QT_TRANSLATE_NOOP("adm", "Rear left to:"), NB_ITEMS(menuRemap), menuRemap);
    diaElemMenu eRemapRR(vChRemap + 6, QT_TRANSLATE_NOOP("adm", "Rear right to:"), NB_ITEMS(menuRemap), menuRemap);
    diaElemMenu eRemapRC(vChRemap + 7, QT_TRANSLATE_NOOP("adm", "Rear center to:"), NB_ITEMS(menuRemap), menuRemap);
    diaElemMenu eRemapLFE(vChRemap + 8, QT_TRANSLATE_NOOP("adm", "Low-frequency effects (LFE) to:"),
                          NB_ITEMS(menuRemap), menuRemap);
    eChRemap.link(1, &eRemapFL);
    eChRemap.link(1, &eRemapFR);
    eChRemap.link(1, &eRemapFC);
    eChRemap.link(1, &eRemapSL);
    eChRemap.link(1, &eRemapSR);
    eChRemap.link(1, &eRemapRL);
    eChRemap.link(1, &eRemapRR);
    eChRemap.link(1, &eRemapRC);
    eChRemap.link(1, &eRemapLFE);

    diaElemReadOnlyText noteRemap(
        NULL,
        QT_TRANSLATE_NOOP("adm", "<hr><p>Remapping does not change the channel layout, therefore:</p><ul>"
                                 "<li>mapping a channel to a non-existent one will result in loss of the channel</li>"
                                 "<li>mapping a non-existent channel will result in silence</li></ul>"),
        NULL);

    diaElem *remapElems[] = {&eChRemap, &eRemapFL, &eRemapFR, &eRemapFC,  &eRemapSL, &eRemapSR,
                             &eRemapRL, &eRemapRR, &eRemapRC, &eRemapLFE, &noteRemap};
    diaElemTabs tabRemap(QT_TRANSLATE_NOOP("adm", "Channel remap"), NB_ELEM(remapElems), remapElems);

    //*** ALL TABS *************************
    diaElemTabs *tabs[] = {&tabMain, &tabDRC, &tabEq, &tabChanGains, &tabChanDelays, &tabRemap};

//**************************************
#undef NB_ELEM
#define NB_ELEM(x) sizeof(x) / sizeof(diaElemTabs *)
    if (diaFactoryRunTabs(QT_TRANSLATE_NOOP("adm", "Audio Filters"), NB_ELEM(tabs), tabs))
    {
        config->mixerConf = (CHANNEL_CONF)vChan;
        config->filmConv = (FILMCONV)vFilm;
        config->filmConvTempo = vConvTempo;
        config->filmConvPitch = vConvPitch;
        config->gainParam.mode = (ADM_GAINMode)vGainMode;
        config->gainParam.gain10 = vGainValue * 10;
        config->gainParam.maxlevel10 = vGainMaxLevel * 10;
        config->mixerEnabled = bMixer;
        config->shiftInMs = vShift;
        config->shiftEnabled = bShiftEnabled;

        config->drcConf.mUseGain = (drcUseGain ? 1 : 0);
        config->drcConf.mFloor = pow(10.0, drcFloorDB / 10.0); // convert to linear; factor of 10 because it's power
        config->drcConf.mAttackTime = drcAttackTime;
        config->drcConf.mDecayTime = drcDecayTime;
        config->drcConf.mRatio = drcRatio;
        config->drcConf.mThresholdDB = drcThresholdDB;

        for (int i = 0; i < ADM_CH_LAST; i++)
        {
            if (i < ADM_CH_FRONT_LEFT)
                continue;
            if (i > ADM_CH_LFE)
                continue;
            config->chansConf.chGainDB[i] = chGainDB[i];
            config->chansConf.chDelayMS[i] = chDelayMS[i];
        }

        config->chansConf.enableRemap = bChRemapEnabled;
        for (int i = 0; i < 9; i++)
            config->chansConf.remap[i] = vChRemap[i];

        config->eqConf.enable = vEqEnable;
        config->eqConf.lowDB = eqLowDB;
        config->eqConf.midDB = eqMidDB;
        config->eqConf.highDB = eqHighDB;
        config->eqConf.cutOffLM = eqCutLM;
        config->eqConf.cutOffMH = eqCutMH;
        return true;
    }

    return false;
}

// EOF
