/*
    Phaser.cpp - Phaser effect

    Original ZynAddSubFX author Nasca Octavian Paul

    This file is a derivative of a ZynAddSubFX original.

*/

#include "Misc/SynthEngine.h"
#include "Effects/Phaser.h"
#include "Misc/NumericFuncs.h"

#include <algorithm>

using func::limit;
using func::invSignal;

#define PHASER_LFO_SHAPE 2
#define ONE_  0.99999f        // To prevent LFO ever reaching 1.0f for filter stability purposes
#define ZERO_ 0.00001f        // Same idea as above.

namespace {
    const int PRESET_SIZE = 15;
    const int NUM_PRESETS = 12;
    unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
        // Phaser
        {64, 64, 36,  0,   0, 64,  110, 64,  1,  0,   0, 20, 0, 0,  0 },
        {64, 64, 35,  0,   0, 88,  40,  64,  3,  0,   0, 20, 0,  0, 0 },
        {64, 64, 31,  0,   0, 66,  68,  107, 2,  0,   0, 20, 0,  0, 0 },
        {39, 64, 22,  0,   0, 66,  67,  10,  5,  0,   1, 20, 0,  0, 0 },
        {64, 64, 20,  0,   1, 110, 67,  78,  10, 0,   0, 20, 0,  0, 0 },
        {64, 64, 53,  100, 0, 58,  37,  78,  3,  0,   0, 20, 0,  0, 0 },
        // APhaser
        {64, 64, 14,  0,   1, 64,  64,  40,  4,  10,  0, 110,1,  20, 1 },
        {64, 64, 14,  5,   1, 64,  70,  40,  6,  10,  0, 110,1,  20, 1 },
        {64, 64, 9,   0,   0, 64,  60,  40,  8,  10,  0, 40, 0,  20, 1 },
        {64, 64, 14,  10,  0, 64,  45,  80,  7,  10,  1, 110,1,  20, 1 },
        {25, 64, 127, 10,  0, 64,  25,  16,  8,  100, 0, 25, 0,  20, 1 },
        {64, 64, 1,   10,  1, 64,  70,  40,  12, 10,  0, 110,1,  20, 1 }
    };
}

Phaser::Phaser(bool insertion_, float *efxoutl_, float *efxoutr_, SynthEngine *_synth) :
    Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, _synth),
    lfo(_synth),
    oldl(NULL),
    oldr(NULL),
    xn1l(NULL),
    xn1r(NULL),
    yn1l(NULL),
    yn1r(NULL)
{
    analog_setup();
    setpreset(Ppreset);
    Pchanged = false;
    cleanup();
}

void Phaser::analog_setup()
{
    //model mismatch between JFET devices
    offset[0]  = -0.250

    offset[1]  = 0.9408924f;
    offset[2]  = 0.998f;
    offset[3]  = -0.3486182f;
    offset[4]  = -0.2762545f;
    offset[5]  = -0.5215785f;
    offset[6]  = 0.2509303f;
    offset[7]  = -0.9408924f;
    offset[8]  = -0.998f;
    offset[9]  = 0.3486182f;
    offset[10] = 0.2762545f;
    offset[11] = 0.5215785f;

    barber = 0;  //Deactivate barber pole phasing by default

    mis       = 1.0f;
    Rmin      = 625.0f; // 2N5457 typical on resistance at Vgs = 0
    Rmax      = 22000.0f; // Resistor parallel to FET
    Rmx       = Rmin / Rmax;
    Rconst    = 1.0f + Rmx; // Handle parallel resistor relationship
    C         = 0.00000005f; // 50 nF
    CFs       = 2.0f * synth->samplerate_f * C;
    invperiod = 1.0f / synth->buffersize_f;
}

Phaser::~Phaser()
{
    if (oldl != NULL)
        delete [] oldl;
    if (oldr != NULL)
        delete [] oldr;

    if (xn1l)
        delete[] xn1l;
    if (yn1l)
        delete[] yn1l;
    if (xn1r)
        delete[] xn1r;
    if (yn1r)
        delete[] yn1r;
}

// Effect output
void Phaser::out(float *smpsl, float *smpsr)
{
    outvolume.advanceValue(synth->sent_buffersize);

    if (Panalog)
        AnalogPhase(smpsl, smpsr);
    else
        NormalPhase(smpsl, smpsr);
}

void Phaser::AnalogPhase(float *smpsl, float *smpsr)
{
    float lfoVall;
    float lfoValr;
    float modl;
    float modr;
    float gl;
    float gr;
    float hpfl = 0;
    float hpfr = 0;

    lfo.effectlfoout(&lfoVall, &lfoValr);
    modl = lfoVall * width + (depth - 0.5f);
    modr = lfoValr * width + (depth - 0.5f);

    modl = limit(modl, ZERO_, ONE_);
    modr = limit(modr, ZERO_, ONE_);

    if (Phyper)
    {
        // Triangle wave squared is approximately sine on bottom, triangle on top
        // Result is exponential sweep more akin to filter in synth with
        // exponential generator circuitry.
        modl *= modl;
        modr *= modr;
    }

    // g.,g. is Vp - Vgs. Typical FET drain-source resistance follows constant/[1-sqrt(Vp - Vgs)]
    modl = sqrtf(1.0f - modl);
    modr = sqrtf(1.0f - modr);

    diffr = (modr - oldrgain) * invperiod;
    diffl = (modl - oldlgain) * invperiod;

    gl = oldlgain;
    gr = oldrgain;
    oldlgain = modl;
    oldrgain = modr;

   for (int i = 0; i < synth->sent_buffersize; ++i)
   {
        gl += diffl; // Linear interpolation between LFO samples
        gr += diffr;

        float xnl(smpsl[i] * pangainL.getAndAdvanceValue());
        float xnr(smpsr[i] * pangainR.getAndAdvanceValue());

        if (barber)
        {
            gl = fmodf((gl + 0.25f), ONE_);
            gr = fmodf((gr + 0.25f), ONE_);
        }

        xnl = applyPhase(xnl, gl, fbl, hpfl, yn1l, xn1l);
        xnr = applyPhase(xnr, gr, fbr, hpfr, yn1r, xn1r);

        fbl = xnl * fb;
        fbr = xnr * fb;
        efxoutl[i] = xnl;
        efxoutr[i] = xnr;
    }

    if (Poutsub)
    {
        invSignal(efxoutl, synth->sent_buffersize);
        invSignal(efxoutr, synth->sent_buffersize);
    }
}

float Phaser::applyPhase(float x, float g, float fb,
                         float &hpf, float *yn1, float *xn1)
{
    for (int j = 0; j < Pstages; ++j)
    { //Phasing routine
        mis = 1.0f + offsetpct * offset[j];

        // This is symmetrical.
        // FET is not, so this deviates slightly, however sym dist. is
        // better sounding than a real FET.
        float d = (1.0f + 2.0f * (0.25f + g) * hpf * hpf * distortion) * mis;
        Rconst = 1.0f + mis * Rmx;

        // This is 1/R. R is being modulated to control filter fc.
        float b    = (Rconst - g) / (d * Rmin);
        float gain = (CFs - b) / (CFs + b);
        yn1[j] = (gain * (x + yn1[j]) - xn1[j]) + 1e-12; // anti-denormal

        // high pass filter:
        // Distortion depends on the high-pass part of the AP stage.
        hpf = yn1[j] + (1.0f - gain) * xn1[j];

        xn1[j] = x;
        x = yn1[j];
        if (j == 1)
            x += fb; // Insert feedback after first phase stage
    }
    return x;
}

void Phaser::NormalPhase(float *smpsl, float *smpsr)
{
    float lfol, lfor, lgain, rgain, tmp;

    lfo.effectlfoout(&lfol, &lfor);
    lgain = lfol;
    rgain = lfor;
    lgain = (expf(lgain * PHASER_LFO_SHAPE) - 1)
            / (expf(PHASER_LFO_SHAPE) - 1.0f);
    rgain = (expf(rgain * PHASER_LFO_SHAPE) - 1)
            / (expf(PHASER_LFO_SHAPE) - 1.0f);

    lgain = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * lgain * depth;
    lgain = limit(lgain,ZERO_,ONE_);//(lgain > 1.0f) ? 1.0f : lgain;
    rgain = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * rgain * depth;
    rgain = limit(rgain,ZERO_,ONE_);//(rgain > 1.0f) ? 1.0f : rgain;

    for (int i = 0; i < synth->sent_buffersize; ++i)
    {
        float x = (float)i / synth->sent_buffersize_f;
        float x1 = 1.0f - x;
        float gl = lgain * x + oldlgain * x1;
        float gr = rgain * x + oldrgain * x1;
        float inl = smpsl[i] * pangainL.getAndAdvanceValue() + fbl;
        float inr = smpsr[i] * pangainR.getAndAdvanceValue() + fbr;

        // Phasing routine
        for (int j = 0; j < Pstages * 2; ++j)
        {
            // Left channel
            tmp = oldl[j];
            oldl[j] = gl * tmp + inl;
            inl = (tmp - gl * oldl[j]) + 1e-12; // anti-denormal
            // Right channel
            tmp = oldr[j];
            oldr[j] = gr * tmp + inr;
            inr = (tmp - gr * oldr[j]) + 1e-12; // anti-denormal
        }

        // Left/Right crossing
        float l = inl;
        float r = inr;
        inl = l * (1.0f - lrcross.getValue()) + r * lrcross.getValue();
        inr = r * (1.0f - lrcross.getValue()) + l * lrcross.getValue();
        lrcross.advanceValue();
        fbl = inl * fb;
        fbr = inr * fb;
        efxoutl[i] = inl;
        efxoutr[i] = inr;
    }
    oldlgain = lgain;
    oldrgain = rgain;
    if (Poutsub)
        for (int i = 0; i < synth->sent_buffersize; ++i)
        {
            efxoutl[i] *= -1.0f;
            efxoutr[i] *= -1.0f;
        }
}

// Cleanup the effect
void Phaser::cleanup(void)
{
    fbl = fbr = oldlgain = oldrgain = 0.0f;
    memset(oldl, 0, sizeof(float)*Pstages * 2);
    memset(oldr, 0, sizeof(float)*Pstages * 2);
    memset(xn1l, 0, sizeof(float)*Pstages);
    memset(xn1r, 0, sizeof(float)*Pstages);
    memset(yn1l, 0, sizeof(float)*Pstages);
    memset(yn1r, 0, sizeof(float)*Pstages);
    lfo.resetState();
}

// Parameter control
void Phaser::setdepth(unsigned char Pdepth_)
{
    Pdepth = Pdepth_;
    depth = Pdepth / 127.0f;
}

void Phaser::setwidth(unsigned char Pwidth_)
{
    Pwidth = Pwidth_;
    width = Pwidth / 127.0f;
}

void Phaser::setfb(unsigned char Pfb_)
{
    Pfb = Pfb_;
    fb = (Pfb - 64.0f) / 64.1f;
}

void Phaser::setvolume(unsigned char Pvolume_)
{
    Pvolume = Pvolume_;
    float tmp = Pvolume / 127.0f;
    outvolume.setTargetValue(tmp);
    volume.setTargetValue((!insertion) ? 1.0f : tmp);
}

void Phaser::setdistortion(unsigned char Pdistortion_)
{
    Pdistortion = Pdistortion_;
    distortion = (float)Pdistortion / 127.0f;
}

void Phaser::setoffset(unsigned char Poffset_)
{
    Poffset = Poffset_;
    offsetpct     = (float)Poffset / 127.0f;
}

void Phaser::setstages(unsigned char Pstages_)
{
    if (oldl != NULL)
        delete [] oldl;
    if (xn1l)
        delete[] xn1l;
    if (yn1l)
        delete[] yn1l;
    if (oldr != NULL)
        delete [] oldr;
    if (xn1r)
        delete[] xn1r;
    if (yn1r)
        delete[] yn1r;

    Pstages = Pstages_;
    oldl = new float[Pstages * 2];
    oldr = new float[Pstages * 2];
    xn1l = new float[Pstages];
    xn1r = new float[Pstages];
    yn1l = new float[Pstages];
    yn1r = new float[Pstages];
    cleanup();
}

void Phaser::setphase(unsigned char Pphase_)
{
    Pphase = Pphase_;
    phase = Pphase / 127.0;
}

void Phaser::setpreset(unsigned char npreset)
{
    if (npreset < 0xf)
    {
        if (npreset >= NUM_PRESETS)
            npreset = NUM_PRESETS - 1;
        for (int n = 0; n < PRESET_SIZE; ++n)
            changepar(n, presets[npreset][n]);
        // All presets use no BPM syncing.
        changepar(EFFECT::control::bpm, 0);
        Ppreset = npreset;
    }
    else
    {
        unsigned char preset = npreset & 0xf;
        unsigned char param = npreset >> 4;
        if (param == 0xf)
            param = 0;
        changepar(param, presets[preset][param]);
    }
    Pchanged = false;
}

void Phaser::changepar(int npar, unsigned char value)
{
    if (npar == -1)
    {
        Pchanged = (value != 0);
        return;
    }
    switch (npar)
    {
        case 0:
            setvolume(value);
            break;

        case 1:
            setpanning(value);
            break;

        case 2:
            lfo.Pfreq = value;
            lfo.updateparams();
            break;

        case 3:
            lfo.Prandomness = value;
            lfo.updateparams();
            break;

        case 4:
            lfo.PLFOtype = value;
            lfo.updateparams();
            barber = (2 == value);
            break;

        case 5:
            lfo.Pstereo = value;
            lfo.updateparams();
            break;

        case 6:
            setdepth(value);
            break;

        case 7:
            setfb(value);
            break;

        case 8:
            setstages(value);
            break;

        case 9:
            setlrcross(value);
            setoffset(value);
            break;

        case 10:
            Poutsub = (value > 1) ? 1 : value;
            break;

        case 11:
            setphase(value);
            setwidth(value);
            break;

        case 12:
            Phyper = std::min(int(value), 1);
            break;

        case 13:
            setdistortion(value);
            break;

        case 14:
            Panalog = value;
            break;

        case EFFECT::control::bpm:
            lfo.Pbpm = value;
            break;

        case EFFECT::control::bpmStart:
            lfo.PbpmStart = value;
            break;
    }
    Pchanged = true;
}

unsigned char Phaser::getpar(int npar)
{
    switch (npar)
    {
        case -1: return Pchanged;
        case 0:  return Pvolume;
        case 1:  return Ppanning;
        case 2:  return lfo.Pfreq;
        case 3:  return lfo.Prandomness;
        case 4:  return lfo.PLFOtype;
        case 5:  return lfo.Pstereo;
        case 6:  return Pdepth;
        case 7:  return Pfb;
        case 8:  return Pstages;
        case 9:  return Plrcross;
            return Poffset;      // same
        case 10: return Poutsub;
        case 11: return Pphase;
            return Pwidth;      // same
        case 12: return Phyper;
        case 13: return Pdistortion;
        case 14: return Panalog;
        case EFFECT::control::bpm: return lfo.Pbpm;
        case EFFECT::control::bpmStart: return lfo.PbpmStart;
        default: break;
    }
    return 0;
}

float Phaserlimit::getlimits(CommandBlock *getData)
{
    int value = getData->data.value;
    int control = getData->data.control;
    int request = getData->data.type & TOPLEVEL::type::Default; // clear flags
    int presetNum = getData->data.engine;
    int min = 0;
    int max = 127;

    int def = presets[presetNum][control];
    unsigned char canLearn = TOPLEVEL::type::Learnable;
    unsigned char isInteger = TOPLEVEL::type::Integer;
    switch (control)
    {
        case 0:
            break;
        case 1:
            break;
        case 2:
            break;
        case 3:
            break;
        case 4:
            max = 1;
            canLearn = 0;
            break;
        case 5:
            break;
        case 6:
            break;
        case 7:
            break;
        case 8:
            min = 1;
            max = 12;
            canLearn = 0;
            break;
        case 9:
            break;
        case 10:
            canLearn = 0;
            max = 1;
            break;
        case 11:
            break;
        case 12:
            canLearn = 0;
            max = 1;
            break;
        case 13:
            break;
        case 14:
            max = 1;
            canLearn = 0;
            break;
        case EFFECT::control::bpm:
            max = 1;
            canLearn = 0;
            break;
        case EFFECT::control::bpmStart:
            break;
        case EFFECT::control::preset:
            max = 11;
            canLearn = 0;
            break;
        default:
            getData->data.type |= TOPLEVEL::type::Error;
            return 1.0f;
            break;
    }

    switch (request)
    {
        case TOPLEVEL::type::Adjust:
            if (value < min)
                value = min;
            else if (value > max)
                value = max;
            break;
        case TOPLEVEL::type::Minimum:
            value = min;
            break;
        case TOPLEVEL::type::Maximum:
            value = max;
            break;
        case TOPLEVEL::type::Default:
            value = def;
            break;
    }
    getData->data.type |= (canLearn + isInteger);
    return float(value);
}


void Phaser::setdepth(unsigned char Pdepth_)
{
    Pdepth = Pdepth_;
    depth = Pdepth / 127.0f;
}

/*
    Phaser.cpp - Phaser effect

    Original ZynAddSubFX author Nasca Octavian Paul

    This file is a derivative of a ZynAddSubFX original.

*/

#include "Misc/SynthEngine.h"
#include "Effects/Phaser.h"
#include "Misc/NumericFuncs.h"

#include <algorithm>

using func::limit;
using func::invSignal;

#define PHASER_LFO_SHAPE 2
#define ONE_  0.99999f        // To prevent LFO ever reaching 1.0f for filter stability purposes
#define ZERO_ 0.00001f        // Same idea as above.

namespace {
    const int PRESET_SIZE = 15;
    const int NUM_PRESETS = 12;
    unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
        // Phaser
        {64, 64, 36,  0,   0, 64,  110, 64,  1,  0,   0, 20, 0, 0,  0 },
        {64, 64, 35,  0,   0, 88,  40,  64,  3,  0,   0, 20, 0,  0, 0 },
        {64, 64, 31,  0,   0, 66,  68,  107, 2,  0,   0, 20, 0,  0, 0 },
        {39, 64, 22,  0,   0, 66,  67,  10,  5,  0,   1, 20, 0,  0, 0 },
        {64, 64, 20,  0,   1, 110, 67,  78,  10, 0,   0, 20, 0,  0, 0 },
        {64, 64, 53,  100, 0, 58,  37,  78,  3,  0,   0, 20, 0,  0, 0 },
        // APhaser
        {64, 64, 14,  0,   1, 64,  64,  40,  4,  10,  0, 110,1,  20, 1 },
        {64, 64, 14,  5,   1, 64,  70,  40,  6,  10,  0, 110,1,  20, 1 },
        {64, 64, 9,   0,   0, 64,  60,  40,  8,  10,  0, 40, 0,  20, 1 },
        {64, 64, 14,  10,  0, 64,  45,  80,  7,  10,  1, 110,1,  20, 1 },
        {25, 64, 127, 10,  0, 64,  25,  16,  8,  100, 0, 25, 0,  20, 1 },
        {64, 64, 1,   10,  1, 64,  70,  40,  12, 10,  0, 110,1,  20, 1 }
    };
}

Phaser::Phaser(bool insertion_, float *efxoutl_, float *efxoutr_, SynthEngine *_synth) :
    Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, _synth),
    lfo(_synth),
    oldl(NULL),
    oldr(NULL),
    xn1l(NULL),
    xn1r(NULL),
    yn1l(NULL),
    yn1r(NULL)
{
    analog_setup();
    setpreset(Ppreset);
    Pchanged = false;
    cleanup();
}

void Phaser::analog_setup()
{
    //model mismatch between JFET devices
    offset[0]  = -0.2509303f;
    offset[1]  = 0.9408924f;
    offset[2]  = 0.998f;
    offset[3]  = -0.3486182f;
    offset[4]  = -0.2762545f;
    offset[5]  = -0.5215785f;
    offset[6]  = 0.2509303f;
    offset[7]  = -0.9408924f;
    offset[8]  = -0.998f;
    offset[9]  = 0.3486182f;
    offset[10] = 0.2762545f;
    offset[11] = 0.5215785f;

    barber = 0;  //Deactivate barber pole phasing by default

    mis       = 1.0f;
    Rmin      = 625.0f; // 2N5457 typical on resistance at Vgs = 0
    Rmax      = 22000.0f; // Resistor parallel to FET
    Rmx       = Rmin / Rmax;
    Rconst    = 1.0f + Rmx; // Handle parallel resistor relationship
    C         = 0.00000005f; // 50 nF
    CFs       = 2.0f * synth->samplerate_f * C;
    invperiod = 1.0f / synth->buffersize_f;
}

Phaser::~Phaser()
{
    if (oldl != NULL)
        delete [] oldl;
    if (oldr != NULL)
        delete [] oldr;

    if (xn1l)
        delete[] xn1l;
    if (yn1l)
        delete[] yn1l;
    if (xn1r)
        delete[] xn1r;
    if (yn1r)
        delete[] yn1r;
}

// Effect output
void Phaser::out(float *smpsl, float *smpsr)
{
    outvolume.advanceValue(synth->sent_buffersize);

    if (Panalog)
        AnalogPhase(smpsl, smpsr);
    else
        NormalPhase(smpsl, smpsr);
}

void Phaser::AnalogPhase(float *smpsl, float *smpsr)
{
    float lfoVall;
    float lfoValr;
    float modl;
    float modr;
    float gl;
    float gr;
    float hpfl = 0;
    float hpfr = 0;

    lfo.effectlfoout(&lfoVall, &lfoValr);
    modl = lfoVall * width + (depth - 0.5f);
    modr = lfoValr * width + (depth - 0.5f);

    modl = limit(modl, ZERO_, ONE_);
    modr = limit(modr, ZERO_, ONE_);

    if (Phyper)
    {
        // Triangle wave squared is approximately sine on bottom, triangle on top
        // Result is exponential sweep more akin to filter in synth with
        // exponential generator circuitry.
        modl *= modl;
        modr *= modr;
    }

    // g.,g. is Vp - Vgs. Typical FET drain-source resistance follows constant/[1-sqrt(Vp - Vgs)]
    modl = sqrtf(1.0f - modl);
    modr = sqrtf(1.0f - modr);

    diffr = (modr - oldrgain) * invperiod;
    diffl = (modl - oldlgain) * invperiod;

    gl = oldlgain;
    gr = oldrgain;
    oldlgain = modl;
    oldrgain = modr;

   for (int i = 0; i < synth->sent_buffersize; ++i)
   {
        gl += diffl; // Linear interpolation between LFO samples
        gr += diffr;

        float xnl(smpsl[i] * pangainL.getAndAdvanceValue());
        float xnr(smpsr[i] * pangainR.getAndAdvanceValue());

        if (barber)
        {
            gl = fmodf((gl + 0.25f), ONE_);
            gr = fmodf((gr + 0.25f), ONE_);
        }

        xnl = applyPhase(xnl, gl, fbl, hpfl, yn1l, xn1l);
        xnr = applyPhase(xnr, gr, fbr, hpfr, yn1r, xn1r);

        fbl = xnl * fb;
        fbr = xnr * fb;
        efxoutl[i] = xnl;
        efxoutr[i] = xnr;
    }

    if (Poutsub)
    {
        invSignal(efxoutl, synth->sent_buffersize);
        invSignal(efxoutr, synth->sent_buffersize);
    }
}

float Phaser::applyPhase(float x, float g, float fb,
                         float &hpf, float *yn1, float *xn1)
{
    for (int j = 0; j < Pstages; ++j)
    { //Phasing routine
        mis = 1.0f + offsetpct * offset[j];

        // This is symmetrical.
        // FET is not, so this deviates slightly, however sym dist. is
        // better sounding than a real FET.
        float d = (1.0f + 2.0f * (0.25f + g) * hpf * hpf * distortion) * mis;
        Rconst = 1.0f + mis * Rmx;

        // This is 1/R. R is being modulated to control filter fc.
        float b    = (Rconst - g) / (d * Rmin);
        float gain = (CFs - b) / (CFs + b);
        yn1[j] = (gain * (x + yn1[j]) - xn1[j]) + 1e-12; // anti-denormal

        // high pass filter:
        // Distortion depends on the high-pass part of the AP stage.
        hpf = yn1[j] + (1.0f - gain) * xn1[j];

        xn1[j] = x;
        x = yn1[j];
        if (j == 1)
            x += fb; // Insert feedback after first phase stage
    }
    return x;
}

void Phaser::NormalPhase(float *smpsl, float *smpsr)
{
    float lfol, lfor, lgain, rgain, tmp;

    lfo.effectlfoout(&lfol, &lfor);
    lgain = lfol;
    rgain = lfor;
    lgain = (expf(lgain * PHASER_LFO_SHAPE) - 1)
            / (expf(PHASER_LFO_SHAPE) - 1.0f);
    rgain = (expf(rgain * PHASER_LFO_SHAPE) - 1)
            / (expf(PHASER_LFO_SHAPE) - 1.0f);

    lgain = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * lgain * depth;
    lgain = limit(lgain,ZERO_,ONE_);//(lgain > 1.0f) ? 1.0f : lgain;
    rgain = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * rgain * depth;
    rgain = limit(rgain,ZERO_,ONE_);//(rgain > 1.0f) ? 1.0f : rgain;

    for (int i = 0; i < synth->sent_buffersize; ++i)
    {
        float x = (float)i / synth->sent_buffersize_f;
        float x1 = 1.0f - x;
        float gl = lgain * x + oldlgain * x1;
        float gr = rgain * x + oldrgain * x1;
        float inl = smpsl[i] * pangainL.getAndAdvanceValue() + fbl;
        float inr = smpsr[i] * pangainR.getAndAdvanceValue() + fbr;

        // Phasing routine
        for (int j = 0; j < Pstages * 2; ++j)
        {
            // Left channel
            tmp = oldl[j];
            oldl[j] = gl * tmp + inl;
            inl = (tmp - gl * oldl[j]) + 1e-12; // anti-denormal
            // Right channel
            tmp = oldr[j];
            oldr[j] = gr * tmp + inr;
            inr = (tmp - gr * oldr[j]) + 1e-12; // anti-denormal
        }

        // Left/Right crossing
        float l = inl;
        float r = inr;
        inl = l * (1.0f - lrcross.getValue()) + r * lrcross.getValue();
        inr = r * (1.0f - lrcross.getValue()) + l * lrcross.getValue();
        lrcross.advanceValue();
        fbl = inl * fb;
        fbr = inr * fb;
        efxoutl[i] = inl;
        efxoutr[i] = inr;
    }
    oldlgain = lgain;
    oldrgain = rgain;
    if (Poutsub)
        for (int i = 0; i < synth->sent_buffersize; ++i)
        {
            efxoutl[i] *= -1.0f;
            efxoutr[i] *= -1.0f;
        }
}

// Cleanup the effect
void Phaser::cleanup(void)
{
    fbl = fbr = oldlgain = oldrgain = 0.0f;
    memset(oldl, 0, sizeof(float)*Pstages * 2);
    memset(oldr, 0, sizeof(float)*Pstages * 2);
    memset(xn1l, 0, sizeof(float)*Pstages);
    memset(xn1r, 0, sizeof(float)*Pstages);
    memset(yn1l, 0, sizeof(float)*Pstages);
    memset(yn1r, 0, sizeof(float)*Pstages);
    lfo.resetState();
}

// Parameter control

void Phaser::setwidth(unsigned char Pwidth_)
{
    Pwidth = Pwidth_;
    width = Pwidth / 127.0f;
}

void Phaser::setfb(unsigned char Pfb_)
{
    Pfb = Pfb_;
    fb = (Pfb - 64.0f) / 64.1f;
}

void Phaser::setvolume(unsigned char Pvolume_)
{
    Pvolume = Pvolume_;
    float tmp = Pvolume / 127.0f;
    outvolume.setTargetValue(tmp);
    volume.setTargetValue((!insertion) ? 1.0f : tmp);
}

void Phaser::setdistortion(unsigned char Pdistortion_)
{
    Pdistortion = Pdistortion_;
    distortion = (float)Pdistortion / 127.0f;
}

void Phaser::setoffset(unsigned char Poffset_)
{
    Poffset = Poffset_;
    offsetpct     = (float)Poffset / 127.0f;
}

void Phaser::setstages(unsigned char Pstages_)
{
    if (oldl != NULL)
        delete [] oldl;
    if (xn1l)
        delete[] xn1l;
    if (yn1l)
        delete[] yn1l;
    if (oldr != NULL)
        delete [] oldr;
    if (xn1r)
        delete[] xn1r;
    if (yn1r)
        delete[] yn1r;

    Pstages = Pstages_;
    oldl = new float[Pstages * 2];
    oldr = new float[Pstages * 2];
    xn1l = new float[Pstages];
    xn1r = new float[Pstages];
    yn1l = new float[Pstages];
    yn1r = new float[Pstages];
    cleanup();
}

void Phaser::setphase(unsigned char Pphase_)
{
    Pphase = Pphase_;
    phase = Pphase / 127.0;
}

void Phaser::setpreset(unsigned char npreset)
{
    if (npreset < 0xf)
    {
        if (npreset >= NUM_PRESETS)
            npreset = NUM_PRESETS - 1;
        for (int n = 0; n < PRESET_SIZE; ++n)
            changepar(n, presets[npreset][n]);
        // All presets use no BPM syncing.
        changepar(EFFECT::control::bpm, 0);
        Ppreset = npreset;
    }
    else
    {
        unsigned char preset = npreset & 0xf;
        unsigned char param = npreset >> 4;
        if (param == 0xf)
            param = 0;
        changepar(param, presets[preset][param]);
    }
    Pchanged = false;
}

void Phaser::changepar(int npar, unsigned char value)
{
    if (npar == -1)
    {
        Pchanged = (value != 0);
        return;
    }
    switch (npar)
    {
        case 0:
            setvolume(value);
            break;

        case 1:
            setpanning(value);
            break;

        case 2:
            lfo.Pfreq = value;
            lfo.updateparams();
            break;

        case 3:
            lfo.Prandomness = value;
            lfo.updateparams();
            break;

        case 4:
            lfo.PLFOtype = value;
            lfo.updateparams();
            barber = (2 == value);
            break;

        case 5:
            lfo.Pstereo = value;
            lfo.updateparams();
            break;

        case 6:
            setdepth(value);
            break;

        case 7:
            setfb(value);
            break;

        case 8:
            setstages(value);
            break;

        case 9:
            setlrcross(value);
            setoffset(value);
            break;

        case 10:
            Poutsub = (value > 1) ? 1 : value;
            break;

        case 11:
            setphase(value);
            setwidth(value);
            break;

        case 12:
            Phyper = std::min(int(value), 1);
            break;

        case 13:
            setdistortion(value);
            break;

        case 14:
            Panalog = value;
            break;

        case EFFECT::control::bpm:
            lfo.Pbpm = value;
            break;

        case EFFECT::control::bpmStart:
            lfo.PbpmStart = value;
            break;
    }
    Pchanged = true;
}

unsigned char Phaser::getpar(int npar)
{
    switch (npar)
    {
        case -1: return Pchanged;
        case 0:  return Pvolume;
        case 1:  return Ppanning;
        case 2:  return lfo.Pfreq;
        case 3:  return lfo.Prandomness;
        case 4:  return lfo.PLFOtype;
        case 5:  return lfo.Pstereo;
        case 6:  return Pdepth;
        case 7:  return Pfb;
        case 8:  return Pstages;
        case 9:  return Plrcross;
            return Poffset;      // same
        case 10: return Poutsub;
        case 11: return Pphase;
            return Pwidth;      // same
        case 12: return Phyper;
        case 13: return Pdistortion;
        case 14: return Panalog;
        case EFFECT::control::bpm: return lfo.Pbpm;
        case EFFECT::control::bpmStart: return lfo.PbpmStart;
        default: break;
    }
    return 0;
}

float Phaserlimit::getlimits(CommandBlock *getData)
{
    int value = getData->data.value;
    int control = getData->data.control;
    int request = getData->data.type & TOPLEVEL::type::Default; // clear flags
    int presetNum = getData->data.engine;
    int min = 0;
    int max = 127;

    int def = presets[presetNum][control];
    unsigned char canLearn = TOPLEVEL::type::Learnable;
    unsigned char isInteger = TOPLEVEL::type::Integer;
    switch (control)
    {
        case 0:
            break;
        case 1:
            break;
        case 2:
            break;
        case 3:
            break;
        case 4:
            max = 1;
            canLearn = 0;
            break;
        case 5:
            break;
        case 6:
            break;
        case 7:
            break;
        case 8:
            min = 1;
            max = 12;
            canLearn = 0;
            break;
        case 9:
            break;
        case 10:
            canLearn = 0;
            max = 1;
            break;
        case 11:
            break;
        case 12:
            canLearn = 0;
            max = 1;
            break;
        case 13:
            break;
        case 14:
            max = 1;
            canLearn = 0;
            break;
        case EFFECT::control::bpm:
            max = 1;
            canLearn = 0;
            break;
        case EFFECT::control::bpmStart:
            break;
        case EFFECT::control::preset:
            max = 11;
            canLearn = 0;
            break;
        default:
            getData->data.type |= TOPLEVEL::type::Error;
            return 1.0f;
            break;
    }

    switch (request)
    {
        case TOPLEVEL::type::Adjust:
            if (value < min)
                value = min;
            else if (value > max)
                value = max;
            break;
        case TOPLEVEL::type::Minimum:
            value = min;
            break;
        case TOPLEVEL::type::Maximum:
            value = max;
            break;
        case TOPLEVEL::type::Default:
            value = def;
            break;
    }
    getData->data.type |= (canLearn + isInteger);
    return float(value);
}


switch (request)
    {
        case TOPLEVEL::type::Adjust:
            if (value < min)
                value = min;
            else if (value > max)
                value = max;
            break;
        case TOPLEVEL::type::Minimum:
            value = min;
            break;
        case TOPLEVEL::type::Maximum:
            value = max;
            break;
        case TOPLEVEL::type::Default:
            value = def;
            break;
    }

/*
    Phaser.cpp - Phaser effect

    Original ZynAddSubFX author Nasca Octavian Paul

    This file is a derivative of a ZynAddSubFX original.

*/

#include "Misc/SynthEngine.h"
#include "Effects/Phaser.h"
#include "Misc/NumericFuncs.h"

#include <algorithm>

using func::limit;
using func::invSignal;

#define PHASER_LFO_SHAPE 2
#define ONE_  0.99999f        // To prevent LFO ever reaching 1.0f for filter stability purposes
#define ZERO_ 0.00001f        // Same idea as above.

namespace {
    const int PRESET_SIZE = 15;
    const int NUM_PRESETS = 12;
    unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
        // Phaser
        {64, 64, 36,  0,   0, 64,  110, 64,  1,  0,   0, 20, 0, 0,  0 },
        {64, 64, 35,  0,   0, 88,  40,  64,  3,  0,   0, 20, 0,  0, 0 },
        {64, 64, 31,  0,   0, 66,  68,  107, 2,  0,   0, 20, 0,  0, 0 },
        {39, 64, 22,  0,   0, 66,  67,  10,  5,  0,   1, 20, 0,  0, 0 },
        {64, 64, 20,  0,   1, 110, 67,  78,  10, 0,   0, 20, 0,  0, 0 },
        {64, 64, 53,  100, 0, 58,  37,  78,  3,  0,   0, 20, 0,  0, 0 },
        // APhaser
        {64, 64, 14,  0,   1, 64,  64,  40,  4,  10,  0, 110,1,  20, 1 },
        {64, 64, 14,  5,   1, 64,  70,  40,  6,  10,  0, 110,1,  20, 1 },
        {64, 64, 9,   0,   0, 64,  60,  40,  8,  10,  0, 40, 0,  20, 1 },
        {64, 64, 14,  10,  0, 64,  45,  80,  7,  10,  1, 110,1,  20, 1 },
        {25, 64, 127, 10,  0, 64,  25,  16,  8,  100, 0, 25, 0,  20, 1 },
        {64, 64, 1,   10,  1, 64,  70,  40,  12, 10,  0, 110,1,  20, 1 }
    };
}

Phaser::Phaser(bool insertion_, float *efxoutl_, float *efxoutr_, SynthEngine *_synth) :
    Effect(insertion_, efxoutl_, efxoutr_, NULL, 0, _synth),
    lfo(_synth),
    oldl(NULL),
    oldr(NULL),
    xn1l(NULL),
    xn1r(NULL),
    yn1l(NULL),
    yn1r(NULL)
{
    analog_setup();
    setpreset(Ppreset);
    Pchanged = false;
    cleanup();
}

void Phaser::analog_setup()
{
    //model mismatch between JFET devices
    offset[0]  = -0.2509303f;
    offset[1]  = 0.9408924f;
    offset[2]  = 0.998f;
    offset[3]  = -0.3486182f;
    offset[4]  = -0.2762545f;
    offset[5]  = -0.5215785f;
    offset[6]  = 0.2509303f;
    offset[7]  = -0.9408924f;
    offset[8]  = -0.998f;
    offset[9]  = 0.3486182f;
    offset[10] = 0.2762545f;
    offset[11] = 0.5215785f;

    barber = 0;  //Deactivate barber pole phasing by default

    mis       = 1.0f;
    Rmin      = 625.0f; // 2N5457 typical on resistance at Vgs = 0
    Rmax      = 22000.0f; // Resistor parallel to FET
    Rmx       = Rmin / Rmax;
    Rconst    = 1.0f + Rmx; // Handle parallel resistor relationship
    C         = 0.00000005f; // 50 nF
    CFs       = 2.0f * synth->samplerate_f * C;
    invperiod = 1.0f / synth->buffersize_f;
}

Phaser::~Phaser()
{
    if (oldl != NULL)
        delete [] oldl;
    if (oldr != NULL)
        delete [] oldr;

    if (xn1l)
        delete[] xn1l;
    if (yn1l)
        delete[] yn1l;
    if (xn1r)
        delete[] xn1r;
    if (yn1r)
        delete[] yn1r;
}

// Effect output
void Phaser::out(float *smpsl, float *smpsr)
{
    outvolume.advanceValue(synth->sent_buffersize);

    if (Panalog)
        AnalogPhase(smpsl, smpsr);
    else
        NormalPhase(smpsl, smpsr);
}

void Phaser::AnalogPhase(float *smpsl, float *smpsr)
{
    float lfoVall;
    float lfoValr;
    float modl;
    float modr;
    float gl;
    float gr;
    float hpfl = 0;
    float hpfr = 0;

    lfo.effectlfoout(&lfoVall, &lfoValr);
    modl = lfoVall * width + (depth - 0.5f);
    modr = lfoValr * width + (depth - 0.5f);

    modl = limit(modl, ZERO_, ONE_);
    modr = limit(modr, ZERO_, ONE_);

    if (Phyper)
    {
        // Triangle wave squared is approximately sine on bottom, triangle on top
        // Result is exponential sweep more akin to filter in synth with
        // exponential generator circuitry.
        modl *= modl;
        modr *= modr;
    }

    // g.,g. is Vp - Vgs. Typical FET drain-source resistance follows constant/[1-sqrt(Vp - Vgs)]
    modl = sqrtf(1.0f - modl);
    modr = sqrtf(1.0f - modr);

    diffr = (modr - oldrgain) * invperiod;
    diffl = (modl - oldlgain) * invperiod;

    gl = oldlgain;
    gr = oldrgain;
    oldlgain = modl;
    oldrgain = modr;

   for (int i = 0; i < synth->sent_buffersize; ++i)
   {
        gl += diffl; // Linear interpolation between LFO samples
        gr += diffr;

        float xnl(smpsl[i] * pangainL.getAndAdvanceValue());
        float xnr(smpsr[i] * pangainR.getAndAdvanceValue());

        if (barber)
        {
            gl = fmodf((gl + 0.25f), ONE_);
            gr = fmodf((gr + 0.25f), ONE_);
        }

        xnl = applyPhase(xnl, gl, fbl, hpfl, yn1l, xn1l);
        xnr = applyPhase(xnr, gr, fbr, hpfr, yn1r, xn1r);

        fbl = xnl * fb;
        fbr = xnr * fb;
        efxoutl[i] = xnl;
        efxoutr[i] = xnr;
    }

    if (Poutsub)
    {
        invSignal(efxoutl, synth->sent_buffersize);
        invSignal(efxoutr, synth->sent_buffersize);
    }
}

float Phaser::applyPhase(float x, float g, float fb,
                         float &hpf, float *yn1, float *xn1)
{
    for (int j = 0; j < Pstages; ++j)
    { //Phasing routine
        mis = 1.0f + offsetpct * offset[j];

        // This is symmetrical.
        // FET is not, so this deviates slightly, however sym dist. is
        // better sounding than a real FET.
        float d = (1.0f + 2.0f * (0.25f + g) * hpf * hpf * distortion) * mis;
        Rconst = 1.0f + mis * Rmx;

        // This is 1/R. R is being modulated to control filter fc.
        float b    = (Rconst - g) / (d * Rmin);
        float gain = (CFs - b) / (CFs + b);
        yn1[j] = (gain * (x + yn1[j]) - xn1[j]) + 1e-12; // anti-denormal

        // high pass filter:
        // Distortion depends on the high-pass part of the AP stage.
        hpf = yn1[j] + (1.0f - gain) * xn1[j];

        xn1[j] = x;
        x = yn1[j];
        if (j == 1)
            x += fb; // Insert feedback after first phase stage
    }
    return x;
}

void Phaser::NormalPhase(float *smpsl, float *smpsr)
{
    float lfol, lfor, lgain, rgain, tmp;

    lfo.effectlfoout(&lfol, &lfor);
    lgain = lfol;
    rgain = lfor;
    lgain = (expf(lgain * PHASER_LFO_SHAPE) - 1)
            / (expf(PHASER_LFO_SHAPE) - 1.0f);
    rgain = (expf(rgain * PHASER_LFO_SHAPE) - 1)
            / (expf(PHASER_LFO_SHAPE) - 1.0f);

    lgain = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * lgain * depth;
    lgain = limit(lgain,ZERO_,ONE_);//(lgain > 1.0f) ? 1.0f : lgain;
    rgain = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * rgain * depth;
    rgain = limit(rgain,ZERO_,ONE_);//(rgain > 1.0f) ? 1.0f : rgain;

    for (int i = 0; i < synth->sent_buffersize; ++i)
    {
        float x = (float)i / synth->sent_buffersize_f;
        float x1 = 1.0f - x;
        float gl = lgain * x + oldlgain * x1;
        float gr = rgain * x + oldrgain * x1;
        float inl = smpsl[i] * pangainL.getAndAdvanceValue() + fbl;
        float inr = smpsr[i] * pangainR.getAndAdvanceValue() + fbr;

        // Phasing routine
        for (int j = 0; j < Pstages * 2; ++j)
        {
            // Left channel
            tmp = oldl[j];
            oldl[j] = gl * tmp + inl;
            inl = (tmp - gl * oldl[j]) + 1e-12; // anti-denormal
            // Right channel
            tmp = oldr[j];
            oldr[j] = gr * tmp + inr;
            inr = (tmp - gr * oldr[j]) + 1e-12; // anti-denormal
        }

        // Left/Right crossing
        float l = inl;
        float r = inr;
        inl = l * (1.0f - lrcross.getValue()) + r * lrcross.getValue();
        inr = r * (1.0f - lrcross.getValue()) + l * lrcross.getValue();
        lrcross.advanceValue();
        fbl = inl * fb;
        fbr = inr * fb;
        efxoutl[i] = inl;
        efxoutr[i] = inr;
    }
    oldlgain = lgain;
    oldrgain = rgain;
    if (Poutsub)
        for (int i = 0; i < synth->sent_buffersize; ++i)
        {
            efxoutl[i] *= -1.0f;
            efxoutr[i] *= -1.0f;
        }
}

// Cleanup the effect
void Phaser::cleanup(void)
{
    fbl = fbr = oldlgain = oldrgain = 0.0f;
    memset(oldl, 0, sizeof(float)*Pstages * 2);
    memset(oldr, 0, sizeof(float)*Pstages * 2);
    memset(xn1l, 0, sizeof(float)*Pstages);
    memset(xn1r, 0, sizeof(float)*Pstages);
    memset(yn1l, 0, sizeof(float)*Pstages);
    memset(yn1r, 0, sizeof(float)*Pstages);
    lfo.resetState();
}

// Parameter control
void Phaser::setdepth(unsigned char Pdepth_)
{
    Pdepth = Pdepth_;
    depth = Pdepth / 127.0f;
}

void Phaser::setwidth(unsigned char Pwidth_)
{
    Pwidth = Pwidth_;
    width = Pwidth / 127.0f;
}

void Phaser::setfb(unsigned char Pfb_)
{
    Pfb = Pfb_;
    fb = (Pfb - 64.0f) / 64.1f;
}

void Phaser::setvolume(unsigned char Pvolume_)
{
    Pvolume = Pvolume_;
    float tmp = Pvolume / 127.0f;
    outvolume.setTargetValue(tmp);
    volume.setTargetValue((!insertion) ? 1.0f : tmp);
}

void Phaser::setdistortion(unsigned char Pdistortion_)
{
    Pdistortion = Pdistortion_;
    distortion = (float)Pdistortion / 127.0f;
}

void Phaser::setoffset(unsigned char Poffset_)
{
    Poffset = Poffset_;
    offsetpct     = (float)Poffset / 127.0f;
}

void Phaser::setstages(unsigned char Pstages_)
{
    if (oldl != NULL)
        delete [] oldl;
    if (xn1l)
        delete[] xn1l;
    if (yn1l)
        delete[] yn1l;
    if (oldr != NULL)
        delete [] oldr;
    if (xn1r)
        delete[] xn1r;
    if (yn1r)
        delete[] yn1r;

    Pstages = Pstages_;
    oldl = new float[Pstages * 2];
    oldr = new float[Pstages * 2];
    xn1l = new float[Pstages];
    xn1r = new float[Pstages];
    yn1l = new float[Pstages];
    yn1r = new float[Pstages];
    cleanup();
}

void Phaser::setphase(unsigned char Pphase_)
{
    Pphase = Pphase_;
    phase = Pphase / 127.0;
}

void Phaser::setpreset(unsigned char npreset)
{
    if (npreset < 0xf)
    {
        if (npreset >= NUM_PRESETS)
            npreset = NUM_PRESETS - 1;
        for (int n = 0; n < PRESET_SIZE; ++n)
            changepar(n, presets[npreset][n]);
        // All presets use no BPM syncing.
        changepar(EFFECT::control::bpm, 0);
        Ppreset = npreset;
    }
    else
    {
        unsigned char preset = npreset & 0xf;
        unsigned char param = npreset >> 4;
        if (param == 0xf)
            param = 0;
        changepar(param, presets[preset][param]);
    }
    Pchanged = false;
}

void Phaser::changepar(int npar, unsigned char value)
{
    if (npar == -1)
    {
        Pchanged = (value != 0);
        return;
    }
    switch (npar)
    {
        case 0:
            setvolume(value);
            break;

        case 1:
            setpanning(value);
            break;

        case 2:
            lfo.Pfreq = value;
            lfo.updateparams();
            break;

        case 3:
            lfo.Prandomness = value;
            lfo.updateparams();
            break;

        case 4:
            lfo.PLFOtype = value;
            lfo.updateparams();
            barber = (2 == value);
            break;

        case 5:
            lfo.Pstereo = value;
            lfo.updateparams();
            break;

        case 6:
            setdepth(value);
            break;

        case 7:
            setfb(value);
            break;

        case 8:
            setstages(value);
            break;

        case 9:
            setlrcross(value);
            setoffset(value);
            break;

        case 10:
            Poutsub = (value > 1) ? 1 : value;
            break;

        case 11:
            setphase(value);
            setwidth(value);
            break;

        case 12:
            Phyper = std::min(int(value), 1);
            break;

        case 13:
            setdistortion(value);
            break;

        case 14:
            Panalog = value;
            break;

        case EFFECT::control::bpm:
            lfo.Pbpm = value;
            break;

        case EFFECT::control::bpmStart:
            lfo.PbpmStart = value;
            break;
    }
    Pchanged = true;
}

unsigned char Phaser::getpar(int npar)
{
    switch (npar)
    {
        case -1: return Pchanged;
        case 0:  return Pvolume;
        case 1:  return Ppanning;
        case 2:  return lfo.Pfreq;
        case 3:  return lfo.Prandomness;
        case 4:  return lfo.PLFOtype;
        case 5:  return lfo.Pstereo;
        case 6:  return Pdepth;
        case 7:  return Pfb;
        case 8:  return Pstages;
        case 9:  return Plrcross;
            return Poffset;      // same
        case 10: return Poutsub;
        case 11: return Pphase;
            return Pwidth;      // same
        case 12: return Phyper;
        case 13: return Pdistortion;
        case 14: return Panalog;
        case EFFECT::control::bpm: return lfo.Pbpm;
        case EFFECT::control::bpmStart: return lfo.PbpmStart;
        default: break;
    }
    return 0;
}

float Phaserlimit::getlimits(CommandBlock *getData)
{
    int value = getData->data.value;
    int control = getData->data.control;
    int request = getData->data.type & TOPLEVEL::type::Default; // clear flags
    int presetNum = getData->data.engine;
    int min = 0;
    int max = 127;

    int def = presets[presetNum][control];
    unsigned char canLearn = TOPLEVEL::type::Learnable;
    unsigned char isInteger = TOPLEVEL::type::Integer;
    switch (control)
    {
        case 0:
            break;
        case 1:
            break;
        case 2:
            break;
        case 3:
            break;
        case 4:
            max = 1;
            canLearn = 0;
            break;
        case 5:
            break;
        case 6:
            break;
        case 7:
            break;
        case 8:
            min = 1;
            max = 12;
            canLearn = 0;
            break;
        case 9:
            break;
        case 10:
            canLearn = 0;
            max = 1;
            break;
        case 11:
            break;
        case 12:
            canLearn = 0;
            max = 1;
            break;
        case 13:
            break;
        case 14:
            max = 1;
            canLearn = 0;
            break;
        case EFFECT::control::bpm:
            max = 1;
            canLearn = 0;
            break;
        case EFFECT::control::bpmStart:
            break;
        case EFFECT::control::preset:
            max = 11;
            canLearn = 0;
            break;
        default:
            getData->data.type |= TOPLEVEL::type::Error;
            return 1.0f;
            break;
    }

    getData->data.type |= (canLearn + isInteger);
    return float(value);
}


END_SECTION

START_SECTION((template < typename PeakType > void pick(const MSSpectrum &input, MSSpectrum &output, float fWindowWidth)))
{
  // TODO
}
END_SECTION

START_SECTION((void pickExperiment(const PeakMap &input, PeakMap &output)))

//                   OpenMS -- Open-Source Mass Spectrometry

#include <OpenMS/CONCEPT/ClassTest.h>

#include <OpenMS/TRANSFORMATIONS/RAW2PEAK/PeakPickerSH.h>

using namespace OpenMS;
using namespace std;

START_TEST(PeakPickerSH, "$Id$")

PeakPickerSH* ptr = nullptr;
PeakPickerSH* null_ptr = nullptr;
START_SECTION(PeakPickerSH())
{
	ptr = new PeakPickerSH();
	TEST_NOT_EQUAL(ptr, null_ptr)
}
END_SECTION

START_SECTION(~PeakPickerSH())
{
	delete ptr;
}
END_SECTION

START_SECTION((virtual ~PeakPickerSH()))
{
  // TODO
}


//                   OpenMS -- Open-Source Mass Spectrometry

#include <OpenMS/CONCEPT/ClassTest.h>

#include <OpenMS/TRANSFORMATIONS/RAW2PEAK/PeakPickerSH.h>

using namespace OpenMS;
using namespace std;

START_TEST(PeakPickerSH, "$Id$")

PeakPickerSH* ptr = nullptr;
PeakPickerSH* null_ptr = nullptr;
START_SECTION(PeakPickerSH())
{
	ptr = new PeakPickerSH();
	TEST_NOT_EQUAL(ptr, null_ptr)
}
END_SECTION

START_SECTION(~PeakPickerSH())
{
	delete ptr;
}
END_SECTION

START_SECTION((virtual ~PeakPickerSH()))
{
  // TODO
}
END_SECTION

START_SECTION((template < typename PeakType > void pick(const MSSpectrum &input, MSSpectrum &output, float fWindowWidth)))
{
  // TODO
}
END_SECTION

START_SECTION((void pickExperiment(const PeakMap &input, PeakMap &output)))


//                   OpenMS -- Open-Source Mass Spectrometry

#include <OpenMS/CONCEPT/ClassTest.h>

#include <OpenMS/TRANSFORMATIONS/RAW2PEAK/PeakPickerSH.h>

using namespace OpenMS;
using namespace std;

START_TEST(PeakPickerSH, "$Id$")

PeakPickerSH* ptr = nullptr;
PeakPickerSH* null_ptr = nullptr;
START_SECTION(PeakPickerSH())
{
	ptr = new PeakPickerSH

	TEST_NOT_EQUAL(ptr, null_ptr)
}
END_SECTION

START_SECTION(~PeakPickerSH())
{
	delete ptr;
}
END_SECTION

START_SECTION((virtual ~PeakPickerSH()))
{
  // TODO
}
END_SECTION

START_SECTION((template < typename PeakType > void pick(const MSSpectrum &input, MSSpectrum &output, float fWindowWidth)))
{
  // TODO
}
END_SECTION

START_SECTION((void pickExperiment(const PeakMap &input, PeakMap &output)))
{
  // TODO
}
END_SECTION

END_TEST


/*
 */

#include <gtest/gtest.h>
#include "can.hpp"
#include <uavcan/driver/can.hpp>

{
    EXPECT_TRUE(makeCanFrame(0, "", EXT).isExtended());
    EXPECT_FALSE(makeCanFrame(0, "", STD).isExtended());
    EXPECT_FALSE(makeCanFrame(0, "", EXT).isRemoteTransmissionRequest());
    EXPECT_FALSE(makeCanFrame(0, "", STD).isRemoteTransmissionRequest());

    uavcan::CanFrame frame = makeCanFrame(123, "", STD);
    frame.id |= uavcan::CanFrame::FlagRTR;
    EXPECT_TRUE(frame.isRemoteTransmissionRequest());
    EXPECT_FALSE(frame.isErrorFrame());
    frame.id |= uavcan::CanFrame::FlagERR;
    EXPECT_TRUE(frame.isErrorFrame());
}

TEST(CanFrame, Arbitration)
{
    using uavcan::CanFrame;

    CanFrame a;
    CanFrame b;

    /*
     * Simple
     */
    a.id = 123;
    b.id = 122;
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));

    a.id = 123 | CanFrame::FlagEFF;
    b.id = 122 | CanFrame::FlagEFF;
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));

    a.id = 8;
    b.id = 8;
    ASSERT_FALSE(a.priorityLowerThan(b));
    ASSERT_FALSE(b.priorityHigherThan(a));

    /*
     * EXT vs STD
     */
    a.id = 1000;                      // 1000
    b.id = 2000 | CanFrame::FlagEFF;  // 2000 >> 18, wins
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));

    a.id = 0x400;
    b.id = 0x10000000 | CanFrame::FlagEFF; // (0x400 << 18), 11 most significant bits are the same --> EFF loses
    ASSERT_TRUE(a.priorityHigherThan(b));
    ASSERT_TRUE(b.priorityLowerThan(a));

    /*
     * RTR vs Data
     */
    a.id = 123 | CanFrame::FlagRTR;      // On the same ID, RTR loses
    b.id = 123;
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));

    a.id = CanFrame::MaskStdID | CanFrame::FlagRTR;  // RTR is STD, so it wins
    b.id = CanFrame::MaskExtID | CanFrame::FlagEFF;  // Lowest possible priority for data frame
    ASSERT_TRUE(a.priorityHigherThan(b));
    ASSERT_TRUE(b.priorityLowerThan(a));

    a.id = 123 | CanFrame::FlagRTR;      // Both RTR arbitrate as usually
    b.id = 122 | CanFrame::FlagRTR;
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));
}

TEST(CanFrame, ToString)
{
    uavcan::CanFrame frame = makeCanFrame(123, "\x01\x02\x03\x04" "1234", EXT);
    EXPECT_EQ("0x0000007b   01 02 03 04 31 32 33 34  '....1234'", frame.toString());
    EXPECT_TRUE(frame.toString() == frame.toString(uavcan::CanFrame::StrAligned));

    frame = makeCanFrame(123, "z", EXT);
    EXPECT_EQ("0x0000007b   7a                       'z'", frame.toString(uavcan::CanFrame::StrAligned));
    EXPECT_EQ("0x0000007b   7a  'z'", frame.toString());

    EXPECT_EQ("     0x141   61 62 63 64 aa bb cc dd  'abcd....'",
              makeCanFrame(321, "abcd" "\xaa\xbb\xcc\xdd", STD).toString(uavcan::CanFrame::StrAligned));

    EXPECT_EQ("     0x100                            ''",
              makeCanFrame(256, "", STD).toString(uavcan::CanFrame::StrAligned));

    EXPECT_EQ("0x100    ''",
              makeCanFrame(256, "", STD).toString());

    EXPECT_EQ("0x141   61 62 63 64 aa bb cc dd  'abcd....'",
              makeCanFrame(321, "abcd" "\xaa\xbb\xcc\xdd", STD).toString());
}


/*
 */

#include <gtest/gtest.h>
#include "can.hpp"
#include <uavcan/driver/can.hpp>

TEST(CanFrame, FrameProperties)
{
    EXPECT_TRUE(makeCanFrame(0, "", EXT).isExtended());
    EXPECT_FALSE(makeCanFrame(0, "", STD).isExtended());
    EXPECT_FALSE(makeCanFrame(0, "", EXT).isRemoteTransmissionRequest());
    EXPECT_FALSE(makeCanFrame(0, "", STD).isRemoteTransmissionRequest(

    uavcan::CanFrame frame = makeCanFrame(123, "", STD);
    frame.id |= uavcan::CanFrame::FlagRTR;
    EXPECT_TRUE(frame.isRemoteTransmissionRequest());
    EXPECT_FALSE(frame.isErrorFrame());
    frame.id |= uavcan::CanFrame::FlagERR;
    EXPECT_TRUE(frame.isErrorFrame());
}

TEST(CanFrame, Arbitration)
{
    using uavcan::CanFrame;

    CanFrame a;
    CanFrame b;

    /*
     * Simple
     */
    a.id = 123;
    b.id = 122;
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));

    a.id = 123 | CanFrame::FlagEFF;
    b.id = 122 | CanFrame::FlagEFF;
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));

    a.id = 8;
    b.id = 8;
    ASSERT_FALSE(a.priorityLowerThan(b));
    ASSERT_FALSE(b.priorityHigherThan(a));

    /*
     * EXT vs STD
     */
    a.id = 1000;                      // 1000
    b.id = 2000 | CanFrame::FlagEFF;  // 2000 >> 18, wins
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));

    a.id = 0x400;
    b.id = 0x10000000 | CanFrame::FlagEFF; // (0x400 << 18), 11 most significant bits are the same --> EFF loses
    ASSERT_TRUE(a.priorityHigherThan(b));
    ASSERT_TRUE(b.priorityLowerThan(a));

    /*
     * RTR vs Data
     */
    a.id = 123 | CanFrame::FlagRTR;      // On the same ID, RTR loses
    b.id = 123;
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));

    a.id = CanFrame::MaskStdID | CanFrame::FlagRTR;  // RTR is STD, so it wins
    b.id = CanFrame::MaskExtID | CanFrame::FlagEFF;  // Lowest possible priority for data frame
    ASSERT_TRUE(a.priorityHigherThan(b));
    ASSERT_TRUE(b.priorityLowerThan(a));

    a.id = 123 | CanFrame::FlagRTR;      // Both RTR arbitrate as usually
    b.id = 122 | CanFrame::FlagRTR;
    ASSERT_TRUE(a.priorityLowerThan(b));
    ASSERT_TRUE(b.priorityHigherThan(a));
}

TEST(CanFrame, ToString)
{
    uavcan::CanFrame frame = makeCanFrame(123, "\x01\x02\x03\x04" "1234", EXT);
    EXPECT_EQ("0x0000007b   01 02 03 04 31 32 33 34  '....1234'", frame.toString());
    EXPECT_TRUE(frame.toString() == frame.toString(uavcan::CanFrame::StrAligned));

    frame = makeCanFrame(123, "z", EXT);
    EXPECT_EQ("0x0000007b   7a                       'z'", frame.toString(uavcan::CanFrame::StrAligned));
    EXPECT_EQ("0x0000007b   7a  'z'", frame.toString());

    EXPECT_EQ("     0x141   61 62 63 64 aa bb cc dd  'abcd....'",
              makeCanFrame(321, "abcd" "\xaa\xbb\xcc\xdd", STD).toString(uavcan::CanFrame::StrAligned));

    EXPECT_EQ("     0x100                            ''",
              makeCanFrame(256, "", STD).toString(uavcan::CanFrame::StrAligned));

    EXPECT_EQ("0x100    ''",
              makeCanFrame(256, "", STD).toString());

    EXPECT_EQ("0x141   61 62 63 64 aa bb cc dd  'abcd....'",
              makeCanFrame(321, "abcd" "\xaa\xbb\xcc\xdd", STD).toString());
}


NeonFillWorkload::NeonFillWorkload(const FillQueueDescriptor& descriptor, const WorkloadInfo& info)
    : BaseWorkload<FillQueueDescriptor>(descriptor, info)

#include "NeonFillWorkload.hpp"

#include <neon/NeonTensorHandle.hpp>
#include <aclCommon/ArmComputeTensorUtils.hpp>
#include <arm_compute/core/Types.h>
#include <arm_compute/runtime/NEON/functions/NEFill.h>

#include "NeonWorkloadUtils.hpp"

namespace armnn
{
using namespace armcomputetensorutils;

{
    // Report Profiling Details
    ARMNN_REPORT_PROFILING_WORKLOAD_DESC("NeonFillWorkload_Construct",
                                         descriptor.m_Parameters,
                                         info,
                                         this->GetGuid());

    m_Data.ValidateInputsOutputs("NeonFillWorkload", 1, 1);

    arm_compute::ITensor& output = static_cast<IAclTensorHandle*>(m_Data.m_Outputs[0])->GetTensor();
    arm_compute::PixelValue pixelValue = GetPixelValue(output, descriptor.m_Parameters.m_Value);

    auto layer = std::make_unique<arm_compute::NEFill>();
    layer->configure(&output, pixelValue);
    m_Layer.reset(layer.release());
}

void NeonFillWorkload::Execute() const
{
    ARMNN_SCOPED_PROFILING_EVENT_NEON_GUID("NeonFillWorkload_Execute", this->GetGuid());
    m_Layer->run();
}

} // namespace armnn


{
    ARMNN_SCOPED_PROFILING_EVENT_NEON_GUID("NeonFillWorkload_Execute", this->GetGuid());
    m_Layer->run();
}

#include "NeonFillWorkload.hpp"

#include <neon/NeonTensorHandle.hpp>
#include <aclCommon/ArmComputeTensorUtils.hpp>
#include <arm_compute/core/Types.h>
#include <arm_compute/runtime/NEON/functions/NEFill.h>

#include "NeonWorkloadUtils.hpp"

namespace armnn
{
using namespace armcomputetensorutils;

NeonFillWorkload::NeonFillWorkload(const FillQueueDescriptor& descriptor, const WorkloadInfo& info)
    : BaseWorkload<FillQueueDescriptor>(descriptor, info)
{
    // Report Profiling Details
    ARMNN_REPORT_PROFILING_WORKLOAD_DESC("NeonFillWorkload_Construct",
                                         descriptor.m_Parameters,
                                         info,
                                         this->GetGuid());

    m_Data.ValidateInputsOutputs("NeonFillWorkload", 1, 1);

    arm_compute::ITensor& output = static_cast<IAclTensorHandle*>(m_Data.m_Outputs[0])->GetTensor();
    arm_compute::PixelValue pixelValue = GetPixelValue(output, descriptor.m_Parameters.m_Value);

    auto layer = std::make_unique<arm_compute::NEFill>();
    layer->configure(&output, pixelValue);
    m_Layer.reset(layer.release());
}

void NeonFillWorkload::Execute() const


#include "NeonFillWorkload.hpp"

#include <neon/NeonTensorHandle.hpp>
#include <aclCommon/ArmComputeTensorUtils.hpp>
#include <arm_compute/core/Types.h>
#include <arm_compute/runtime/NEON/functions/NEFill.h>

#include "NeonWorkloadUtils.hpp"

namespace armnn
{
using namespace armcomputetensorutils;

NeonFillWorkload::NeonFillWorkload(const FillQueueDescriptor& descriptor, const WorkloadInfo& info)
    : BaseWorkload<FillQueueDescriptor>(descriptor, info)
{
    // Report Profiling Details
    ARMNN_REPORT_PROFILING_WORKLOAD_DESC("NeonFillWorkload_Construct",
                                         descriptor.m_Parameters,
                                         info,
                                         this->GetGuid());

    m_Data.ValidateInputsOutputs("NeonFillWorkload", 1, 1);

    arm_compute::ITensor& output = static_cast<IAclTensorHandle*>(m_Data.m_Outputs[0])->GetTensor();
    arm_compute::PixelValue pixelValue = GetPixelValue(output, descriptor.m_Parameters.m_Value);

    auto layer = std::make_uniqu

    layer->configure(&output, pixelValue);
    m_Layer.reset(layer.release());
}

void NeonFillWorkload::Execute() const
{
    ARMNN_SCOPED_PROFILING_EVENT_NEON_GUID("NeonFillWorkload_Execute", this->GetGuid());
    m_Layer->run();
}

} // namespace armnn


{
    // TODO: 在此添加专用代码和/或调用基类

    CDialogEx::OnOK();
}

#include "stdafx.h"
#include "TabDlg.h"
#include "MusicPlayer2.h"

IMPLEMENT_DYNAMIC(CTabDlg, CDialogEx)

CTabDlg::CTabDlg(UINT nIDTemplate, CWnd * pParent) : CDialogEx(nIDTemplate, pParent)
{
	m_pParent = pParent;
}

CTabDlg::~CTabDlg()
{
}

BOOL CTabDlg::PreTranslateMessage(MSG* pMsg)
{
	// TODO: 在此添加专用代码和/或调用基类
	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
	{
		return TRUE;
	}
	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
	{
		return TRUE;
	}

	return CDialogEx::PreTranslateMessage(pMsg);
}

BOOL CTabDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO:  在此添加额外的初始化
	//将窗口背景设置成白色
	SetBackgroundColor(RGB(255, 255, 255));

	return TRUE;  // return TRUE unless you set the focus to a control
				  // 异常: OCX 属性页应返回 FALSE
}

CWnd* CTabDlg::GetParentWindow()
{
    CWnd* pParent = GetParent();
    if (pParent != nullptr)
    {
        return pParent->GetParent();
    }
    return nullptr;
}

void CTabDlg::SetScrollbarInfo(int nPage, int nMax)
{
	//初始化滚动条
	SCROLLINFO scrollinfo;
	GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
	scrollinfo.nPage = nPage;    //设置滑块大小
	scrollinfo.nMin = 0;
	scrollinfo.nMax = nMax;     //设置滚动条的最大位置
	if (scrollinfo.nMax < 0)
		scrollinfo.nMax = 0;
	scrollinfo.nPos = scrollinfo.nMin;
	SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);

    m_scroll_enable = true;
}

void CTabDlg::ResetScroll()
{
	if (m_scroll_enable)
	{
		SCROLLINFO scrollinfo;
		GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
		int step = scrollinfo.nPos - scrollinfo.nMin;
		scrollinfo.nPos = scrollinfo.nMin;
		SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
		ScrollWindow(0, step);
	}
}

void CTabDlg::ScrollWindowSimple(int step)
{
	SCROLLINFO scrollinfo;
	GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
	scrollinfo.nPos -= step;
	if (scrollinfo.nPos < scrollinfo.nMin)
	{
		step = scrollinfo.nPos + step - scrollinfo.nMin;		//如果向上滚动一个距离后小于滚动条的最小位置了，则修正step的值，使窗口滚动到最上方
		scrollinfo.nPos = scrollinfo.nMin;
	}
	if (scrollinfo.nPos + scrollinfo.nPage > scrollinfo.nMax)  //此处一定要注意加上滑块的长度，再作判断
	{
		step -= (scrollinfo.nMax - (scrollinfo.nPos + scrollinfo.nPage));		//如果向上滚动一个距离后大于滚动条的最大位置了，则修正step的值，使窗口滚动到最下方
		if (step > 0)
			step = 0;
		scrollinfo.nPos = scrollinfo.nMax;
	}
	SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
	ScrollWindow(0, step);

}

void CTabDlg::EnableDlgCtrl(UINT id, bool enable)
{
    CWnd* pCtrl = GetDlgItem(id);
    if (pCtrl != nullptr)
        pCtrl->EnableWindow(enable);
}

void CTabDlg::ShowDlgCtrl(UINT id, bool show)
{
    CWnd* pCtrl = GetDlgItem(id);
    if (pCtrl != nullptr)
        pCtrl->ShowWindow(show);
}

BEGIN_MESSAGE_MAP(CTabDlg, CDialogEx)
	ON_WM_VSCROLL()
	ON_WM_MOUSEWHEEL()
END_MESSAGE_MAP()

void CTabDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	//参考资料：http://example.com

	if (m_scroll_enable)
	{
		SCROLLINFO scrollinfo;
		GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
		int unit = 1;
		int step = theApp.DPI(16);
		switch (nSBCode)
		{
		case SB_LINEUP:          //Scroll one line up
			ScrollWindowSimple(unit * step);
			break;
		case SB_LINEDOWN:           //Scroll one line down
			ScrollWindowSimple(-unit * step);
			break;
		case SB_PAGEUP:            //Scroll one page up.
			ScrollWindowSimple(unit * step * 5);
			break;
		case SB_PAGEDOWN:        //Scroll one page down
			ScrollWindowSimple(-unit * step * 5);
			break;
		case SB_ENDSCROLL:      //End scroll
			break;
		case SB_THUMBPOSITION:  //Scroll to the absolute position. The current position is provided in nPos
			break;
		case SB_THUMBTRACK:                  //Drag scroll box to specified position. The current position is provided in nPos
        {
            int y_amount = (m_last_pos - nPos)*unit;
            ScrollWindow(0, y_amount);
            scrollinfo.nPos = nPos;
            SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
        }
			break;
		}
        m_last_pos = scrollinfo.nPos;
	}
	CDialogEx::OnVScroll(nSBCode, nPos, pScrollBar);
}

BOOL CTabDlg::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	if (m_scroll_enable)
	{
		int step = theApp.DPI(64);
		if (zDelta > 0)
		{
			ScrollWindowSimple(step);
		}
		if (zDelta < 0)
		{
			ScrollWindowSimple(-step);
		}
        SCROLLINFO scrollinfo;
        GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
        m_last_pos = scrollinfo.nPos;
    }

	return CDialogEx::OnMouseWheel(nFlags, zDelta, pt);
}

void CTabDlg::OnOK()


#include "stdafx.h"
#include "TabDlg.h"
#include "MusicPlayer2.h"

IMPLEMENT_DYNAMIC(CTabDlg, CDialogEx)

CTabDlg::CTabDlg(UINT nIDTemplate, CWnd * pParent) : CDialogEx(nIDTemplate, pParent)
{
	m_pParent = pParent;
}

CTabDlg::~CTabDlg()
{
}

BOOL CTabDlg::PreTranslateMessage(MSG* pMsg)
{
	// TODO: 在此添加专用代码和/或调用基类
	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
	{
		return TRUE;
	}
	if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
	{
		return TRUE;
	}

	return CDialogEx::PreTranslateMessage(pMsg);
}

BOOL CTabDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO:  在此添加额外的初始化
	//将窗口背景设置成白色
	SetBackgroundColor(RGB(255, 255, 255));

	return TRUE;  // return TRUE unless you set the focus to a control
				  // 异常: OCX 属性页应返回 FALSE
}

CWnd* CTabDlg::GetParentWindow()
{
    CWnd* pParent = GetParent();
    if (pParent != nullptr)
    {
        return pParent->GetParent();
    }
    return nullptr;
}

void CTabDlg::SetScrollbarInfo(int nPage, int nMax)
{
	//初始化滚动条
	SCROLLINFO scrollinfo;
	GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
	scrollinfo.nPage = nPage;    //设置滑块大小
	scrollinfo.nMin = 0;
	scrollinfo.nMax = nMax;     //设置滚动条的最大位置
	if (scrollinfo.nMax < 0)
		scrollinfo.nMax = 0;
	scrollinfo.nPos = scrollinfo.nMin

	SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);

    m_scroll_enable = true;
}

void CTabDlg::ResetScroll()
{
	if (m_scroll_enable)
	{
		SCROLLINFO scrollinfo;
		GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
		int step = scrollinfo.nPos - scrollinfo.nMin;
		scrollinfo.nPos = scrollinfo.nMin;
		SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
		ScrollWindow(0, step);
	}
}

void CTabDlg::ScrollWindowSimple(int step)
{
	SCROLLINFO scrollinfo;
	GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
	scrollinfo.nPos -= step;
	if (scrollinfo.nPos < scrollinfo.nMin)
	{
		step = scrollinfo.nPos + step - scrollinfo.nMin;		//如果向上滚动一个距离后小于滚动条的最小位置了，则修正step的值，使窗口滚动到最上方
		scrollinfo.nPos = scrollinfo.nMin;
	}
	if (scrollinfo.nPos + scrollinfo.nPage > scrollinfo.nMax)  //此处一定要注意加上滑块的长度，再作判断
	{
		step -= (scrollinfo.nMax - (scrollinfo.nPos + scrollinfo.nPage));		//如果向上滚动一个距离后大于滚动条的最大位置了，则修正step的值，使窗口滚动到最下方
		if (step > 0)
			step = 0;
		scrollinfo.nPos = scrollinfo.nMax;
	}
	SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
	ScrollWindow(0, step);

}

void CTabDlg::EnableDlgCtrl(UINT id, bool enable)
{
    CWnd* pCtrl = GetDlgItem(id);
    if (pCtrl != nullptr)
        pCtrl->EnableWindow(enable);
}

void CTabDlg::ShowDlgCtrl(UINT id, bool show)
{
    CWnd* pCtrl = GetDlgItem(id);
    if (pCtrl != nullptr)
        pCtrl->ShowWindow(show);
}

BEGIN_MESSAGE_MAP(CTabDlg, CDialogEx)
	ON_WM_VSCROLL()
	ON_WM_MOUSEWHEEL()
END_MESSAGE_MAP()

void CTabDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	//参考资料：http://example.com

	if (m_scroll_enable)
	{
		SCROLLINFO scrollinfo;
		GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
		int unit = 1;
		int step = theApp.DPI(16);
		switch (nSBCode)
		{
		case SB_LINEUP:          //Scroll one line up
			ScrollWindowSimple(unit * step);
			break;
		case SB_LINEDOWN:           //Scroll one line down
			ScrollWindowSimple(-unit * step);
			break;
		case SB_PAGEUP:            //Scroll one page up.
			ScrollWindowSimple(unit * step * 5);
			break;
		case SB_PAGEDOWN:        //Scroll one page down
			ScrollWindowSimple(-unit * step * 5);
			break;
		case SB_ENDSCROLL:      //End scroll
			break;
		case SB_THUMBPOSITION:  //Scroll to the absolute position. The current position is provided in nPos
			break;
		case SB_THUMBTRACK:                  //Drag scroll box to specified position. The current position is provided in nPos
        {
            int y_amount = (m_last_pos - nPos)*unit;
            ScrollWindow(0, y_amount);
            scrollinfo.nPos = nPos;
            SetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
        }
			break;
		}
        m_last_pos = scrollinfo.nPos;
	}
	CDialogEx::OnVScroll(nSBCode, nPos, pScrollBar);
}

BOOL CTabDlg::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	if (m_scroll_enable)
	{
		int step = theApp.DPI(64);
		if (zDelta > 0)
		{
			ScrollWindowSimple(step);
		}
		if (zDelta < 0)
		{
			ScrollWindowSimple(-step);
		}
        SCROLLINFO scrollinfo;
        GetScrollInfo(SB_VERT, &scrollinfo, SIF_ALL);
        m_last_pos = scrollinfo.nPos;
    }

	return CDialogEx::OnMouseWheel(nFlags, zDelta, pt);
}

void CTabDlg::OnOK()
{
    // TODO: 在此添加专用代码和/或调用基类

    CDialogEx::OnOK();
}


{
  if (length == 0)
    return true;

  this->adjust (size * length, align);
  return true;
}

#include "ace/CDR_Size.h"
#include "ace/SString.h"
#include "ace/OS_Memory.h"
#include "ace/Truncate.h"

#if !defined (__ACE_INLINE__)
# include "ace/CDR_Size.inl"
#endif /* ! __ACE_INLINE__ */

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_CDR::Boolean
ACE_SizeCDR::write_wchar (ACE_CDR::WChar x)
{
  // Note: translator framework is not supported.
  if (ACE_OutputCDR::wchar_maxbytes () == 0)
    {
      errno = EACCES;
      return (this->good_bit_ = false);
    }

  if (static_cast<ACE_CDR::Short> (major_version_) == 1
          && static_cast<ACE_CDR::Short> (minor_version_) == 2)
    {
      ACE_CDR::Octet len =
        static_cast<ACE_CDR::Octet> (ACE_OutputCDR::wchar_maxbytes ());

      if (this->write_1 (&len))
        {
          if (ACE_OutputCDR::wchar_maxbytes () == sizeof(ACE_CDR::WChar))
            {
              return
                this->write_octet_array (
                  reinterpret_cast<const ACE_CDR::Octet*> (&x),
                  static_cast<ACE_CDR::ULong> (len));
            }
          else
            {
              if (ACE_OutputCDR::wchar_maxbytes () == 2)
                {
                  ACE_CDR::Short sx = static_cast<ACE_CDR::Short> (x);
                  return
                    this->write_octet_array (
                      reinterpret_cast<const ACE_CDR::Octet*> (&sx),
                      static_cast<ACE_CDR::ULong> (len));
                }
              else
                {
                  ACE_CDR::Octet ox = static_cast<ACE_CDR::Octet> (x);
                  return
                    this->write_octet_array (
                      reinterpret_cast<const ACE_CDR::Octet*> (&ox),
                      static_cast<ACE_CDR::ULong> (len));
                }
            }
        }
    }
  else if (static_cast<ACE_CDR::Short> (minor_version_) == 0)
    { // wchar is not allowed with GIOP 1.0.
      errno = EINVAL;
      return (this->good_bit_ = false);
    }

  if (ACE_OutputCDR::wchar_maxbytes () == sizeof (ACE_CDR::WChar))
    {
      const void *temp = &x;
      return this->write_4 (reinterpret_cast<const ACE_CDR::ULong *> (temp));
    }
  else if (ACE_OutputCDR::wchar_maxbytes () == 2)
    {
      ACE_CDR::Short sx = static_cast<ACE_CDR::Short> (x);
      return this->write_2 (reinterpret_cast<const ACE_CDR::UShort *> (&sx));
    }

  ACE_CDR::Octet ox = static_cast<ACE_CDR::Octet> (x);
  return this->write_1 (reinterpret_cast<const ACE_CDR::Octet *> (&ox));
}

ACE_CDR::Boolean
ACE_SizeCDR::write_string (ACE_CDR::ULong len,
                             const ACE_CDR::Char *x)
{
  // Note: translator framework is not supported.
  if (len != 0)
    {
      if (this->write_ulong (len + 1))
        return this->write_char_array (x, len + 1);
    }
  else
    {
      // Be nice to programmers: treat nulls as empty strings not
      // errors. (OMG-IDL supports languages that don't use the C/C++
      // notion of null v. empty strings; nulls aren't part of the OMG-IDL
      if (this->write_ulong (1))
        return this->write_char (0);
    }

  return (this->good_bit_ = false);
}

ACE_CDR::Boolean
ACE_SizeCDR::write_string (const ACE_CString &x)
{
  // @@ Leave this method in here, not the `.i' file so that we don't
  //    have to unnecessarily pull in the `ace/SString.h' header.
  return this->write_string (static_cast<ACE_CDR::ULong> (x.length ()),
                             x.c_str());
}

ACE_CDR::Boolean
ACE_SizeCDR::write_wstring (ACE_CDR::ULong len,
                              const ACE_CDR::WChar *x)
{
  // Note: translator framework is not supported.
  if (ACE_OutputCDR::wchar_maxbytes () == 0)
    {
      errno = EACCES;
      return (this->good_bit_ = false);
    }

  if (static_cast<ACE_CDR::Short> (this->major_version_) == 1
      && static_cast<ACE_CDR::Short> (this->minor_version_) == 2)
    {
      if (x != 0)
        {
          //In GIOP 1.2 the length field contains the number of bytes
          //the wstring occupies rather than number of wchars
          //Taking sizeof might not be a good way! This is a temporary fix.
          ACE_CDR::Boolean good_ulong =
            this->write_ulong (
              ACE_Utils::truncate_cast<ACE_CDR::ULong> (
                ACE_OutputCDR::wchar_maxbytes () * len));

          if (good_ulong)
            {
              return this->write_wchar_array (x, len);
            }
        }
      else
        {
          //In GIOP 1.2 zero length wstrings are legal
          return this->write_ulong (0);
        }
    }

  else
    if (x != 0)
      {
        if (this->write_ulong (len + 1))
          return this->write_wchar_array (x, len + 1);
      }
    else if (this->write_ulong (1))
      return this->write_wchar (0);
   return (this->good_bit_ = false);
}

ACE_CDR::Boolean
ACE_SizeCDR::write_1 (const ACE_CDR::Octet *)
{
  this->adjust (1);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_2 (const ACE_CDR::UShort *)
{
  this->adjust (ACE_CDR::SHORT_SIZE);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_4 (const ACE_CDR::ULong *)
{
  this->adjust (ACE_CDR::LONG_SIZE);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_8 (const ACE_CDR::ULongLong *)
{
  this->adjust (ACE_CDR::LONGLONG_SIZE);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_16 (const ACE_CDR::LongDouble *)
{
  this->adjust (ACE_CDR::LONGDOUBLE_SIZE,
                ACE_CDR::LONGDOUBLE_ALIGN);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_wchar_array_i (const ACE_CDR::WChar *,
                                    ACE_CDR::ULong length)
{
  if (length == 0)
    return true;

  size_t const align = (ACE_OutputCDR::wchar_maxbytes () == 2) ?
    ACE_CDR::SHORT_ALIGN :
    ACE_CDR::OCTET_ALIGN;

  this->adjust (ACE_OutputCDR::wchar_maxbytes () * length, align);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_array (const void *,
                          size_t size,
                          size_t align,
                          ACE_CDR::ULong length)

ACE_CDR::Boolean
ACE_SizeCDR::write_boolean_array (const ACE_CDR::Boolean*,
                                  ACE_CDR::ULong length)
{
  this->adjust (length, 1);
  return true;
}

void
ACE_SizeCDR::adjust (size_t size)
{
  adjust (size, size);
}

void
ACE_SizeCDR::adjust (size_t size,
                     size_t align)
{
#if !defined (ACE_LACKS_CDR_ALIGNMENT)
  const size_t offset = ACE_align_binary (size_, align) - size_;
  size_ += offset;
#endif /* ACE_LACKS_CDR_ALIGNMENT */
  size_ += size;
}

ACE_CDR::Boolean
operator<< (ACE_SizeCDR &ss, const ACE_CString &x)
{
  ss.write_string (x);
  return ss.good_bit ();
}

ACE_END_VERSIONED_NAMESPACE_DECL


#include "ace/CDR_Size.h"
#include "ace/SString.h"
#include "ace/OS_Memory.h"
#include "ace/Truncate.h"

#if !defined (__ACE_INLINE__)
# include "ace/CDR_Size.inl"
#endif /* ! __ACE_INLINE__ */

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_CDR::Boolean
ACE_SizeCDR::write_wchar (ACE_CDR::WChar x)
{
  // Note: translator framework is not supported.
  if (ACE_OutputCDR::wchar_maxbytes () == 0)
    {
      errno = EACCES;
      return (this->good_bit_ = false);
    }

  if (static_cast<ACE_CDR::Short> (major_version_) == 1
          && static_cast<ACE_CDR::Short> (minor_version_) == 2)
    {
      ACE_CDR::Octet len =
        static_cast<ACE_CDR::Octet> (ACE_OutputCDR::wchar_maxbytes ());

      if (this->write_1 (&len))
        {
          if (ACE_OutputCDR::wchar_maxbytes () == sizeof(ACE_CDR::WChar))
            {
              return
                this->write_octet_array (
                  reinterpret_cast<const ACE_CDR::Octet*> (&x),
                  static_cast<ACE_CDR::ULong> (len));
            }
          else
            {
              if (ACE_OutputCDR::wchar_maxbytes () == 2)
                {
                  ACE_CDR::Short sx = static_cast<ACE_CDR::Short> (x);
                  return
                    this->write_octet_array (
                      reinterpret_cast<const ACE_CDR::Octet*> (&sx),
                      static_cast<ACE_CDR::ULong> (len));
                }
              else
                {
                  ACE_CDR::Octet ox = static_cast<ACE_CDR::Octet> (x);
                  return
                    this->write_octet_array (
                      reinterpret_cast<const ACE_CDR::Octet*> (&ox),
                      static_cast<ACE_CDR::ULong> (len));
                }
            }
        }
    }
  else if (static_cast<ACE_CDR::Short> (minor_version_) == 0)
    { // wchar is not allowed with GIOP 1.0.
      errno = EINVAL;
      return (this->good_bit_ = false);
    }

  if (ACE_OutputCDR::wchar_maxbytes () == sizeof (ACE_CDR::WChar))
    {
      const void *temp = &x;
      return this->write_4 (reinterpret_cast<const ACE_CDR::ULong *> (temp));
    }
  else if (ACE_OutputCDR::wchar_maxbytes () == 2)
    {
      ACE_CDR::Short sx = static_cast<ACE_CDR::Short> (x);
      return this->write_2 (reinterpret_cast<const ACE_CDR::UShort *> (&sx));
    }

  ACE_CDR::Octet ox = static_cast<ACE_CDR::Octet> (x);
  return this->write_1 (reinterpret_cast<const ACE_CDR::Octet *> (&ox));
}

ACE_CDR::Boolean
ACE_SizeCDR::write_string (ACE_CDR::ULong len,
                             const ACE_CDR::Char *x)
{
  // Note: translator framework is not supported.
  if (len != 0)
    {
      if (this->write_ulong (len + 1))
        return this->write_char_array (x, len + 1);
    }
  else
    {
      // Be nice to programmers: treat nulls as empty strings not
      // errors. (OMG-IDL supports languages that don't use the C/C++
      // notion of null v. empty strings; nulls aren't part of the OMG-IDL
      if (this->write_ulong (1))
        return this->write_char (0);
    }

  return (this->good_bit_ = false);
}

ACE_CDR::Boolean
ACE_SizeCDR::write_string (const ACE_CString &x)
{
  // @@ Leave this method in here, not the `.i' file so that we don't
  //    have to unnecessarily pull in the `ace/SString.h' header.
  return this->write_string (static_cast<ACE_CDR::ULong> (x.length ()),
                             x.c_str());
}

ACE_CDR::Boolean
ACE_SizeCDR::write_wstring (ACE_CDR::ULong len,
                              const ACE_CDR::WChar *x)
{
  // Note: translator framework is not supported.
  if (ACE_OutputCDR::wchar_maxbytes () == 0)
    {
      errno = EACCES;
      return (this->good_bit_ = false);
    }

  if (static_cast<ACE_CDR::Short> (this->major_version_) == 1
      && static_cast<ACE_CDR::Short> (this->minor_version_) == 2)
    {
      if (x != 0)
        {
          //In GIOP 1.2 the length field contains the number of bytes
          //the wstring occupies rather than number of wchars
          //Taking sizeof might not be a good way! This is a temporary fix.
          ACE_CDR::Boolean good_ulong =
            this->write_ulong (
              ACE_Utils::truncate_cast<ACE_CDR::ULong> (
                ACE_OutputCDR::wchar_maxbytes () * len));

          if (good_ulong)
            {
              return this->write_wchar_array (x, len);
            }
        }
      else
        {
          //In GIOP 1.2 zero length wstrings are legal
          return this->write_ulong (0);
        }
    }

  else
    if (x != 0)
      {
        if (this->write_ulong (len + 1))
          return this->write_wchar_array (x, len + 1);
      }
    else if (this->write_ulong (1))
      return this->write_wchar (0);
   return (this->good_bit_ = false);
}

ACE_CDR::Boolean
ACE_SizeCDR::write_1 (const ACE_CDR::Octet *)
{
  this->adjust (1);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_2 (const ACE_CDR::UShort *)
{
  this->adjust (ACE_CDR::SHORT_SIZE);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_4 (const ACE_CDR::ULong *)
{
  this->adjust (ACE_CDR::LONG_SIZE);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_8 (const ACE_CDR::ULongLong *)
{
  this->adju

  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_16 (const ACE_CDR::LongDouble *)
{
  this->adjust (ACE_CDR::LONGDOUBLE_SIZE,
                ACE_CDR::LONGDOUBLE_ALIGN);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_wchar_array_i (const ACE_CDR::WChar *,
                                    ACE_CDR::ULong length)
{
  if (length == 0)
    return true;

  size_t const align = (ACE_OutputCDR::wchar_maxbytes () == 2) ?
    ACE_CDR::SHORT_ALIGN :
    ACE_CDR::OCTET_ALIGN;

  this->adjust (ACE_OutputCDR::wchar_maxbytes () * length, align);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_array (const void *,
                          size_t size,
                          size_t align,
                          ACE_CDR::ULong length)
{
  if (length == 0)
    return true;

  this->adjust (size * length, align);
  return true;
}

ACE_CDR::Boolean
ACE_SizeCDR::write_boolean_array (const ACE_CDR::Boolean*,
                                  ACE_CDR::ULong length)
{
  this->adjust (length, 1);
  return true;
}

void
ACE_SizeCDR::adjust (size_t size)
{
  adjust (size, size);
}

void
ACE_SizeCDR::adjust (size_t size,
                     size_t align)
{
#if !defined (ACE_LACKS_CDR_ALIGNMENT)
  const size_t offset = ACE_align_binary (size_, align) - size_;
  size_ += offset;
#endif /* ACE_LACKS_CDR_ALIGNMENT */
  size_ += size;
}

ACE_CDR::Boolean
operator<< (ACE_SizeCDR &ss, const ACE_CString &x)
{
  ss.write_string (x);
  return ss.good_bit ();
}

ACE_END_VERSIONED_NAMESPACE_DECL


/*
	This file is part of Warzone 2100.

*/
#include "lib/framework/frame.h"
#include "lib/framework/file.h"
#include "lib/ivis_opengl/piestate.h"
#include "lib/ivis_opengl/piepalette.h"
#include "screen.h"

PIELIGHT psPalette[WZCOL_MAX];

void pal_Init()
{
	char *pFileData = nullptr;
	UDWORD fileSize = 0;

	// Read these from file so that mod-makers can change them
	if (loadFile("palette.txt", &pFileData, &fileSize))
	{
		char * ptr = pFileData;
		int lenLeft = fileSize;
		for (int i = 0; i < WZCOL_MAX; i++)
		{
			if (lenLeft <= 0)
			{
				// No more data to read - palette.txt is likely for an older version of WZ
				debug(LOG_FATAL, "palette.txt is missing required value(s); (missing lines %d - %d)", i, (WZCOL_MAX-1));
				return;
			}

			unsigned int r, g, b, a;
			int len;

			int result = sscanf(ptr, "%x, %x, %x, %x %*[^\n]\n%n", &r, &g, &b, &a, &len);

			ptr += len;
			lenLeft -= len;
			ASSERT(lenLeft >= 0, "Buffer overrun reading palette data");

			if (result != 4)
			{
				debug(LOG_ERROR, "Truncated / invalid line (%d) in palette.txt only has %d valid values", (i + 1), result);
				continue;
			}
			psPalette[i].byte.r = r;
			psPalette[i].byte.g = g;
			psPalette[i].byte.b = b;
			psPalette[i].byte.a = a;
		}
		free(pFileData);
	}
	else
	{
		debug(LOG_FATAL, "Failed to load palette.txt");
	}
}

void pal_ShutDown()

PIELIGHT pal_GetTeamColour(int team)
{
	PIELIGHT tcolour;

	// set correct team colour based on team
	switch (team)
	{
	case 0:
		tcolour = WZCOL_TEAM1; //green
		break;
	case 1:
		tcolour = WZCOL_TEAM2; //orange
		break;
	case 2:
		tcolour = WZCOL_TEAM3; //gray
		break;
	case 3:
		tcolour = WZCOL_TEAM4; //black
		break;
	case 4:
		tcolour = WZCOL_TEAM5; //red
		break;
	case 5:
		tcolour = WZCOL_TEAM6; //blue
		break;
	case 6:
		tcolour = WZCOL_TEAM7; //purple
		break;
	case 7:
		tcolour = WZCOL_TEAM8; //teal
		break;
	case  8: return WZCOL_TEAM9;
	case  9: return WZCOL_TEAM10;
	case 10: return WZCOL_TEAM11;
	case 11: return WZCOL_TEAM12;
	case 12: return WZCOL_TEAM13;
	case 13: return WZCOL_TEAM14;
	case 14: return WZCOL_TEAM15;
	case 15: return WZCOL_TEAM16;
	default:
		STATIC_ASSERT(MAX_PLAYERS <= 16);
		ASSERT(false, "Attempting to get colour for non-existing team %u", (unsigned int)team);
		tcolour = WZCOL_WHITE; //default is white
		break;
	}

	return tcolour;
}


/*
	This file is part of Warzone 2100.

*/
#include "lib/framework/frame.h"
#include "lib/framework/file.h"
#include "lib/ivis_opengl/piestate.h"
#include "lib/ivis_opengl/piepalette.h"
#include "screen.h"

PIELIGHT psPalette[WZCOL_MAX];

void pal_Init()
{
	char *pFileData = nullptr;
	UDWORD fileSize = 0;

	// Read these from file so that mod-makers can change them
	if (loadFile("palette.txt", &pFileData, &fileSize))
	{
		char * ptr = pFileData;
		int lenLeft = fileSize;
		for (int i = 0; i < WZCOL_MAX; i++)
		{
			if (lenLeft <= 0)
			{
				// No more data to read - palette.txt is likely for an older version of WZ
				debug(LOG_FATAL, "palette.txt is missing required value(s); (missing lines %d - %d)", i, (WZCOL_MAX-1));
				return;
			}

			unsigned int r, g, b, a;
			int len;

			int result = sscanf(ptr, "%x, %x, %x, %x %*[^\n]\n%n", &r, &g, &b, &a, &len);

			ptr += len;
			lenLeft -= len;
			ASSERT(lenLeft >= 0, "Buffer overrun reading palette data");

			if (result != 4)
			{
				debug(LOG_ERROR, "Truncated / invalid line (%d) in palette.txt only has %d valid values", (i + 1), result);
				continue;
			}
			psPalette[i].byte.r = r;
			psPalette[i].byte.g = g;
			psPalette[i].byte.b = b;
			psPalette[i].byte.a = a;
		}
		free(pFileData);
	}
	else
	{
		debug(LOG_FATAL, "Failed to load palette.txt");
	}
}

void pal_ShutDown()
{
	// placeholder
}

PIELIGHT pal_GetTeamColour(int team)
{
	PIELIGHT tcolour;

	// set correct team colour based on team
	switch (team)
	{
	case 0:
		tcolour = WZCOL_TEAM1; //green
		break;
	case 1:
		tcolour = WZCOL_TEAM2; //orange
		break;
	case 2:
		tcolour = WZCOL_TEAM3; //gray
		break;
	case 3:
		tcolour = WZCOL_TEAM4; //black
		break;
	case 4:
		tcolour = WZCOL_TEAM5; //red
		break;
	case 5:
		tcolour = WZCOL_TEAM6; //blue
		break;
	case 6:
		tcolour = WZCOL_TEAM7; //purple
		break;
	case 7:
		tcolour = WZCOL_TEAM8; //teal
		break;
	case  8: return WZCOL_TEAM9;
	case  9: return WZCOL_TEAM10;
	case 10: return WZCOL_TEAM11;
	case 11: return WZCOL_TEAM12;
	case 12: return WZCOL_TEAM13;
	case 13: return WZCOL_TEAM14;
	case 14: return WZCOL_TEAM15;
	case 15: return WZCOL_TEAM16;
	default:
		STATIC_ASSERT(MAX_PLAYERS <= 16);
		ASSERT(false, "Attempting to get colour for non-existing team %u", (unsigned int)team);
		tcolour = WZCOL_WHITE; //default is white
		break;
	}

	retu


{
  int i, k;

  for (i = 0; i < HASHSIZE; i++) {
    parms.hash[i] = -1;
  }

  for (i = 0; parms.rules[i][0] != '\0'; i += 2) {
    /**  set hash value  **/
    k = (unsigned char)parms.rules[i][0];

    if (parms.hash[k] < 0) {
      parms.hash[k] = i;
    }
  }
}

/*  phonetic.c - generic replacement aglogithms for phonetic transformation

    Changelog:

    2000-01-05  Bjoern Jacke <bjoern at j3e.de>
                Initial Release insprired by the article about phonetic
                transformations out of c't 25/1999

    2007-07-26  Bjoern Jacke <bjoern at j3e.de>

    2007-08-23  Laszlo Nemeth <nemeth at OOo>
                Porting from Aspell to Hunspell using C-like structs
*/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "csutil.hxx"
#include "phonet.hxx"

void init_phonet_hash(phonetable& parms)

// like strcpy but safe if the strings overlap
//   but only if dest < src
static inline void strmove(char* dest, char* src) {
  while (*src)
    *dest++ = *src++;
  *dest = '\0';
}

static int myisalpha(char ch) {
  if ((unsigned char)ch < 128)
    return isalpha(ch);
  return 1;
}

/*  phonetic transcription algorithm                   */
/*  see: http://example.com */
/*  convert string to uppercase before this call       */
int phonet(const char* inword, char* target, int len, phonetable& parms) {
  /**       Do phonetic transformation.       **/
  /**  "len" = length of "inword" incl. '\0'. **/

  /**  result:  >= 0:  length of "target"    **/
  /**            otherwise:  error            **/

  int i, j, k = 0, n, p, z;
  int k0, n0, p0 = -333, z0;
  char c, c0;
  const char* s;
  typedef unsigned char uchar;
  char word[MAXPHONETUTF8LEN + 1];
  if (len == -1)
    len = strlen(inword);
  if (len > MAXPHONETUTF8LEN)
    return 0;
  strncpy(word, inword, MAXPHONETUTF8LEN);
  word[MAXPHONETUTF8LEN] = '\0';

  /**  check word  **/
  i = j = z = 0;
  while ((c = word[i]) != '\0') {
    n = parms.hash[(uchar)c];
    z0 = 0;

    if (n >= 0) {
      /**  check all rules for the same letter  **/
      while (parms.rules[n][0] == c) {
        /**  check whole string  **/
        k = 1; /** number of found letters  **/
        p = 5; /** default priority  **/
        s = parms.rules[n];
        s++; /**  important for (see below)  "*(s-1)"  **/

        while (*s != '\0' && word[i + k] == *s && !isdigit((unsigned char)*s) &&
               strchr("(-<^$", *s) == NULL) {
          k++;
          s++;
        }
        if (*s == '(') {
          /**  check letters in "(..)"  **/
          if (myisalpha(word[i + k])  // ...could be implied?
              && strchr(s + 1, word[i + k]) != NULL) {
            k++;
            while (*s != ')')
              s++;
            s++;
          }
        }
        p0 = (int)*s;
        k0 = k;
        while (*s == '-' && k > 1) {
          k--;
          s++;
        }
        if (*s == '<')
          s++;
        if (isdigit((unsigned char)*s)) {
          /**  determine priority  **/
          p = *s - '0';
          s++;
        }
        if (*s == '^' && *(s + 1) == '^')
          s++;

        if (*s == '\0' || (*s == '^' && (i == 0 || !myisalpha(word[i - 1])) &&
                           (*(s + 1) != '$' || (!myisalpha(word[i + k0])))) ||
            (*s == '$' && i > 0 && myisalpha(word[i - 1]) &&
             (!myisalpha(word[i + k0])))) {
          /**  search for followup rules, if:     **/
          /**  parms.followup and k > 1  and  NO '-' in searchstring **/
          c0 = word[i + k - 1];
          n0 = parms.hash[(uchar)c0];

          if (k > 1 && n0 >= 0 && p0 != (int)'-' && word[i + k] != '\0') {
            /**  test follow-up rule for "word[i+k]"  **/
            while (parms.rules[n0][0] == c0) {
              /**  check whole string  **/
              k0 = k;
              p0 = 5;
              s = parms.rules[n0];
              s++;
              while (*s != '\0' && word[i + k0] == *s &&
                     !isdigit((unsigned char)*s) &&
                     strchr("(-<^$", *s) == NULL) {
                k0++;
                s++;
              }
              if (*s == '(') {
                /**  check letters  **/
                if (myisalpha(word[i + k0]) &&
                    strchr(s + 1, word[i + k0]) != NULL) {
                  k0++;
                  while (*s != ')' && *s != '\0')
                    s++;
                  if (*s == ')')
                    s++;
                }
              }
              while (*s == '-') {
                /**  "k0" gets NOT reduced   **/
                /**  because "if (k0 == k)"  **/
                s++;
              }
              if (*s == '<')
                s++;
              if (isdigit((unsigned char)*s)) {
                p0 = *s - '0';
                s++;
              }

              if (*s == '\0'
                  /**  *s == '^' cuts  **/
                  || (*s == '$' && !myisalpha(word[i + k0]))) {
                if (k0 == k) {
                  /**  this is just a piece of the string  **/
                  n0 += 2;
                  continue;
                }

                if (p0 < p) {
                  /**  priority too low  **/
                  n0 += 2;
                  continue;
                }
                /**  rule fits; stop search  **/
                break;
              }
              n0 += 2;
            } /**  End of "while (parms.rules[n0][0] == c0)"  **/

            if (p0 >= p && parms.rules[n0][0] == c0) {
              n += 2;
              continue;
            }
          } /** end of follow-up stuff **/

          /**  replace string  **/
          s = parms.rules[n + 1];
          p0 = (parms.rules[n][0] != '\0' &&
                strchr(parms.rules[n] + 1, '<') != NULL)
                   ? 1
                   : 0;
          if (p0 == 1 && z == 0) {
            /**  rule with '<' is used  **/
            if (j > 0 && *s != '\0' &&
                (target[j - 1] == c || target[j - 1] == *s)) {
              j--;
            }
            z0 = 1;
            z = 1;
            k0 = 0;
            while (*s != '\0' && word[i + k0] != '\0') {
              word[i + k0] = *s;
              k0++;
              s++;
            }
            if (k > k0)
              strmove(&word[0] + i + k0, &word[0] + i + k);

            /**  new "actual letter"  **/
            c = word[i];
          } else { /** no '<' rule used **/
            i += k - 1;
            z = 0;
            while (*s != '\0' && *(s + 1) != '\0' && j < len) {
              if (j == 0 || target[j - 1] != *s) {
                target[j] = *s;
                j++;
              }
              s++;
            }
            /**  new "actual letter"  **/
            c = *s;
            if (parms.rules[n][0] != '\0' &&
                strstr(parms.rules[n] + 1, "^^") != NULL) {
              if (c != '\0') {
                target[j] = c;
                j++;
              }
              strmove(&word[0], &word[0] + i + 1);
              i = 0;
              z0 = 1;
            }
          }
          break;
        } /** end of follow-up stuff **/
        n += 2;
      } /**  end of while (parms.rules[n][0] == c)  **/
    }   /**  end of if (n >= 0)  **/
    if (z0 == 0) {
      if (k && !p0 && j < len && c != '\0' &&
          (1 || j == 0 || target[j - 1] != c)) {
        /**  condense only double letters  **/
        target[j] = c;
        j++;
      }

      i++;
      z = 0;
      k = 0;
    }
  } /**  end of   while ((c = word[i]) != '\0')  **/

  target[j] = '\0';
  return (j);

} /**  end of function "phonet"  **/