/*
###############################################################################
#
#  EGSnrc egs++ cd geometry
#
#  This file is part of EGSnrc.
#
#
###############################################################################
#
#  Author:          Iwan Kawrakow, 2005
#
#  Contributors:    Frederic Tessier
#                   Ernesto Mainegra-Hing
#
###############################################################################
*/

/*! \file egs_cd_geometry.cpp
 *  \brief A CD-Geometry: implementation
 *  \IK
 */

#include "egs_cd_geometry.h"
#include "egs_input.h"

#ifdef NO_SSTREAM
    #include <strstream>
    #define S_STREAM std::istrstream
#else
    #include <sstream>
    #define S_STREAM std::istringstream
#endif

string EGS_CDGeometry::type = "EGS_CDGeometry";

void EGS_CDGeometry::setMedia(EGS_Input *,int,const int *) {
    egsWarning("EGS_CDGeometry::setMedia: don't use this method. Use the\n"
               " setMedia() methods of the geometry objects that make up this geometry\n");
}

void EGS_CDGeometry::setRelativeRho(int start, int end, EGS_Float rho)

void EGS_CDGeometry::setRelativeRho(EGS_Input *) {
    egsWarning("EGS_CDGeometry::setRelativeRho(): don't use this method\n"
               "  Use the setRelativeRho() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setBScaling(int start, int end, EGS_Float bf) {
    egsWarning("EGS_CDGeometry::setBScaling(): don't use this method\n"
               "  Use the setBScaling() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setBScaling(EGS_Input *) {
    egsWarning("EGS_CDGeometry::setBScaling(): don't use this method\n"
               "  Use the setBScaling() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setUpIndexing() {
    int nr = 0;
    int j;
    for (j=0; j<nbase; j++) {
        if (g[j]) {
            nr += g[j]->regions();
        }
        else {
            ++nr;
        }
    }
    reg_to_base = new int [nr];
    local_start = new int [nbase];
    int ir=0;
    for (j=0; j<nbase; j++) {
        local_start[j] = ir;
        if (g[j]) {
            int n = g[j]->regions();
            for (int i=0; i<n; i++) {
                reg_to_base[ir++] = j;
            }
        }
        else {
            reg_to_base[ir++] = j;
        }
    }
    new_indexing = true;
    nreg = nr;
}

extern "C" {

    EGS_CDGEOMETRY_EXPORT EGS_BaseGeometry *createGeometry(EGS_Input *input) {
        if (!input) {
            egsWarning("createGeometry(CD_Geometry): null input?\n");
            return 0;
        }
        EGS_Input *ij;
        while ((ij = input->takeInputItem("geometry",false)) != 0) {
            EGS_BaseGeometry::createSingleGeometry(ij);
            delete ij;
        }
        string bg_name;
        int err = input->getInput("base geometry",bg_name);
        if (err) {
            egsWarning("createGeometry(CD_Geometry): no 'base geometry' input\n");
            return 0;
        }
        EGS_BaseGeometry *g = EGS_BaseGeometry::getGeometry(bg_name);
        if (!g) {
            egsWarning("createGeometry(CD_Geometry): no geometry named %s is"
                       " defined\n",bg_name.c_str());
            return 0;
        }
        int nreg = g->regions();
        if (nreg < 1) {
            egsWarning("createGeometry(CD_Geometry): the base geometry has %d"
                       " regions?\n",nreg);
            return 0;
        }
        EGS_BaseGeometry **G = new EGS_BaseGeometry* [nreg];
        int j;
        for (j=0; j<nreg; j++) {
            G[j] = 0;
        }
        int ng = 0;
        string aux;
        err = 0;
        while ((ij = input->takeInputItem("set geometry")) != 0) {
            vector<string> aux;
            ij->getInput("set geometry",aux);
            int istart, iend;
            string name;
            bool is_ok = true;
            if (aux.size() == 2) {
                string auxx = aux[0];
                auxx += ' ';
                auxx += aux[1];
                auxx += ' ';
                S_STREAM in(auxx.c_str());
                in >> istart >> name;
                if (in.fail() || !in.good()) {
                    egsWarning("createGeometry(CD_Geometry): parse error in\n"
                               "  set geometry = %s\n",auxx.c_str());
                    err++;
                    is_ok = false;
                }
                else {
                    if (istart < 0 || istart > nreg-1) {
                        istart = -1;
                        iend = -2;
                    }
                    else {
                        iend = istart+1;
                    }
                }
            }
            else if (aux.size() == 3) {
                string auxx = aux[0];
                auxx += ' ';
                auxx += aux[1];
                auxx += ' ';
                auxx += aux[2];
                auxx += ' ';
                S_STREAM in(auxx.c_str());
                in >> istart >> iend  >> name;
                if (in.fail() || !in.good()) {
                    egsWarning("createGeometry(CD_Geometry): parse error in\n"
                               "  set geometry = %s\n",auxx.c_str());
                    err++;
                    is_ok = false;
                }
                else {
                    if (istart < 0) {
                        istart = 0;
                    }
                    if (iend > nreg) {
                        iend = nreg;
                    }
                }
            }
            else {
                err++;
                is_ok = false;
            }
            if (is_ok) {
                EGS_BaseGeometry *gj = EGS_BaseGeometry::getGeometry(name);
                if (!gj) {
                    egsWarning("createGeometry(CD_Geometry): no geometry named %s"
                               " is defined\n",name.c_str());
                    err++;
                }
                else {
                    for (int j=istart; j<iend; j++) {
                        if (G[j]  && G[j] != gj) {
                            G[j]->deref();
                        }
                        /*
                        if( !G[j] ) gj->ref();
                        else {
                            if( G[j] != gj ) G[j]->deref();
                        }
                        */
                        G[j] = gj;
                        gj->ref();
                        ng++;
                    }
                }
            }
            delete ij;
        }
        if (err) {
            egsWarning("createGeometry(CD_Geometry): %d errors\n",err);
            for (j=0; j<nreg; j++) {
                if (G[j]) {
                    if (!G[j]->deref()) {
                        delete G[j];
                    }
                }
            }
            delete [] G[j];
            return 0;
        }
        if (!ng)
            egsWarning("createGeometry(CD_Geometry): no geometries in addition to"
                       " the base geometry defined?\n"
                       "  Hope you know what you are doing\n");
        g->ref();
        int indexing = 0;
        input->getInput("new indexing style",indexing);
        EGS_BaseGeometry *result = new EGS_CDGeometry(g,G,"",indexing);
        delete [] G;
        result->setName(input);
        result->setBoundaryTolerance(input);
        result->setLabels(input);
        return result;

    }

    void EGS_CDGeometry::getLabelRegions(const string &str, vector<int> &regs) {

        // label defined in the base geometry
        vector<int> bgregs;
        bg->getLabelRegions(str, bgregs);

        // expand base regions to global region lists
        int rstart = 0;
        int rend   = 0;
        for (int i=0; i<bgregs.size(); i++) {
            if (new_indexing) {
                rstart = local_start[bgregs[i]];
                if (bgregs[i] < nbase-1) {
                    rend = local_start[bgregs[i]+1];
                }
                else {
                    rend = nreg;
                }
            }
            else {
                rstart = bgregs[i]*nmax;
                rend   = (bgregs[i]+1)*nmax;
            }
            for (int j=rstart; j<rend; j++) {
                regs.push_back(j);
            }
        }

        // label defined in the set geometries
        vector<int> gregs;
        int shift=0;
        for (int i=0; i<nbase; i++) {

            // add regions from set geometries
            gregs.clear();
            if (g[i]) {
                g[i]->getLabelRegions(str, gregs);
            }

            // shift region numbers according to indexing style
            if (new_indexing) {
                shift = local_start[i];
            }
            else {
                shift = i*nmax;
            }
            for (int j=0; j<gregs.size(); j++) {
                gregs[j] += shift;
            }

            // add regions to the list
            regs.insert(regs.end(), gregs.begin(), gregs.end());

        }

        // label defined in self (cd input block)
        EGS_BaseGeometry::getLabelRegions(str, regs);

    }

}


/*
###############################################################################
#
#  EGSnrc egs++ cd geometry
#
#  This file is part of EGSnrc.
#
#
###############################################################################
#
#  Author:          Iwan Kawrakow, 2005
#
#  Contributors:    Frederic Tessier
#                   Ernesto Mainegra-Hing
#
###############################################################################
*/

/*! \file egs_cd_geometry.cpp
 *  \brief A CD-Geometry: implementation
 *  \IK
 */

#include "egs_cd_geometry.h"
#include "egs_input.h"

#ifdef NO_SSTREAM
    #include <strstream>
    #define S_STREAM std::istrstream
#else
    #include <sstream>
    #define S_STREAM std::istringstream
#endif

string EGS_CDGeometry::type = "EGS_CDGeometry";

void EGS_CDGeometry::setMedia(EGS_Input *,int,const int *) {
    egsWarning("EGS_CDGeometry::setMedia: don't use this method. Use the\n"
               " setMedia() methods of the geometry objects that make up this geometry\n");
}

void EGS_CDGeometry::setRelativeRho(int start, int end, EGS_Float rho) {
    egsWarning("EGS_CDGeometry::setRelativeRho(): don't use this method\n"
               "  Use the setRelativeRho() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setRelativeRho(EGS_Input *) {
    egsWarning("EGS_CDGeometry::setRelativeRho(): don't use this method\n"
               "  Use the setRelativeRho() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setBScaling(int start, int end, EGS_Float bf) {
    egsWarning("EGS_CDGeometry::setBScaling(): don't use this method\n"
               "  Use the setBScaling() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setBScaling(EGS_Input *) {
    egsWarning("EGS_CDGeometry::setBScaling(): don't use this method\n"
               "  Use the setBScaling() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setUpIndexing() {
    int nr = 0;
    int j;
    for (j=0; j<nbase; j++) {
        if (g[j]) {
            nr += g[j]->regions();
        }
        else {
            ++nr;
        }
    }
    reg_to_base = new int [nr];
    local_start = new int [nbase];
    int ir=0;
    for (j=0; j<nbase; j++) {
        local_start[j] = ir;
        if (g[j]) {
            int n = g[j]->regions();
            for (int i=0; i<n; i++) {
                reg_to_base[ir++] = j;
            }
        }
        else {
            reg_to_base[ir++] = j;
        }
    }
    new_indexing = true;
    nreg = nr;
}

extern "C" {

    EGS_CDGEOMETRY_EXPORT EGS_BaseGeometry *createGeometry(EGS_Input *input) {
        if (!input) {
            egsWarning("createGeometry(CD_Geometry): null input?\n");
            return 0;
        }
        EGS_Input *ij;
        while ((ij = input->takeInputItem("geometry",false)) != 0) {
            EGS_BaseGeometry::createSingleGeometry(ij);
            delete ij;
        }
        string bg_name;
        int err = input->getInput("base geometry",bg_name);
        if (err) {
            egsWarning("createGeometry(CD_Geometry): no 'base geometry' input\n");
            return 0;
        }
        EGS_BaseGeometry *g = EGS_BaseGeometry::getGeometry(bg_name);
        if (!g) {
            egsWarning("createGeometry(CD_Geometry): no geometry named %s is"
                       " defined\n",bg_name.c_str());
            return 0;
        }
        int nreg = g->regions();
        if (nreg < 1) {
            egsWarning("createGeometry(CD_Geometry): the base geometry has %d"
                       " regions?\n",nreg);
            return 0;
        }
        EGS_BaseGeometry **G = new EGS_BaseGeometry* [nreg];
        int j;
        for (j=0; j<nreg; j++) {
            G[j] = 0;
        }
        int ng = 0;
        string aux;
        err = 0;
        while ((ij = input->takeInputItem("set geometry")) != 0) {
            vector<string> aux;
            ij->getInput("set geometry",aux);
            int istart, iend;
            string name;
            bool is_ok = true;
            if (aux.size() == 2) {
                string auxx = aux[0];
                auxx += ' ';
                auxx += aux[1];
                auxx += ' ';
                S_STREAM in(auxx.c_str());
                in >> istart >> name;
                if (in.fail() || !in.good()) {
                    egsWarning("createGeometry(CD_Geometry): parse error in\n"
                               "  set geometry = %s\n",auxx.c_str());
                    err++;
                    is_ok = false;
                }
                else {
                    if (istart < 0 || istart > nreg-1) {
                        istart = -1;
                        iend = -2;
                    }
                    else {
                        iend = istart+1;
                    }
                }
            }
            else if (aux.size() == 3) {
                string auxx = aux[0];
                auxx += ' ';
                auxx += aux[1];
                auxx += ' ';
                auxx += aux[2];
                auxx += ' ';
                S_STREAM in(auxx.c_str());
                in >> istart >> iend  >> name;
                if (in.fail() || !in.good()) {
                    egsWarning("createGeometry(CD_Geometry): parse error in\n"
                               "  set geometry = %s\n",auxx.c_str());
                    err++;
                    is_ok = false;
                }
                else {
                    if (istart < 0) {
                        istart = 0;
                    }
                    if (iend > nreg) {
                        iend = nreg;
                    }
                }
            }
            else {
                err++;
                is_ok = false;
            }
            if (is_ok) {
                EGS_BaseGeometry *gj = EGS_BaseGeometry::getGeometry(name);
                if (!gj) {
                    egsWarning("createGeometry(CD_Geometry): no geometry named %s"
                               " is defined\n",name.c_str());
                    err++;
                }
                else {
                    for (int j=istart; j<iend; j++) {
                        if (G[j]  && G[j] != gj) {
                            G[j]->deref();
                        }
                        /*
                        if( !G[j] ) gj->ref();
                        else {
                            if( G[j] != gj ) G[j]->deref();
                        }
                        */
                        G[j] = gj;
                        gj->ref();
                        ng++;
                    }
                }
            }
            delete ij;
        }
        if (err) {
            egsWarning("createGeometry(CD_Geometry): %d errors\n",err);
            for (j=0; j<nreg; j++) {
                if (G[j]) {
                    if (!G[j]->deref()) {
                        delete G[j];
                    }
                }
            }
            delete [] G[j];
            return 0;
        }
        if (!ng)
            egsWarning("createGeometry(CD_Geometry): no geometries in addition to"
                       " the base geometry defined?\n"
                       "  Hope you know what you are doing\n");
        g->ref();
        int indexing = 0;
        input->getInput

        EGS_BaseGeometry *result = new EGS_CDGeometry(g,G,"",indexing);
        delete [] G;
        result->setName(input);
        result->setBoundaryTolerance(input);
        result->setLabels(input);
        return result;

    }

    void EGS_CDGeometry::getLabelRegions(const string &str, vector<int> &regs) {

        // label defined in the base geometry
        vector<int> bgregs;
        bg->getLabelRegions(str, bgregs);

        // expand base regions to global region lists
        int rstart = 0;
        int rend   = 0;
        for (int i=0; i<bgregs.size(); i++) {
            if (new_indexing) {
                rstart = local_start[bgregs[i]];
                if (bgregs[i] < nbase-1) {
                    rend = local_start[bgregs[i]+1];
                }
                else {
                    rend = nreg;
                }
            }
            else {
                rstart = bgregs[i]*nmax;
                rend   = (bgregs[i]+1)*nmax;
            }
            for (int j=rstart; j<rend; j++) {
                regs.push_back(j);
            }
        }

        // label defined in the set geometries
        vector<int> gregs;
        int shift=0;
        for (int i=0; i<nbase; i++) {

            // add regions from set geometries
            gregs.clear();
            if (g[i]) {
                g[i]->getLabelRegions(str, gregs);
            }

            // shift region numbers according to indexing style
            if (new_indexing) {
                shift = local_start[i];
            }
            else {
                shift = i*nmax;
            }
            for (int j=0; j<gregs.size(); j++) {
                gregs[j] += shift;
            }

            // add regions to the list
            regs.insert(regs.end(), gregs.begin(), gregs.end());

        }

        // label defined in self (cd input block)
        EGS_BaseGeometry::getLabelRegions(str, regs);

    }

}


while ((ij = input->takeInputItem("geometry",false)) != 0) {
            EGS_BaseGeometry::createSingleGeometry(ij);
            delete ij;
        }

/*
###############################################################################
#
#  EGSnrc egs++ cd geometry
#
#  This file is part of EGSnrc.
#
#
###############################################################################
#
#  Author:          Iwan Kawrakow, 2005
#
#  Contributors:    Frederic Tessier
#                   Ernesto Mainegra-Hing
#
###############################################################################
*/

/*! \file egs_cd_geometry.cpp
 *  \brief A CD-Geometry: implementation
 *  \IK
 */

#include "egs_cd_geometry.h"
#include "egs_input.h"

#ifdef NO_SSTREAM
    #include <strstream>
    #define S_STREAM std::istrstream
#else
    #include <sstream>
    #define S_STREAM std::istringstream
#endif

string EGS_CDGeometry::type = "EGS_CDGeometry";

void EGS_CDGeometry::setMedia(EGS_Input *,int,const int *) {
    egsWarning("EGS_CDGeometry::setMedia: don't use this method. Use the\n"
               " setMedia() methods of the geometry objects that make up this geometry\n");
}

void EGS_CDGeometry::setRelativeRho(int start, int end, EGS_Float rho) {
    egsWarning("EGS_CDGeometry::setRelativeRho(): don't use this method\n"
               "  Use the setRelativeRho() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setRelativeRho(EGS_Input *) {
    egsWarning("EGS_CDGeometry::setRelativeRho(): don't use this method\n"
               "  Use the setRelativeRho() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setBScaling(int start, int end, EGS_Float bf) {
    egsWarning("EGS_CDGeometry::setBScaling(): don't use this method\n"
               "  Use the setBScaling() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setBScaling(EGS_Input *) {
    egsWarning("EGS_CDGeometry::setBScaling(): don't use this method\n"
               "  Use the setBScaling() methods of the geometry objects that make"
               " up this geometry\n");
}

void EGS_CDGeometry::setUpIndexing() {
    int nr = 0;
    int j;
    for (j=0; j<nbase; j++) {
        if (g[j]) {
            nr += g[j]->regions();
        }
        else {
            ++nr;
        }
    }
    reg_to_base = new int [nr];
    local_start = new int [nbase];
    int ir=0;
    for (j=0; j<nbase; j++) {
        local_start[j] = ir;
        if (g[j]) {
            int n = g[j]->regions();
            for (int i=0; i<n; i++) {
                reg_to_base[ir++] = j;
            }
        }
        else {
            reg_to_base[ir++] = j;
        }
    }
    new_indexing = true;
    nreg = nr;
}

extern "C" {

    EGS_CDGEOMETRY_EXPORT EGS_BaseGeometry *createGeometry(EGS_Input *input) {
        if (!input) {
            egsWarning("createGeometry(CD_Geometry): null input?\n");
            return 0;
        }
        EGS_Input *ij;

        string bg_name;
        int err = input->getInput("base geometry",bg_name);
        if (err) {
            egsWarning("createGeometry(CD_Geometry): no 'base geometry' input\n");
            return 0;
        }
        EGS_BaseGeometry *g = EGS_BaseGeometry::getGeometry(bg_name);
        if (!g) {
            egsWarning("createGeometry(CD_Geometry): no geometry named %s is"
                       " defined\n",bg_name.c_str());
            return 0;
        }
        int nreg = g->regions();
        if (nreg < 1) {
            egsWarning("createGeometry(CD_Geometry): the base geometry has %d"
                       " regions?\n",nreg);
            return 0;
        }
        EGS_BaseGeometry **G = new EGS_BaseGeometry* [nreg];
        int j;
        for (j=0; j<nreg; j++) {
            G[j] = 0;
        }
        int ng = 0;
        string aux;
        err = 0;
        while ((ij = input->takeInputItem("set geometry")) != 0) {
            vector<string> aux;
            ij->getInput("set geometry",aux);
            int istart, iend;
            string name;
            bool is_ok = true;
            if (aux.size() == 2) {
                string auxx = aux[0];
                auxx += ' ';
                auxx += aux[1];
                auxx += ' ';
                S_STREAM in(auxx.c_str());
                in >> istart >> name;
                if (in.fail() || !in.good()) {
                    egsWarning("createGeometry(CD_Geometry): parse error in\n"
                               "  set geometry = %s\n",auxx.c_str());
                    err++;
                    is_ok = false;
                }
                else {
                    if (istart < 0 || istart > nreg-1) {
                        istart = -1;
                        iend = -2;
                    }
                    else {
                        iend = istart+1;
                    }
                }
            }
            else if (aux.size() == 3) {
                string auxx = aux[0];
                auxx += ' ';
                auxx += aux[1];
                auxx += ' ';
                auxx += aux[2];
                auxx += ' ';
                S_STREAM in(auxx.c_str());
                in >> istart >> iend  >> name;
                if (in.fail() || !in.good()) {
                    egsWarning("createGeometry(CD_Geometry): parse error in\n"
                               "  set geometry = %s\n",auxx.c_str());
                    err++;
                    is_ok = false;
                }
                else {
                    if (istart < 0) {
                        istart = 0;
                    }
                    if (iend > nreg) {
                        iend = nreg;
                    }
                }
            }
            else {
                err++;
                is_ok = false;
            }
            if (is_ok) {
                EGS_BaseGeometry *gj = EGS_BaseGeometry::getGeometry(name);
                if (!gj) {
                    egsWarning("createGeometry(CD_Geometry): no geometry named %s"
                               " is defined\n",name.c_str());
                    err++;
                }
                else {
                    for (int j=istart; j<iend; j++) {
                        if (G[j]  && G[j] != gj) {
                            G[j]->deref();
                        }
                        /*
                        if( !G[j] ) gj->ref();
                        else {
                            if( G[j] != gj ) G[j]->deref();
                        }
                        */
                        G[j] = gj;
                        gj->ref();
                        ng++;
                    }
                }
            }
            delete ij;
        }
        if (err) {
            egsWarning("createGeometry(CD_Geometry): %d errors\n",err);
            for (j=0; j<nreg; j++) {
                if (G[j]) {
                    if (!G[j]->deref()) {
                        delete G[j];
                    }
                }
            }
            delete [] G[j];
            return 0;
        }
        if (!ng)
            egsWarning("createGeometry(CD_Geometry): no geometries in addition to"
                       " the base geometry defined?\n"
                       "  Hope you know what you are doing\n");
        g->ref();
        int indexing = 0;
        input->getInput("new indexing style",indexing);
        EGS_BaseGeometry *result = new EGS_CDGeometry(g,G,"",indexing);
        delete [] G;
        result->setName(input);
        result->setBoundaryTolerance(input);
        result->setLabels(input);
        return result;

    }

    void EGS_CDGeometry::getLabelRegions(const string &str, vector<int> &regs) {

        // label defined in the base geometry
        vector<int> bgregs;
        bg->getLabelRegions(str, bgregs);

        // expand base regions to global region lists
        int rstart = 0;
        int rend   = 0;
        for (int i=0; i<bgregs.size(); i++) {
            if (new_indexing) {
                rstart = local_start[bgregs[i]];
                if (bgregs[i] < nbase-1) {
                    rend = local_start[bgregs[i]+1];
                }
                else {
                    rend = nreg;
                }
            }
            else {
                rstart = bgregs[i]*nmax;
                rend   = (bgregs[i]+1)*nmax;
            }
            for (int j=rstart; j<rend; j++) {
                regs.push_back(j);
            }
        }

        // label defined in the set geometries
        vector<int> gregs;
        int shift=0;
        for (int i=0; i<nbase; i++) {

            // add regions from set geometries
            gregs.clear();
            if (g[i]) {
                g[i]->getLabelRegions(str, gregs);
            }

            // shift region numbers according to indexing style
            if (new_indexing) {
                shift = local_start[i];
            }
            else {
                shift = i*nmax;
            }
            for (int j=0; j<gregs.size(); j++) {
                gregs[j] += shift;
            }

            // add regions to the list
            regs.insert(regs.end(), gregs.begin(), gregs.end());

        }

        // label defined in self (cd input block)
        EGS_BaseGeometry::getLabelRegions(str, regs);

    }

}


bool FileRecord::has_extended_description() const noexcept

/**************************************************************************/
/*                                                                        */
/*                          WWIV Version 5.x                              */
/*                                                                        */
/**************************************************************************/
#include "sdk/files/file_record.h"

#include "core/datetime.h"
#include "core/file.h"
#include "core/strings.h"
#include "local_io/keycodes.h"
#include "sdk/vardec.h"
#include "sdk/files/files_ext.h"
#include <string>

using namespace wwiv::core;
using namespace wwiv::local::io;
using namespace wwiv::strings;

namespace wwiv::sdk::files {

namespace {
const char* WWIV_FILE_DATE_FORMAT = "%m/%d/%y";
}

static void _align(char* fn) {
  // TODO Modify this to handle long file names
  char name[40], ext[40];

  auto invalid = false;
  if (fn[0] == '.') {
    invalid = true;
  }

  for (int i = 0; i < ssize(fn); i++) {
    if (fn[i] == '\\' || fn[i] == '/' || fn[i] == ':' || fn[i] == '<' || fn[i] == '>' ||
        fn[i] == '|') {
      invalid = true;
    }
  }
  if (invalid) {
    strcpy(fn, "        .   ");
    return;
  }
  auto* s2 = strrchr(fn, '.');
  if (s2 == nullptr || strrchr(fn, '\\') > s2) {
    ext[0] = '\0';
  } else {
    strcpy(ext, &(s2[1]));
    ext[3] = '\0';
    s2[0] = '\0';
  }
  strcpy(name, fn);

  for (int j = ssize(name); j < 8; j++) {
    name[j] = ' ';
  }
  name[8] = '\0';
  auto has_wildcard = false;
  auto has_space = false;
  for (auto k = 0; k < 8; k++) {
    if (name[k] == '*') {
      has_wildcard = true;
    }
    if (name[k] == ' ') {
      has_space = true;
    }
    if (has_space) {
      name[k] = ' ';
    }
    if (has_wildcard) {
      name[k] = '?';
    }
  }

  for (int i2 = ssize(ext); i2 < 3; i2++) {
    ext[i2] = ' ';
  }
  ext[3] = '\0';
  has_wildcard = false;
  for (int i3 = 0; i3 < 3; i3++) {
    if (ext[i3] == '*') {
      has_wildcard = true;
    }
    if (has_wildcard) {
      ext[i3] = '?';
    }
  }

  char buffer[MAX_PATH];
  for (auto i4 = 0; i4 < 12; i4++) {
    buffer[i4] = SPACE;
  }
  strcpy(buffer, name);
  buffer[8] = '.';
  strcpy(&(buffer[9]), ext);
  strcpy(fn, buffer);
  for (auto i5 = 0; i5 < 12; i5++) {
    fn[i5] = to_upper_case<char>(fn[i5]);
  }
}

std::string align(const std::string& file_name) {
  if (file_name.size() >= 1024) {
    throw std::runtime_error("size to align >=1024 chars");
  }
  char s[1024];
  to_char_array(s, file_name);
  _align(s);
  return s;
}

std::string unalign(const std::string& file_name) {
  auto s{file_name};
  s.erase(remove(s.begin(), s.end(), ' '), s.end());
  if (s.empty()) {
    return {};
  }
  if (s.back() == '.') {
    s.pop_back();
  }
  return ToStringLowerCase(s);
}

FileName::FileName(std::string aligned_filename)
    : aligned_filename_(align(aligned_filename)), unaligned_filename_(unalign(aligned_filename_)) {}

FileName::FileName(const FileName& that)
    : aligned_filename_(that.aligned_filename()), unaligned_filename_(that.unaligned_filename()) {}

const std::string& FileName::aligned_filename() const noexcept {
  return aligned_filename_;
}

const std::string& FileName::unaligned_filename() const noexcept {
  return unaligned_filename_;
}

std::string FileName::basename() const {
  return StringTrim(aligned_filename_.substr(0, 8));
}

std::string FileName::extension() const {
  return StringTrim(aligned_filename_.substr(9));
}

std::optional<FileName> FileName::FromUnaligned(const std::string& unaligned_name) {
  return {FileName(align(unaligned_name))};
}

FileRecord::FileRecord() : FileRecord(uploadsrec{}) {}

FileRecord::FileRecord(const FileRecord& that) : u_(that.u()) {}

FileRecord& FileRecord::operator=(const FileRecord& that) {
  u_ = that.u();
  return *this;
}

{
  return mask(mask_extended);
}

void FileRecord::set_extended_description(bool b) {
  set_mask(mask_extended, b);
}

void FileRecord::set_mask(int mask, bool on) {
  if (on) {
    u_.mask |= mask;
  } else {
    u_.mask &= ~mask;
  }
}

bool FileRecord::mask(int mask) const {
  return u_.mask & mask;
}

bool FileRecord::set_filename(const FileName& filename) {
  return set_filename(filename.unaligned_filename());
}

bool FileRecord::set_filename(const std::string& unaligned_filename) {
  if (unaligned_filename.size() > 12) {
    return false;
  }
  to_char_array(u_.filename, align(unaligned_filename));
  return true;
}

uint32_t FileRecord::numbytes() const noexcept {
  return u_.numbytes;
}

void FileRecord::set_numbytes(int size) {
  u_.numbytes = static_cast<daten_t>(size);
}

uint16_t FileRecord::ownerusr() const noexcept {
  return u_.ownerusr;
}

void FileRecord::set_ownerusr(int o) {
  u_.ownerusr = static_cast<uint16_t>(o);
}

uint16_t FileRecord::ownersys() const noexcept {
  return u_.ownersys;
}

void FileRecord::set_ownersys(int o) {
  u_.ownersys = static_cast<uint16_t>(o);
}

bool FileRecord::set_description(const std::string& desc) {
  auto d = desc;
  if (d.size() > 55u) {
    d.resize(55);
  }
  to_char_array(u_.description, d);
  return true;
}

bool FileRecord::set_uploaded_by(const std::string& name) {
  auto d = name;
  if (d.size() > 35u) {
    d.resize(35);
  }
  to_char_array(u_.upby, d);
  return true;
}

std::string FileRecord::uploaded_by() const {
  return u_.upby;
}

std::string FileRecord::description() const {
  return u_.description;
}

wwiv::core::DateTime FileRecord::date() const {
  return DateTime::from_daten(u_.daten);
}

std::string FileRecord::actual_date() const {
  return std::string(u_.actualdate);
}

bool FileRecord::set_date(const wwiv::core::DateTime& dt) {
  const auto d = dt.to_time_t() == 0 ? DateTime::now() : dt;
  to_char_array(u_.date, d.to_string(WWIV_FILE_DATE_FORMAT));
  u_.daten = d.to_daten_t();
  return true;
}

bool FileRecord::set_actual_date(const wwiv::core::DateTime& dt) {
  const auto d = dt.to_time_t() == 0 ? DateTime::now() : dt;
  to_char_array(u_.actualdate, d.to_string(WWIV_FILE_DATE_FORMAT));
  return true;
}

FileName FileRecord::filename() const {
  return FileName(u_.filename);
}

std::string FileRecord::aligned_filename() const {
  return align(u_.filename);
}

std::string FileRecord::unaligned_filename() const {
  return unalign(u_.filename);
}

std::filesystem::path FilePath(const std::filesystem::path& directory_name,
                                   const FileRecord& f) {
  if (directory_name.empty()) {
    return f.unaligned_filename();
  }
  return directory_name / f.unaligned_filename();
}

std::filesystem::path FilePath(const std::filesystem::path& directory_name,
                                   const FileName& f) {
  if (directory_name.empty()) {
    return f.unaligned_filename();
  }
  return directory_name / f.unaligned_filename();
}

std::ostream& operator<<(std::ostream& os, const FileName& f) {
  os << f.unaligned_filename();
  return os;
}

std::ostream& operator<<(std::ostream& os, const FileRecord& f) {
  os << f.unaligned_filename();
  return os;
}

} // namespace wwiv::sdk::files


/**************************************************************************/
/*                                                                        */
/*                          WWIV Version 5.x                              */
/*                                                                        */
/**************************************************************************/
#include "sdk/files/file_record.h"

#include "core/datetime.h"
#include "core/file.h"
#include "core/strings.h"
#include "local_io/keycodes.h"
#include "sdk/vardec.h"
#include "sdk/files/files_ext.h"
#include <string>

using namespace wwiv::core;
using namespace wwiv::local::io;
using namespace wwiv::strings;

namespace wwiv::sdk::files {

namespace {
const char* WWIV_FILE_DATE_FORMAT = "%m/%d/%y";
}

static void _align(char* fn) {
  // TODO Modify this to handle long file names
  char name[40], ext[40];

  auto invalid = false;
  if (fn[0] == '.') {
    invalid = true;
  }

  for (int i = 0; i < ssize(fn); i++) {
    if (fn[i] == '\\' || fn[i] == '/' || fn[i] == ':' || fn[i] == '<' || fn[i] == '>' ||
        fn[i] == '|') {
      invalid = true;
    }
  }
  if (invalid) {
    strcpy(fn, "        .   ");
    return;
  }
  auto* s2 = strrchr(fn, '.');
  if (s2 == nullptr || strrchr(fn, '\\') > s2) {
    ext[0] = '\0';
  } else {
    strcpy(ext, &(s2[1]));
    ext[3] = '\0';
    s2[0] = '\0';
  }
  strcpy(name, fn);

  for (int j = ssize(name); j < 8; j++) {
    name[j] = ' ';
  }
  name[8] = '\0';
  auto has_wildcard = false;
  auto has_space = false;
  for (auto k = 0; k < 8; k++) {
    if (name[k] == '*') {
      has_wildcard = true;
    }
    if (name[k] == ' ') {
      has_space = true;
    }
    if (has_space) {
      name[k] = ' ';
    }
    if (has_wildcard) {
      name[k] = '?';
    }
  }

  for (int i2 = ssize(ext); i2 < 3; i2++) {
    ext[i2] = ' ';
  }
  ext[3] = '\0';
  has_wildcard = false;
  for (int i3 = 0; i3 < 3; i3++) {
    if (ext[i3] == '*') {
      has_wildcard = true;
    }
    if (has_wildcard) {
      ext[i3] = '?';
    }
  }

  char buffer[MAX_PATH];
  for (auto i4 = 0; i4 < 12; i4++) {
    buffer[i4] = SPACE;
  }
  strcpy(buffer, name);
  buffer[8] = '.';
  strcpy(&(buffer[9]), ext);
  strcpy(fn, buffer);
  for (auto i5 = 0; i5 < 12; i5++) {
    fn[i5] = to_upper_case<char>(fn[i5]);
  }
}

std::string align(const std::string& file_name) {
  if (file_name.size() >= 1024) {
    throw std::runtime_error("size to align >=1024 chars");
  }
  char s[1024];
  to_char_array(s, file_name);
  _align(s);
  return s;
}

std::string unalign(const std::string& file_name) {
  auto s{file_name};
  s.erase(remove(s.begin(), s.end(), ' '), s.end());
  if (s.empty()) {
    return {};
  }
  if (s.back() == '.') {
    s.pop_back();
  }
  return ToStringLowerCase(s);
}

FileName::FileName(std::string aligned_filename)
    : aligned_filename_(align(aligned_filename)), unaligned_filename_(unalign(aligned_filename_)) {}

FileName::FileName(const FileName& that)
    : aligned_filename_(that.aligned_filename()), unaligned_filename_(that.unaligned_filename()) {}

const std::string& FileName::aligned_filename() const noexcept {
  return aligned_filename_;
}

const std::string& FileName::unaligned_filename() const noexcept {
  return unaligned_filename_;
}

std::string FileName::basename() const {
  return StringTrim(aligned_filename_.substr(0, 8));
}

std::string FileName::extension() const {
  return StringTrim(aligned_filename_.substr(9));
}

std::optional<FileName> FileName::FromUnaligned(const std::string& unaligned_name) {
  return {FileName(align(unaligned_name))};
}

FileRecord::FileRecord() : FileRecord(uploadsrec{}) {}

FileRecord::FileRecord(const FileRecord& that) : u_(that.u()) {}

FileRecord& FileRecord::operator=(const FileRecord& that) {
  u_ = that.u();
  return *this;
}

bool FileRecord::has_extended_description() const noexcept {
  return mask(mask_extended);
}

void FileRecord::set_extended_description(bool b) {
  set_mask(mask_extended, b);
}

void FileRecord::set_mask(int mask, bool on) {
  if (on) {
    u_.mask |= mask;
  } else {
    u_.mask &= ~mask;
  }
}

bool FileRecord::mask(int mask) const {
  return u_.mask & mask;
}

bool FileRecord::set_filename(const FileName& filename) {
  return set_filename(filename.unaligned_filename());
}

bool FileRecord::set_filename(const std::string& unaligned_filename) {
  if (unaligned_filename.size() > 12) {
    return false;
  }
  to_char_array(u_.filename, align(unaligned_filename));
  return true;
}

uint32_t FileRecord::numbytes() const noexcept {
  return u_.numbytes;
}

void FileRecord::set_numbytes(int size) {
  u_.numbytes = static_cast<daten_t>(size);
}

uint16_t FileRecord::ownerusr() const noexcept {
  return u_.ownerusr;
}

void FileRecord::set_ownerusr(int o) {
  u_.ownerusr = static_cast<uint16_t>(o);
}

uint16_t FileRecord::ownersys() const noexcept {
  return u_.ownersys;
}

void FileRecord::set_ownersys(int o) {
  u_.ownersys = static_cast<uint16_t>(o);
}

bool FileRecord::set_description(const std::string& desc) {
  auto d = desc;
  if (d.size() > 55u) {
    d.resize(55);
  }
  to_char_array(u_.description, d);
  return true;
}

bool FileRecord::set_uploaded_by(const std::string& name) {
  auto d = name;
  if (d.size() > 35u) {
    d.resize(35);
  }
  to_char_array(u_.upby, d);
  return true;
}

std::string FileRecord::uploaded_by() const {
  return u_.upby;
}

std::string FileRecord::description() const {
  return u_.description;
}

wwiv::core::DateTime FileRecord::date() const {
  return DateTime::from_daten(u_.daten);
}

std::string FileRecord::actual_date() const {
  return std::string(u_.actualdate);
}

bool FileRecord::set_date(const wwiv::core::DateTime& dt) {
  const auto d = dt.to_time_t() == 0 ? DateTime::now() : dt;
  to_char_array(u_.date, d.to_string(WWIV_FILE_DATE_FORMAT));
  u_.daten = d.to_daten_t();
  return true;
}

bool FileRecord::set_actual_date(const wwiv::core::DateTime& dt) {
  const auto d = dt.to_time_t() == 0 ? DateTime::now() : dt;
  to_char_array(u_.actualdate, d.to_string(WWIV_FILE_DATE_FORMAT));
  return true;
}

FileName FileRecord::filename() const {
  return FileName(u_.filename);
}

std::string FileRecord::aligned_filename() const {
  return align(u_.filename);
}

std::string FileRecord::unaligned_filename() const {
  return unalign(u_.filename);
}

std::filesystem::path FilePath(const std::filesystem::path& directory_name,
                                   const FileRecord& f) {
  if (directory_name.empty()) {
    return f.unaligned_filename();
  }
  return directory_name / f.unaligned_filename();
}

std::filesystem::path FilePath(const std::filesystem::path& directory_name,
                                   const FileName& f) {
  if (directory_name.empty()) {
    return f.unaligned_filename();
  }
  return directory_name / f.unaligned_filename();
}

std::ostream& operator<<(std::ostream& os, const FileName& f) {
  os << f.unaligned_filename();
  return os;
}

std::ostream& operator<<(std::ostream& os, const FileRecord& f)


/**************************************************************************/
/*                                                                        */
/*                          WWIV Version 5.x                              */
/*                                                                        */
/**************************************************************************/
#include "sdk/files/file_record.h"

#include "core/datetime.h"
#include "core/file.h"
#include "core/strings.h"
#include "local_io/keycodes.h"
#include "sdk/vardec.h"
#include "sdk/files/files_ext.h"
#include <string>

using namespace wwiv::core;
using namespace wwiv::local::io;
using namespace wwiv::strings;

namespace wwiv::sdk::files {

namespace {
const char* WWIV_FILE_DATE_FORMAT = "%m/%d/%y";
}

static void _align(char* fn) {
  // TODO Modify this to handle long file names
  char name[40], ext[40];

  auto invalid = false;
  if (fn[0] == '.') {
    invalid = true;
  }

  for (int i = 0; i < ssize(fn); i++) {
    if (fn[i] == '\\' || fn[i] == '/' || fn[i] == ':' || fn[i] == '<' || fn[i] == '>' ||
        fn[i] == '|') {
      invalid = true;
    }
  }
  if (invalid) {
    strcpy(fn, "        .   ");
    return;
  }
  auto* s2 = strrchr(fn, '.');
  if (s2 == nullptr || strrchr(fn, '\\') > s2) {
    ext[0] = '\0';
  } else {
    strcpy(ext, &(s2[1]));
    ext[3] = '\0';
    s2[0] = '\0';
  }
  strcpy(name, fn);

  for (int j = ssize(name); j < 8; j++) {
    name[j] = ' ';
  }
  name[8] = '\0';
  auto has_wildcard = false;
  auto has_space = false;
  for (auto k = 0; k < 8; k++) {
    if (name[k] == '*') {
      has_wildcard = true;
    }
    if (name[k] == ' ') {
      has_space = true;
    }
    if (has_space) {
      name[k] = ' ';
    }
    if (has_wildcard) {
      name[k] = '?';
    }
  }

  for (int i2 = ssize(ext); i2 < 3; i2++) {
    ext[i2] = ' ';
  }
  ext[3] = '\0';
  has_wildcard = false;
  for (int i3 = 0; i3 < 3; i3++) {
    if (ext[i3] == '*') {
      has_wildcard = true;
    }
    if (has_wildcard) {
      ext[i3] = '?';
    }
  }

  char buffer[MAX_PATH];
  for (auto i4 = 0; i4 < 12; i4++) {
    buffer[i4] = SPACE;
  }
  strcpy(buffer, name);
  buffer[8] = '.';
  strcpy(&(buffer[9]), ext);
  strcpy(fn, buffer);
  for (auto i5 = 0; i5 < 12; i5++) {
    fn[i5] = to_upper_case<char>(fn[i5]);
  }
}

std::string align(const std::string& file_name) {
  if (file_name.size() >= 1024) {
    throw std::runtime_error("size to align >=1024 chars");
  }
  char s[1024];
  to_char_array(s, file_name);
  _ali

  return s;
}

std::string unalign(const std::string& file_name) {
  auto s{file_name};
  s.erase(remove(s.begin(), s.end(), ' '), s.end());
  if (s.empty()) {
    return {};
  }
  if (s.back() == '.') {
    s.pop_back();
  }
  return ToStringLowerCase(s);
}

FileName::FileName(std::string aligned_filename)
    : aligned_filename_(align(aligned_filename)), unaligned_filename_(unalign(aligned_filename_)) {}

FileName::FileName(const FileName& that)
    : aligned_filename_(that.aligned_filename()), unaligned_filename_(that.unaligned_filename()) {}

const std::string& FileName::aligned_filename() const noexcept {
  return aligned_filename_;
}

const std::string& FileName::unaligned_filename() const noexcept {
  return unaligned_filename_;
}

std::string FileName::basename() const {
  return StringTrim(aligned_filename_.substr(0, 8));
}

std::string FileName::extension() const {
  return StringTrim(aligned_filename_.substr(9));
}

std::optional<FileName> FileName::FromUnaligned(const std::string& unaligned_name) {
  return {FileName(align(unaligned_name))};
}

FileRecord::FileRecord() : FileRecord(uploadsrec{}) {}

FileRecord::FileRecord(const FileRecord& that) : u_(that.u()) {}

FileRecord& FileRecord::operator=(const FileRecord& that) {
  u_ = that.u();
  return *this;
}

bool FileRecord::has_extended_description() const noexcept {
  return mask(mask_extended);
}

void FileRecord::set_extended_description(bool b) {
  set_mask(mask_extended, b);
}

void FileRecord::set_mask(int mask, bool on) {
  if (on) {
    u_.mask |= mask;
  } else {
    u_.mask &= ~mask;
  }
}

bool FileRecord::mask(int mask) const {
  return u_.mask & mask;
}

bool FileRecord::set_filename(const FileName& filename) {
  return set_filename(filename.unaligned_filename());
}

bool FileRecord::set_filename(const std::string& unaligned_filename) {
  if (unaligned_filename.size() > 12) {
    return false;
  }
  to_char_array(u_.filename, align(unaligned_filename));
  return true;
}

uint32_t FileRecord::numbytes() const noexcept {
  return u_.numbytes;
}

void FileRecord::set_numbytes(int size) {
  u_.numbytes = static_cast<daten_t>(size);
}

uint16_t FileRecord::ownerusr() const noexcept {
  return u_.ownerusr;
}

void FileRecord::set_ownerusr(int o) {
  u_.ownerusr = static_cast<uint16_t>(o);
}

uint16_t FileRecord::ownersys() const noexcept {
  return u_.ownersys;
}

void FileRecord::set_ownersys(int o) {
  u_.ownersys = static_cast<uint16_t>(o);
}

bool FileRecord::set_description(const std::string& desc) {
  auto d = desc;
  if (d.size() > 55u) {
    d.resize(55);
  }
  to_char_array(u_.description, d);
  return true;
}

bool FileRecord::set_uploaded_by(const std::string& name) {
  auto d = name;
  if (d.size() > 35u) {
    d.resize(35);
  }
  to_char_array(u_.upby, d);
  return true;
}

std::string FileRecord::uploaded_by() const {
  return u_.upby;
}

std::string FileRecord::description() const {
  return u_.description;
}

wwiv::core::DateTime FileRecord::date() const {
  return DateTime::from_daten(u_.daten);
}

std::string FileRecord::actual_date() const {
  return std::string(u_.actualdate);
}

bool FileRecord::set_date(const wwiv::core::DateTime& dt) {
  const auto d = dt.to_time_t() == 0 ? DateTime::now() : dt;
  to_char_array(u_.date, d.to_string(WWIV_FILE_DATE_FORMAT));
  u_.daten = d.to_daten_t();
  return true;
}

bool FileRecord::set_actual_date(const wwiv::core::DateTime& dt) {
  const auto d = dt.to_time_t() == 0 ? DateTime::now() : dt;
  to_char_array(u_.actualdate, d.to_string(WWIV_FILE_DATE_FORMAT));
  return true;
}

FileName FileRecord::filename() const {
  return FileName(u_.filename);
}

std::string FileRecord::aligned_filename() const {
  return align(u_.filename);
}

std::string FileRecord::unaligned_filename() const {
  return unalign(u_.filename);
}

std::filesystem::path FilePath(const std::filesystem::path& directory_name,
                                   const FileRecord& f) {
  if (directory_name.empty()) {
    return f.unaligned_filename();
  }
  return directory_name / f.unaligned_filename();
}

std::filesystem::path FilePath(const std::filesystem::path& directory_name,
                                   const FileName& f) {
  if (directory_name.empty()) {
    return f.unaligned_filename();
  }
  return directory_name / f.unaligned_filename();
}

std::ostream& operator<<(std::ostream& os, const FileName& f) {
  os << f.unaligned_filename();
  return os;
}

std::ostream& operator<<(std::ostream& os, const FileRecord& f) {
  os << f.unaligned_filename();
  return os;
}

} // namespace wwiv::sdk::files


bool
SolidWidget::KeyPress(unsigned key_code) noexcept

/*
}
*/

#include "SolidWidget.hpp"
#include "ui/window/SolidContainerWindow.hpp"
#include "UIGlobals.hpp"
#include "Look/DialogLook.hpp"

SolidWidget::~SolidWidget() noexcept
{
  widget.reset();
  DeleteWindow();
}

PixelSize
SolidWidget::GetMinimumSize() const noexcept
{
  return widget->GetMinimumSize();
}

PixelSize
SolidWidget::GetMaximumSize() const noexcept
{
  return widget->GetMaximumSize();
}

constexpr
static PixelRect
ToOrigin(PixelRect rc) noexcept
{
  return PixelRect(PixelPoint(0, 0), rc.GetSize());
}

void
SolidWidget::Initialise(ContainerWindow &parent, const PixelRect &rc) noexcept
{
  WindowStyle style;
  style.ControlParent();
  style.Hide();

  auto window = std::make_unique<SolidContainerWindow>();
  window->Create(parent, rc, UIGlobals::GetDialogLook().background_color,
                 style);
  SetWindow(std::move(window));

  widget->Initialise((ContainerWindow &)GetWindow(), ToOrigin(rc));
}

void
SolidWidget::Prepare(ContainerWindow &parent, const PixelRect &rc) noexcept
{
  widget->Prepare((ContainerWindow &)GetWindow(), ToOrigin(rc));
}

void
SolidWidget::Unprepare() noexcept
{
  widget->Unprepare();
}

bool
SolidWidget::Save(bool &changed) noexcept
{
  return widget->Save(changed);
}

bool
SolidWidget::Click() noexcept
{
  return widget->Click();
}

void
SolidWidget::ReClick() noexcept
{
  widget->ReClick();
}

void
SolidWidget::Show(const PixelRect &rc) noexcept
{
  widget->Show(ToOrigin(rc));

  WindowWidget::Show(rc);
}

bool
SolidWidget::Leave() noexcept
{
  return widget->Leave();
}

void
SolidWidget::Hide() noexcept
{
  WindowWidget::Hide();
  widget->Hide();
}

void
SolidWidget::Move(const PixelRect &rc) noexcept
{
  WindowWidget::Move(rc);
  widget->Move(ToOrigin(rc));
}

bool
SolidWidget::SetFocus() noexcept
{
  return widget->SetFocus();
}

{
  return widget->KeyPress(key_code);
}


/*
}
*/

#include "SolidWidget.hpp"
#include "ui/window/SolidContainerWindow.hpp"
#include "UIGlobals.hpp"
#include "Look/DialogLook.hpp"

SolidWidget::~SolidWidget() noexcept
{
  widget.reset();
  DeleteWindow();
}

PixelSize
SolidWidget::GetMinimumSize() const noexcept
{
  return widget->GetMinimumSize();
}

PixelSize
SolidWidget::GetMaximumSize() const noexcept
{
  return widget->GetMaximumSize();
}

constexpr
static PixelRect
ToOrigin(PixelRect rc) noexcept
{
  return PixelRect(PixelPoint(0, 0), rc.GetSize());
}

void
SolidWidget::Initialise(ContainerWindow &parent, const PixelRect &rc) noexcept
{
  WindowStyle style;
  style.ControlParent();
  style.Hide();

  auto window = std::make_unique<SolidContainerWindow>();
  window->Create(parent, rc, UIGlobals::GetDialogLook().background_color,
                 style);
  SetWindow(std::move(window));

  widget->Initialise((ContainerWindow &)GetWindow(), ToOrigin(rc));
}

void
SolidWidget::Prepare(ContainerWindow &parent, const PixelRect &rc) noexcept
{
  widget->Prepare((ContainerWindow &)GetWindow(), ToOrigin(rc));
}

void
SolidWidget::Unprepare() noexcept

bool
SolidWidget::Save(bool &changed) noexcept
{
  return widget->Save(changed);
}

bool
SolidWidget::Click() noexcept
{
  return widget->Click();
}

void
SolidWidget::ReClick() noexcept
{
  widget->ReClick();
}

void
SolidWidget::Show(const PixelRect &rc) noexcept
{
  widget->Show(ToOrigin(rc));

  WindowWidget::Show(rc);
}

bool
SolidWidget::Leave() noexcept
{
  return widget->Leave();
}

void
SolidWidget::Hide() noexcept
{
  WindowWidget::Hide();
  widget->Hide();
}

void
SolidWidget::Move(const PixelRect &rc) noexcept
{
  WindowWidget::Move(rc);
  widget->Move(ToOrigin(rc));
}

bool
SolidWidget::SetFocus() noexcept
{
  return widget->SetFocus();
}

bool
SolidWidget::KeyPress(unsigned key_code) noexcept
{
  return widget->KeyPress(key_code);
}


/*
}
*/

#include "SolidWidget.hpp"
#include "ui/window/SolidContainerWindow.hpp"
#include "UIGlobals.hpp"
#include "Look/DialogLook.hpp"

SolidWidget::~SolidWidget() noexcept
{
  widget.reset();
  DeleteWindow();
}

PixelSize
SolidWidget::GetMinimumSize() const noexcept
{
  return widget->GetMinimumSize();
}

PixelSize
SolidWidget::GetMaximumSize() const noexcept
{
  return widget->GetMaximumSize();
}

constexpr
static PixelRect
ToOrigin(PixelRect rc) noexcept
{
  return PixelRect(PixelPoint(0, 0), rc.GetSize());
}

void
SolidWidget::Initialise(ContainerWindow &parent, const PixelRect &rc) noexcept
{
  WindowStyle style;
  style.ControlParent();
  style.Hide();

  auto window = std::make_unique<SolidContainerWindow>();
  window->Create(parent, rc, UIGlobals::GetDialogLook().background_color,
                 style);
  SetWindow(std::move(window));

  widget->Initialise((ContainerWindow &)GetWindow(), ToOrigin(rc));
}

void
SolidWidget::Prepare(ContainerWindow &parent, const PixelRect &rc) noexcept
{
  widget->Prepare((ContainerWindow &)GetWindow(), ToOrigin(rc));
}

void
SolidWidget::Unprepare() noexcept
{
  widget->Unprepare();
}

bool
SolidWidget::Save(bool &changed) noexcept
{
  return widget->Save(changed);
}

bool
SolidWidget::Click() noexcept
{
  return widget->Click();
}

void
SolidWidget::ReClick() noexcept
{
  widget->ReClick();
}

void
SolidWidget::Show(const PixelRect &rc) noexcept
{
  widget->Show(ToOrigin(rc));

  WindowWidget::Show(rc);
}

bool
SolidWidget::Leave() noexcept
{
  re

}

void
SolidWidget::Hide() noexcept
{
  WindowWidget::Hide();
  widget->Hide();
}

void
SolidWidget::Move(const PixelRect &rc) noexcept
{
  WindowWidget::Move(rc);
  widget->Move(ToOrigin(rc));
}

bool
SolidWidget::SetFocus() noexcept
{
  return widget->SetFocus();
}

bool
SolidWidget::KeyPress(unsigned key_code) noexcept
{
  return widget->KeyPress(key_code);
}


/**********************************************************\
Original Author: Richard Bateman (taxilian)

Created: Sept 17, 2009
- or -

\**********************************************************/

#include "axmain.h"
#include "precompiled_headers.h" // On windows, everything above this line in PCH

CFireBreathWinModule _AtlModule;


/**********************************************************\
Original Author: Richard Bateman (taxilian)

Created: Sept 17, 2009
- or -

\**********************************************************/

#include "axmain.h"
#include "precompiled_headers.h" // On windows, everything above this line in PCH

CFireBreathWinModule _AtlModule;

CFireBreathWinModule::CFireBreathWinModule()


void Phaser::AnalogPhase(float *smpsl, float *smpsr)

/*
    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);
}

{
    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);
}


{
    Pdistortion = Pdistortion_;
    distortion = (float)Pdistortion / 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::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_)

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);
}