#include <cmath>
#include <iostream>

#include <QFile>
#include <QRegExp>
#include <QString>
#include <QStringList>

using namespace std;

const int minPrefixExp = -24;
const int maxPrefixExp = 24;
const int numPrefix = int((maxPrefixExp - minPrefixExp) / 3) + 1;
const QString SIprefix[] = {"y", "z", "a", "f", "p", "n", QChar(0xB5), "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"};

/**
 * Converts a number string (including an optional SI suffix) to a real number.
 */
double toReal(QString text);

const QString inputFile = "spicemodels_bipolar_transistors.txt";
const QString outputFile = "output.lib";

#define setProperty(property, value) outputStream << QString("%1=%2\n").arg(property).arg(value)

int main()
{
    QFile input(inputFile);
    if (!input.open(QIODevice::ReadOnly)) {
        cerr << "Could not open input file \"" << inputFile << "\" for reading.\n";
        return 1;
    }

    QFile output(outputFile);
    if (!output.open(QIODevice::WriteOnly)) {
        cerr << "Could not open output file \"" << outputFile << "\" for writing.\n";
        return 1;
    }

    QTextStream inputStream(&input);
    QTextStream outputStream(&output);

    QStringList IDs;

    // Set true inside the while loop so when we come across a new model
    // in the input file, we can close the previous one in the output file.
    bool modelOpen = false;

    while (!inputStream.atEnd()) {
        QString line = inputStream.readLine();

        // Make life easier
        line.replace(QRegExp("\\.MODEL[^(]*\\("), "+ ");
        line.remove(")");

        if (line.isEmpty())
            continue;

        if (line.startsWith("*SRC=")) {
            if (modelOpen)
                outputStream << "[/]\n\n";
            modelOpen = true;

            line.remove(0, 5); // remove "*SRC="

            QStringList segments = line.split(';'); // QStringList::split( ';', line ); // 2018.12.01
            if (segments.size() != 5) {
                cerr << "Wrong number of \";\" for line \"" << line.latin1() << "\".\n";
                continue;
            }

            QStringList::iterator segmentsEnd = segments.end();
            for (QStringList::iterator it = segments.begin(); it != segmentsEnd; ++it)
                *it = (*it).trimmed();

            QString ID = segments[0];
            outputStream << QString("[%1]\n").arg(ID);
            if (IDs.contains(ID)) {
                cerr << "Already have ID \"" << ID << "\".\n";
                continue;
            } else
                IDs.append(ID);

            if (segments[2] == "BJTs PNP")
                setProperty("Type", "PNP");
            else if (segments[2] == "BJTs NPN")
                setProperty("Type", "NPN");
            else
                cerr << "Unknown type \"" << segments[2] << "\".\n";

            setProperty("Description", segments[4]);
        }

        else if (line.startsWith("+ ")) {
            line.remove(0, 2); // remove "+ ":
            QRegExp rx("([^\\s=]*)=([^\\s]*)");

            int pos = 0;
            while ((pos = rx.search(line, pos)) >= 0) {
                QString property = rx.cap(1);
                QString value = rx.cap(2);

                setProperty(property, toReal(value));

                pos += 4; // avoid the string we just found
            }
        }

        else
            cerr << "Unknown line for line \"" << line.latin1() << "\".\n";
    }

    // And the final closing brace
    if (modelOpen)
        outputStream << "[/]\n\n";

    input.close();
    output.close();

    return 0;
}

double getMultiplier(QString mag)
{
    if (mag.isEmpty())
        return 1.0;

    // Allow the user to enter in "u" instead of mu
    if (mag == "u")
        mag = QChar(0xB5);

    for (int i = 0; i < numPrefix; ++i) {
        if (mag == SIprefix[i])
            return pow(10., (i * 3) + minPrefixExp);
    }

    // default
    return 1.0;
}

double toReal(QString text)
{
    if (text.isEmpty()) {
        cerr << "Attempted to convert empty text to number.\n";
        return 0.0;
    }

    // Extract the possible SI suffix
    QChar suffix = text[text.length() - 1];
    double multiplier = 1.0;
    if (suffix.isLetter()) {
        multiplier = getMultiplier(QString(suffix));
        text.remove(text.length() - 1, 1);
    }

    bool ok;
    double val = text.toDouble(&ok);

    if (!ok) {
        cerr << "Could not convert number string \"" << text << "\" to real.\n";
        return 0.0;
    }

    return val * multiplier;
}
