/****************************************************************************
**
** Copyright (C) 2019 Minnesota Department of Transportation
** Office of Materials & Road Research
** 1400 Gervais Avenue
** Saint Paul, Minnesota 55109-2044
** USA
** http://www.dot.state.mn.us/materials/pvmtdesign/software.html
**
**
** $QT_BEGIN_LICENSE:GPL$
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** If you did not receive a copy of the GNU General Public License
** see http://www.gnu.org/licenses
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "globals.h"

// ALL THE GLOBAL DEFINITIONS
int deg2degmin(double decdeg, double *min, int minround)
{
    double tmp = floor(decdeg);
    int deg = (int) tmp;
    tmp = decdeg - tmp;
    *min = rnd2dec(tmp*60.,minround);
    if(*min >= 60.)
    {
        *min -= 60.;
        deg += 1;
    }
    return deg;
}

double degmin2deg(int deg, double min)
{
    return (double) deg + min/60.;
}

bool setSpin(double oldval, double *newval, double incr)
{// resets the spinner if the start point is not a multiple of the increment
    if(qFabs(*newval - oldval) == incr) // indicates spinner was used instead of edit box
    {
        if(remainder(*newval, incr) != 0)
        {
            *newval = *newval > oldval ? qFloor(*newval/incr) * incr : qCeil(*newval/incr) * incr;
        }
        return true;
    }
    else return false;
}

int decl(bool si, bool hma)
{// decimal places for length (mm or in)
    if(hma) return si ? DEC_MM : DEC_IN;
    else return si ? DEC_MM : DECR_IN;
}

int nint(double x) // provided by koahnig at qt-project.org
{// avoids binary rounding error in (int) conversion
    return x < 0 ? (int)ceil(x) : (int)floor(x);
}

long nlong(double x)
{// avoids binary rounding error in (long) conversion
    return x < 0 ? (long)ceil(x) : (long)floor(x);
}

int soilclass(double Silt, double Clay)
{
    int s = 59;
    double Sand = 100. - Silt - Clay;
    if(Sand > 90) s = 1; // Sand
    else if(Sand > 80) s = 2; // Loamy Sand
    else if(Sand > 50)
    {
        if(Clay > 30) s = 9; // Sandy Clay
        else if(Clay > 20) s = 6; // Sandy Clay Loam
        else if(Clay > 10) s = 40; // Sandy Loam (plastic)
        else s = 3; // Sandy Loam (slightly plastic)
    }
    else if(Silt > 80) s = 41; // Silt
    else if(Silt > 50)
    {
        if(Clay > 30)s = 10; // Silty Clay
        else if(Clay > 20) s = 8; // Silty Clay Loam
        else if(Clay > 10) s = 42; // Silt Loam (plastic)
        else s = 5; // Silt Loam (slightly plastic)
    }
    else if(Clay > 30) s = 11; // Clay
    else if(Clay > 20) s = 7; // Clay Loam
    else s = 4; // Loam
    return s;
}

double incl(bool si)
{// spin button increments for length (mm or in)
    return si ? INC_MM : INC_IN;
}

// The following facilitate conversions between stored and displayed data (SI or English units)
// insi and outsi refer to the input and output units (true = SI)
double convt(bool insi, double tin, bool outsi)
{// temperature °C or °F
    if((insi && outsi) || (!insi && !outsi)) return tin;
    else if(insi) return  tin * K_R + 32.;
    else return (tin - 32.) / K_R;
}

double convl(bool insi, double lin, bool outsi)
{// length mm or in
    if((insi && outsi) || (!insi && !outsi)) return lin;
    else if(insi) return lin / IN_MM;
    else return lin * IN_MM;
}

double convll(bool insi, double lin, bool outsi)
{// input length m or ft, output m
    if((insi && outsi) || (!insi && !outsi)) return lin;
    else if(insi) return lin / FT_M;
    else return lin * FT_M;
}

double convlll(bool insi, double lin, bool outsi)
{// length km or mi
    if((insi && outsi) || (!insi && !outsi)) return lin;
    else if(insi) return lin / MI_KM;
    else return lin * MI_KM;
}

double convf(bool insi, double fin, bool outsi)
{// force N (kN) or lbs (kips)
    if((insi && outsi) || (!insi && !outsi)) return fin;
    else if(insi) return fin / LB_N;
    else return fin * LB_N;
}

double convp(bool insi, double pin, bool outsi)
{// pressure kPa (MPa) or psi (ksi)
    if((insi && outsi) || (!insi && !outsi)) return pin;
    else if(insi) return pin / PSI_KPA;
    else return pin * PSI_KPA;
}

double convd(bool insi, double din, bool outsi)
{// density kg/m3 or pcf
    if((insi && outsi) || (!insi && !outsi)) return din;
    else if(insi) return din / PCF_KGM3;
    else return din * PCF_KGM3;
}

double log10(double val)
{
    return log(val)/log(10);
}

double rnd2dec(double value, int dec, bool sigfig)
{// RoundToDecimal written by Corey Spagnoli  1/14/98 adapted by Bruce Tanquist
    long   temp_value;
    double ret_value;
    double raise_to;
    double tolerance;
    double final_diff;
    bool bneg = value < 0;
    if(bneg) value = -value;
    // Significant figures option added by Bruce Tanquist 5/11/01
    if(sigfig)
    {
        dec = value == 0 ? 0 : dec - (1 + nint(log10(value)));
        if(dec > MAXDEC) dec = MAXDEC;
    }
    raise_to = pow(10.0, (double) dec);
    tolerance = 1.0 / raise_to;
    temp_value = (long)round(value * raise_to);
    ret_value = (double)temp_value / raise_to;
    final_diff = value - ret_value;
    if (final_diff < -tolerance || final_diff > tolerance) ret_value = value;
    value = ret_value;
    if(bneg) value = -value;
    return value;
}

QPoint rectIndex(bool ind, QRect* rect, QPoint pt, int xRects, int yRects)
{	// returns x and y index values for a point in an evenly divided grid
    // Clicking in the upper left cell returns (0,0)
    // Clicking in the lower right cell returns (xRects-1, yRects-1)
    // rect is changed to the inner rect
    // if ind is TRUE, pt holds index values of the inner rect and return is upper left coord.
    int x0, y0, w, h, xi, yi;
    QRect rectTmp = *rect;
    x0 = rectTmp.left();
    y0 = rectTmp.top();
    w = rectTmp.right() - x0;
    h = rectTmp.bottom() - y0;
    if(ind)
    {
        xi = pt.x();
        yi = pt.y();
    }
    else
    {
        xi = xRects * (pt.x() - x0) / w;
        yi = yRects * (pt.y() - y0) / h;
    }
    // Rectangle
    rectTmp = QRect(x0 + xi * w / xRects, y0 + yi * h / yRects,
            x0 + (xi+1) * w / xRects, y0 + (yi+1) * h / yRects);
    *rect = rectTmp;
    if(ind) return rectTmp.topLeft();
    else return QPoint(xi, yi);
}

int pdfRect(QRect *rect, int wpage, int top, double ht, int ncells, int n)
{   // ncells <= 9
    // returns the top of the next line's rect
    // n is the horizontal cell index (zero-based)
    // if n > 9, a range is represented (30 = from 0 to 3)
    // top gets updated for next line
    int n1, n2;
    int lt, wd;
    if(n < 10)
    {
        n1 = n;
        n2 = -1; // unused
    }
    else
    {
        n1 = n % 10;
        n2 = n/10;
    }
    wd = wpage/ncells;
    lt = n1*wd;
    if(n2 > -1)
    {
        wd *= n2-n1+1;
    }
    QRect rectTmp = QRect(lt, top, wd, ht);
    *rect = rectTmp;
    return rectTmp.bottom();
//    return rectTmp.y() + rectTmp.height();
}

QString bas2bas54(QString tnum, const QString &talpha1, const QString &talpha2, quint64 *udec, bool bdec)
{   // converts from any base >= 2 into decimal
    // *udec & bdec are used if decimal input is desired (tnum & talpha1 are ignored)
    // if bdec is false, *udec returns a decimal result
    // for some reason these calcs max out at 2^54-1 instead of 2^64-1
    qint32 j, k;
    quint16 nsize = tnum.length();
    quint16 nbase1 = talpha1.length();
    quint16 nbase2 = talpha2.length();
    quint16 nmax16 = ~(quint16(0));
    quint64 nmax64 = ~(quint64(0));
    quint64 ndec;
    if(nbase1 < 2 || nbase1 > nmax16 || nbase2 < 2 || nbase2 > nmax16 || nsize == 0
            || nsize > floor(log((nmax64)/log(nbase1))+1))
        return "out of range";

    if(bdec) ndec = *udec;
    else if(talpha1 == A10)
    {
        ndec = tnum.toULongLong();
        if(ndec > MAX54) return "max exceeded";
    }
    else
    {   // convert from base1 to decimal
        bool berr = false;
        QChar cc;
        quint16 nc;
        ndec = 0;
        for(j = 0; j < nsize; ++j)
        {
            cc = tnum.at(tnum.length()-1);
            nc = talpha1.indexOf(cc);
            if(nc > nbase1-1) berr = true;
            else if(ndec > MAX54 - nc * floor(pow(nbase1, j))) return "max exceeded";
            else ndec += nc * floor(pow(nbase1, j));
            tnum.chop(1);
        }
        if(berr) return "error in base to decimal conversion";
    }
    if(!bdec) *udec = ndec;   // makes decimal value available
    if(talpha2 == A10) return QString::number(ndec);
    // convert from decimal to base2
    nsize = (quint16) floor(log(ndec)/log(nbase2)) + 1;	// number of characters in output

    QString tbase = "";
    bool bhigh;
    if(ndec < pow(nbase2, nsize))
    {
        for(j = nsize-1; j >= 0; --j)
        {
            bhigh = true;
            for(k = nbase2; k > 1; --k)
            {
                if(bhigh && ndec < pow(nbase2, j) * (nbase2-k+1))
                {
                    bhigh = false;
                    tbase.append(talpha2.at(nbase2-k));
                    ndec -= (quint64) floor(pow(nbase2, j)) * (nbase2-k);
                }
            }
            if(bhigh)
            {
                tbase.append(talpha2.at(nbase2-1));
                ndec -= (quint64) floor(pow(nbase2, j)) * (nbase2-1);
            }
        }
    }
    return tbase;
}

QString txtt(bool si)
{
    return si ? "°C" : "°F";
}

QString txtl(bool si)
{
    return si ? "mm" : "in.";
}

QString txtll(bool si)
{
    return si ? "m" : "ft.";
}

QString txtlll(bool si)
{
    return si ? "km" : "mi.";
}

QString txtf(bool si)
{
    return si ? "N" : "lbs.";
}

QString txtff(bool si)
{
    return si ? "kN" : "kips";
}

QString txtp(bool si)
{
    return si ? "kPa" : "psi";
}

QString txte(bool si)
{
    return si ? "MPa" : "ksi";
}

QString txtu(bool si)
{
    return si ? "µm" : "mils";
}

QString tontxt(bool si)
{
    return si ? "tonnes" : "tons";
}

QString txtv(bool si)
{
    return si ? "km/h" : "mph";
}

QString trimright(QString txt, QString ttrim)
{// trims all of the ttrim characters from the right side of txt
    while(txt.right(1) == ttrim) txt.resize(txt.size() - 1);
    return txt;
}

QString txtdbl(double value, int dec, bool sigfig, bool trimzero, bool comma)
{// convert a double to text with options for decimal place, significant digits, remove extra zeroes and add commas
    int  i, decimal;
    QString tnum;
    value = rnd2dec(value, dec, sigfig);
    // run conversion with precision 6 to make sure the decimal point is found
    tnum = QString::number(value, 'f', 6);
    decimal = tnum.indexOf(".");
    if(sigfig)
    {
        tnum = QString::number(value, 'e', dec-1);
        tnum = tnum.left(dec+1); // remove "e+00" notation
    }
    else tnum = QString::number(value, 'f', dec);

    tnum.remove(".");
    if(decimal > 0)
    {
        int lg = tnum.length();
        if(decimal > lg) for(i = 0; i < decimal - lg; i++) tnum.insert(lg+i, "0");
        tnum.insert(decimal, ".");
        if(trimzero) tnum = trimright(tnum, "0");
        tnum = trimright(tnum, ".");
    }
    else if(decimal == 0)
    {
        tnum.insert(0, ".");
        tnum.insert(0, "0");
        if(trimzero) tnum = trimright(tnum, "0");
    }
    else // decimal < 0
    {
        for(i = decimal; i < 0; i++) tnum.insert(0, "0");
        tnum.insert(0, ".");
        tnum.insert(0, "0");
        if(trimzero) tnum = trimright(tnum, "0");
    }
    if(comma)
    {
        int length = tnum.indexOf(".");
        if(length == -1) length = tnum.size();
        for(i = 1; i < 4; i++) if(i*3 < length) tnum.insert(length-i*3, ",");
    }
    return tnum;
}

QString txtdegmin(int deg, double min)
{
    return QString::number(deg) + "°" + QString::number(min) + "'";
}

QString txthrmin(int hour, int min, bool b24)
{// convert hour and minute strings to one time string with option for 12 or 24 hour format
    if(hour < 0 || hour > 23) hour = 0;
    if(min < 0 || min > 59) min = 0;
    QString tm;
    QString th;
    QString tmin = QString::number(min);
    QString tc = ":";
    QString tz = "0";
    if(b24) tm = "";
    else if(hour < 12) tm = " AM";
    else tm = " PM";
    if(min < 10) tmin = tc + tz + tmin;
    else tmin = tc + tmin;
    if(b24)
    {
        th = QString::number(hour);
        if(hour < 10) th = tz + th;
    }
    else
    {
        if(hour == 0) hour = 12;
        else if(hour > 12) hour -= 12;
        th = QString::number(hour);
    }
    return th + tmin + tm;
}

QTime TimeText(QString *time, bool b24)
{// interpret a string with numbers, am, pm, a, p and other characters to produce
    // a QTime variable and formatted time string with option for 12 or 24 hour format
       QString ttime = *time;
       QString th;	// hours
       QString tm = "0";
       QString tmin;
       QString tc = ":";
       QString tz = "0";
       ttime.remove(" ");
       ttime.remove(".");
       ttime.remove(",");
       ttime.remove(";");
       ttime.remove("-");
       ttime.replace("a","A");
       ttime.replace("p","P");
       ttime.replace("m","M");
       int na = ttime.indexOf("A");
       ttime.remove("A");
       int np = ttime.indexOf("P");
       ttime.remove("P");
       int nz;	// indicates leading zero (military time)
       ttime.remove("M");
       int nmin = 0;	// minutes
       // check to see if there are more or less than 2 characters after the colon
       int nt = ttime.indexOf(":");
       int nl = ttime.length();
       if(nt > -1 && nl == nt+2) ttime.insert(nt+1, "0");	// insert 0 if only 1 char in minutes
       ttime.remove(":");
       nl = ttime.length();
       if(nl > 4) ttime.remove(4, nl-4); // remove extra digits
       QString ttmp = ttime.at(0);
       nz = ttmp.toInt();	// zero indicates military time
       nt = ttime.toInt();
       if(nl > 2)	// assume hours and minutes entered
       {
           tmin = ttime.right(2);
           nmin = tmin.toInt();
           if(nt >= 100) nt /= 100;	// remove minutes
           else if(b24) nt = 0;
       }
       if(na >= 0 && nt == 12) nt = 0;
       else if(np >= 0 && nt < 12) nt += 12;
       if(nt == 24) nt = 0;
       else if(nt > 24) nt = 12;
       else if(!b24 && na + np == -2	// no am or pm indicated
               && nz > 0 && nt < 6) // assume pm if 12:00 to 5:59
       {
           nt += 12;
       }
       if(nmin >= 60) nmin = 0;
       tmin = QString::number(nmin);
       tmin = nmin < 10 ? tc + tz + tmin : tc + tmin;
       if(b24)
       {
           th = QString::number(nt);
           tm = "";
           if(nt < 10) th = tz + th;
       }
       else
       {
           if(nt < 12) tm = " AM";
           else tm = " PM";
           if(nt == 0) th = "12";
           else if(nt > 12) th = QString::number(nt-12);
           else th = QString::number(nt);
       }
       *time = th + tmin + tm;
       return QTime(nt, nmin);
}

QString tmat(bool bshort, int mat)
{
    QString tMat[26];
    if(bshort)
    {
        tMat[0] = QObject::tr("PCC");
        tMat[1] = QObject::tr("HMA");
        tMat[2] = QObject::tr("Old HMA");
        tMat[3] = QObject::tr("CIR");
        tMat[4] = QObject::tr("SFDR");
        tMat[5] = QObject::tr("BSB");
        tMat[6] = QObject::tr("MSB");
        tMat[7] = QObject::tr("RPCC");

        tMat[10] = QObject::tr("AggBase");
        tMat[11] = QObject::tr("Subbase");
        tMat[12] = QObject::tr("StabSoil");
        tMat[13] = QObject::tr("EngSoil");
        tMat[14] = QObject::tr("UndSoil");

        tMat[20] = QObject::tr("Bedrock");
        tMat[21] = QObject::tr("Gdwater");

        tMat[25] = QObject::tr("Other");
    }
    else
    {
        tMat[0] = QObject::tr("New Concrete");
        tMat[1] = QObject::tr("Hot-Mix Asphalt");
        tMat[2] = QObject::tr("Old HMA");
        tMat[3] = QObject::tr("Cold In-Place Recycling");
        tMat[4] = QObject::tr("Stabilized Full-Depth Reclaim");
        tMat[5] = QObject::tr("Bituminous Stabilized Base");
        tMat[6] = QObject::tr("Mineral Stabilized Base");
        tMat[7] = QObject::tr("Rubblized Concrete");

        tMat[10] = QObject::tr("Aggregate Base");
        tMat[11] = QObject::tr("Aggregate Subase");
        tMat[12] = QObject::tr("Stabilized Soil");
        tMat[13] = QObject::tr("Engineered Soil");
        tMat[14] = QObject::tr("Undisturbed Soil");

        tMat[20] = QObject::tr("Bedrock");
        tMat[21] = QObject::tr("Groundwater");

        tMat[25] = QObject::tr("Other");
    }
    return tMat[mat];
}

QString tdet(bool bshort, int mat, int det)
{
    int i;
    QString tDet[64];
    for(i = 0; i < 64; ++i) tDet[i] = "";

    if(mat > 0 && mat < 3)	// HMA
    {
        // Viscosity Grades
        tDet[0] = QObject::tr("AC 2.5");
        tDet[1] = QObject::tr("AC 5");
        tDet[2] = QObject::tr("AC 10");
        tDet[3] = QObject::tr("AC 20");
        tDet[4] = QObject::tr("AC 40");
        // Penetration Grades
        tDet[5] = QObject::tr("40/50 Pen.");
        tDet[6] = QObject::tr("60/70 Pen.");
        tDet[7] = QObject::tr("85/100 Pen.");
        tDet[8] = QObject::tr("120/150 Pen.");
        tDet[9] = QObject::tr("200/300 Pen.");
        // PG Grades
        tDet[10] = QObject::tr("N/A");	// user defined
        tDet[11] = QObject::tr("N/A");
        tDet[12] = QObject::tr("N/A");
        tDet[13] = QObject::tr("N/A");
        tDet[14] = QObject::tr("PG46-34");
        tDet[15] = QObject::tr("PG46-40");
        tDet[16] = QObject::tr("PG46-46");
        tDet[17] = QObject::tr("PG52-10");
        tDet[18] = QObject::tr("PG52-16");
        tDet[19] = QObject::tr("PG52-22");
        tDet[20] = QObject::tr("PG52-28");
        tDet[21] = QObject::tr("PG52-34");
        tDet[22] = QObject::tr("PG52-40");
        tDet[23] = QObject::tr("PG52-46");
        tDet[24] = QObject::tr("PG58-10");
        tDet[25] = QObject::tr("PG58-16");
        tDet[26] = QObject::tr("PG58-22");
        tDet[27] = QObject::tr("PG58-28");
        tDet[28] = QObject::tr("PG58-34");
        tDet[29] = QObject::tr("PG58-40");
        tDet[30] = QObject::tr("PG58-46");
        tDet[31] = QObject::tr("PG64-10");
        tDet[32] = QObject::tr("PG64-16");
        tDet[33] = QObject::tr("PG64-22");
        tDet[34] = QObject::tr("PG64-28");
        tDet[35] = QObject::tr("PG64-34");
        tDet[36] = QObject::tr("PG64-40");
        tDet[37] = QObject::tr("N/A");
        tDet[38] = QObject::tr("PG70-10");
        tDet[39] = QObject::tr("PG70-16");
        tDet[40] = QObject::tr("PG70-22");
        tDet[41] = QObject::tr("PG70-28");
        tDet[42] = QObject::tr("PG70-34");
        tDet[43] = QObject::tr("PG70-40");
        tDet[44] = QObject::tr("N/A");
        tDet[45] = QObject::tr("PG76-10");
        tDet[46] = QObject::tr("PG76-16");
        tDet[47] = QObject::tr("PG76-22");
        tDet[48] = QObject::tr("PG76-28");
        tDet[49] = QObject::tr("PG76-34");
        tDet[50] = QObject::tr("N/A");
        tDet[51] = QObject::tr("N/A");
        tDet[52] = QObject::tr("PG82-10");
        tDet[53] = QObject::tr("PG82-16");
        tDet[54] = QObject::tr("PG82-22");
        tDet[55] = QObject::tr("PG82-28");
        tDet[56] = QObject::tr("PG82-34");
        tDet[57] = QObject::tr("N/A");
        tDet[58] = QObject::tr("N/A");

        tDet[61] = QObject::tr(": Multi");
        tDet[62] = QObject::tr(": Other");
        tDet[63] = QObject::tr("Click Here");

        if(bshort)
        {
            tDet[63] = QObject::tr("undefined");
            tDet[det].replace(QObject::tr(" Pen."),QObject::tr(""));
        }
        return tDet[det];
    }
    else if(mat > 9)
    {
        if(mat < 12)	// base
        {
            if(bshort)
            {
                // Aggregate Base
                tDet[0] = QObject::tr(": FDR");
                tDet[1] = QObject::tr(": Cl.6");
                tDet[2] = QObject::tr(": Cl.5");

                // Aggregate Subbase
                tDet[10] = QObject::tr(": Cl.4");
                tDet[11] = QObject::tr(": Cl.3");
                tDet[12] = QObject::tr(": SelGr");
                tDet[13] = QObject::tr(": Gran");

                tDet[61] = QObject::tr(": Multi");
                tDet[62] = QObject::tr(": Other");
                tDet[63] = QObject::tr(": undefined");
            }
            else
            {
                // Aggregate Base
                tDet[0] = QObject::tr("FDR");
                tDet[1] = QObject::tr("MnDOT Class 6");
                tDet[2] = QObject::tr("MnDOT Class 5");

                // Aggregate Subbase
                tDet[10] = QObject::tr("MnDOT Class 4");
                tDet[11] = QObject::tr("MnDOT Class 3");
                tDet[12] = QObject::tr("MnDOT Select Granular");
                tDet[13] = QObject::tr("MnDOT Granular");

                tDet[61] = QObject::tr("Multi-Layer");
                tDet[62] = QObject::tr("Other");
                tDet[63] = QObject::tr("Click Here");
            }
            return tDet[det];
        }
        else if(mat < 15)	// soil
        {
            if(bshort)
            {
                // Soil
                // MN Classes
                tDet[0] = QObject::tr(": G");
                tDet[1] = QObject::tr(": Sa");
                tDet[2] = QObject::tr(": LSa");
                tDet[3] = QObject::tr(": SaL(p)");
                tDet[4] = QObject::tr(": SaL(sp)");
                tDet[5] = QObject::tr(": L");
                tDet[6] = QObject::tr(": SiL(p)");
                tDet[7] = QObject::tr(": SiL(sp)");
                tDet[8] = QObject::tr(": Si");
                tDet[9] = QObject::tr(": SaCL");
                tDet[10] = QObject::tr(": CL");
                tDet[11] = QObject::tr(": SiCL");
                tDet[12] = QObject::tr(": SaC");
                tDet[13] = QObject::tr(": SiC");
                tDet[14] = QObject::tr(": C");
                // AASHTO Classes
                tDet[20] = QObject::tr(": A-1-a");
                tDet[21] = QObject::tr(": A-1-b");
                tDet[22] = QObject::tr(": A-3");
                tDet[23] = QObject::tr(": A-2-4");
                tDet[24] = QObject::tr(": A-2-5");
                tDet[25] = QObject::tr(": A-2-6");
                tDet[26] = QObject::tr(": A-2-7");
                tDet[27] = QObject::tr(": A-4");
                tDet[28] = QObject::tr(": A-5");
                tDet[29] = QObject::tr(": A-6");
                tDet[30] = QObject::tr(": A-7-5");
                tDet[31] = QObject::tr(": A-7-6");
                // Unified Classes
                tDet[40] = QObject::tr(": GW");
                tDet[41] = QObject::tr(": GP");
                tDet[42] = QObject::tr(": GM");
                tDet[43] = QObject::tr(": GC");
                tDet[44] = QObject::tr(": SW");
                tDet[45] = QObject::tr(": SP");
                tDet[46] = QObject::tr(": SM");
                tDet[47] = QObject::tr(": SC");
                tDet[48] = QObject::tr(": ML");
                tDet[49] = QObject::tr(": CL");
                tDet[50] = QObject::tr(": OL");
                tDet[51] = QObject::tr(": MH");
                tDet[52] = QObject::tr(": CH");
                tDet[53] = QObject::tr(": OH");
                tDet[54] = QObject::tr(": Pt");

                tDet[62] = QObject::tr(": Other");
                tDet[63] = QObject::tr(": undefined");

            }
            else
            {
                // Eng. Soil
                tDet[0] = QObject::tr("Gravel");
                tDet[1] = QObject::tr("Sand");
                tDet[2] = QObject::tr("Loamy Sand");
                tDet[3] = QObject::tr("Sandy Loam (plastic)");
                tDet[4] = QObject::tr("Sandy Loam (slightly plastic)");
                tDet[5] = QObject::tr("Loam");
                tDet[6] = QObject::tr("Silt Loam (plastic)");
                tDet[7] = QObject::tr("Silt Loam (slightly plastic)");
                tDet[8] = QObject::tr("Silt");
                tDet[9] = QObject::tr("Sandy Clay Loam");
                tDet[10] = QObject::tr("Clay Loam");
                tDet[11] = QObject::tr("Silty Clay Loam");
                tDet[12] = QObject::tr("Sandy Clay");
                tDet[13] = QObject::tr("Silty Clay");
                tDet[14] = QObject::tr("Clay");

                tDet[20] = QObject::tr("AASHTO A-1-a");
                tDet[21] = QObject::tr("AASHTO A-1-b");
                tDet[22] = QObject::tr("AASHTO A-3");
                tDet[23] = QObject::tr("AASHTO A-2-4");
                tDet[24] = QObject::tr("AASHTO A-2-5");
                tDet[25] = QObject::tr("AASHTO A-2-6");
                tDet[26] = QObject::tr("AASHTO A-2-7");
                tDet[27] = QObject::tr("AASHTO A-4");
                tDet[28] = QObject::tr("AASHTO A-5");
                tDet[29] = QObject::tr("AASHTO A-6");
                tDet[30] = QObject::tr("AASHTO A-7-5");
                tDet[31] = QObject::tr("AASHTO A-7-6");

                tDet[40] = QObject::tr("Unified GW");
                tDet[41] = QObject::tr("Unified GP");
                tDet[42] = QObject::tr("Unified GM");
                tDet[43] = QObject::tr("Unified GC");
                tDet[44] = QObject::tr("Unified SW");
                tDet[45] = QObject::tr("Unified SP");
                tDet[46] = QObject::tr("Unified SM");
                tDet[47] = QObject::tr("Unified SC");
                tDet[48] = QObject::tr("Unified ML");
                tDet[49] = QObject::tr("Unified CL");
                tDet[50] = QObject::tr("Unified OL");
                tDet[51] = QObject::tr("Unified MH");
                tDet[52] = QObject::tr("Unified CH");
                tDet[53] = QObject::tr("Unified OH");
                tDet[54] = QObject::tr("Unified Pt");

                tDet[62] = QObject::tr("Other");
                tDet[63] = QObject::tr("Click Here");
            }
            return tDet[det];
        }
    }
    return "";
}

QPoint mpText(QPainter* painter, QFont font, QPoint start, QString text, int vert, bool italOffset)
{	// vert -1: subscript, 0: normal, 1: superscript
    int xs, ys, os, w;
    QFont oldFont;
    QFontMetrics fmOld(font);
    Qt::BGMode nbk = painter->backgroundMode(); // save existing mode in case it is opaque
    painter->setBackgroundMode(Qt::TransparentMode);
    // save existing font
    oldFont = painter->font();
    double size = (double) fmOld.height();
    double pwr = (double) abs(vert);
    font.setPointSizeF(font.pointSizeF() * pow(0.65, pwr)); // reduce size for sub & super
    os = nint(rnd2dec(size * -pow(0.25, pwr) * vert * pow(pwr, 0.25) + size * pow(0.15, pwr) * pow(pwr, 2.0),0));
    xs = start.x();
    ys = start.y() + os;

    if(pwr > 0) xs -= size/12.;
    if(italOffset && font.italic() && vert < 0) xs -= os / 4.; // adjust horiz. placement of subscript
    painter->setFont(font);
    painter->drawText(xs, ys, text);
    QFontMetrics fm(font);

    painter->setFont(oldFont);
    painter->setBackgroundMode(nbk);
    w = fm.width(text) * font.pointSizeF() * 0.85; // convert pixels to points?
    return QPoint(xs + w + nint(rnd2dec(size/8.,0)), start.y());
}

void debugmsg(QString tmsg, QString tval)
{
    QMessageBox msgBox;
    msgBox.setText(tmsg + " " + tval);
    msgBox.exec();
}
