/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK 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 Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/
#include "ParameterGenerator.h"

#include <QTextStream>
#include <QDate>
#include <QTime>
#include <QStringList>


#include <Parameter.hxx>
#include <ParameterType.hxx>

// to debug..
#include <iostream>

using namespace cepcoreschema;

static bool isInitialized = false;

ParameterGenerator::ParameterGenerator(Parameter& domParameter) {
    ParameterGenerator::initNamesAndValues();

    this->name = QString(domParameter.name().c_str());
    this->editable = domParameter.editable();
    this->description = QString(domParameter.description().c_str());

    switch (domParameter.type()) {
        case (ParameterType::int_) :
            this->type = INT;
            break;
        case (ParameterType::bool_) :
            this->type = BOOLEAN;
            break;
        case (ParameterType::double_) :
            this->type = DOUBLE;
            break;
        case (ParameterType::QString) :
            this->type = QSTRING;
            break;
        case (ParameterType::QDate) :
            this->type = QDATE;
            break;
        case (ParameterType::QTime) :
            this->type = QTIME;
            break;
        case (ParameterType::QColor) :
            this->type = QCOLOR;
            break;
        case (ParameterType::QPoint) :
            this->type = QPOINT;
            break;
        case (ParameterType::QPointF) :
            this->type = QPOINTF;
            break;
        case (ParameterType::QVector3D) :
            this->type = QVECTOR3D;
            break;
        case (ParameterType::QVector4D) :
            this->type = QVECTOR4D;
            break;
        default:
            this->type = UNKNOWN;
            break;
    }

    if (domParameter.defaultValue().present()) {
        this->defaultValue = QString(domParameter.defaultValue().get().c_str());
        // add surroundings quotes on string default value
        if (this->type == QSTRING) {
            this->defaultValue = "\"" + this->defaultValue + "\"";
        }
    }
    else {
        this->defaultValue = getDefaultValues()[this->type];
    }

    if (domParameter.unit().present()) {
        this->unit = QString(domParameter.unit().get().c_str());
    }
    else {
        this->unit = QString("");
    }
}


QString ParameterGenerator::getDefaultValue() const {
    return this->defaultValue;
}

QString ParameterGenerator::getName() const {
    return this->name;
}

QString ParameterGenerator::getType() const {
    return getTypeNames().value(this->type);
}

bool ParameterGenerator::isEditable() const {
    return this->editable;
}

bool ParameterGenerator::needsAdditionalInclude() const {
    bool b;
    switch (this->type) {
        case INT:
        case DOUBLE:
        case BOOLEAN:
            b = false;
            break;
        default:
            b = true;
            break;
    }
    return b;
}

QString ParameterGenerator::getAdditionalInclude() const {
    QString add = "";
    if (needsAdditionalInclude()) {
        add = "#include <" + getTypeNames()[this->type] +  ">";
    }

    return add;
}

QString ParameterGenerator::getCppName() const {
    QString cppName = name;
    cppName = cppName.simplified(); // transform all to whitespace
    QStringList cppNameList = cppName.split(" "); // split words
    // lower case for the first letter of the first word
    QString firstWord = cppNameList.takeFirst();
    // only if there is more than one word... (otherwise it means the user has just enter a longNameWithPossiblySomeUpperCaseLetters)
    if (cppNameList.size() > 0) {
        cppName = firstWord.toLower();
    }

    // uppercase the first letter of each word and concatenate
    for (QString s : cppNameList) {
        cppName += s.at(0).toUpper() + s.mid(1).toLower();
    }

    return cppName;
}

QString ParameterGenerator::getQVariantConversion() const {
    QString toType;

    QString typeStr = getTypeNames()[this->type];
    switch (this->type) {
        case INT:
        case BOOLEAN:
        case DOUBLE:
            // using toType() method
            toType = "to" + typeStr.left(1).toUpper() + typeStr.mid(1) + "()";
            break;
        default:
            toType = QString("value<").append(typeStr).append(">()");
            break;
    }
    return toType;
}

QString ParameterGenerator::getPropertyQVariant() const {
    // QVector3D and QColor are not in QtCore
    // but in QtGui, QVariant class could not provide
    // a conversion functions or copy constructor
    // => the operator QVariant() of these classes is needed
    QString toQVariant;
    switch (this->type) {
        case QVECTOR3D:
        case QCOLOR:
            toQVariant  = getTypeNames()[this->type];
        default:
            toQVariant = "QVariant";
    }
    return toQVariant;
}

QString ParameterGenerator::getToString() const {
    QString toString;
    QString cppName = getCppName();
    switch (this->type) {
        case BOOLEAN:
            toString = "(" + cppName + R"(? "true" : "false"))";
            break;
        case INT:
        case DOUBLE:
            toString = "QString::number(" + cppName + ")";
            break;
        case QSTRING:
            // using toType() method
            toString = cppName;
            break;
        case QPOINT:
        case QPOINTF:
            toString = "\"(\" + QString::number(" + cppName + ".x()) + \",\" + QString::number(" + cppName + ".y()) + \")\"";
            break;
        case QVECTOR3D:
            toString = "\"(\" + QString::number(" + cppName + ".x()) + \",\" + QString::number(" + cppName + ".y()) + \",\" + QString::number(" + cppName + ".z()) + \")\"";
            break;
        case QVECTOR4D:
            toString = "\"(\" + QString::number(" + cppName + ".w()) + \",\" + QString::number(" + cppName + ".x()) + \",\" + QString::number(" + cppName + ".y()) + \",\" + QString::number(" + cppName + ".z()) + \")\"";
            break;
        case QCOLOR:
            toString = cppName + ".name()";
            break;
        case QDATE:
            toString = cppName + ".toString(Qt::TextDate)";
            break;
        default:
            toString = cppName + ".toString()";
            break;
    }
    return toString;
}


void ParameterGenerator::initNamesAndValues() {
    if (isInitialized) {
        return;
    }

    getTypeNames().insert(ParameterGenerator::INT, QString("int"));
    getTypeNames().insert(ParameterGenerator::DOUBLE, QString("double"));
    getTypeNames().insert(ParameterGenerator::BOOLEAN, QString("bool"));
    getTypeNames().insert(ParameterGenerator::QSTRING, QString("QString"));
    getTypeNames().insert(ParameterGenerator::QDATE, QString("QDate"));
    getTypeNames().insert(ParameterGenerator::QTIME, QString("QTime"));
    getTypeNames().insert(ParameterGenerator::QCOLOR, QString("QColor"));
    getTypeNames().insert(ParameterGenerator::QPOINT, QString("QPoint"));
    getTypeNames().insert(ParameterGenerator::QPOINTF, QString("QPointF"));
    getTypeNames().insert(ParameterGenerator::QVECTOR3D, QString("QVector3D"));
    getTypeNames().insert(ParameterGenerator::QVECTOR4D, QString("QVector4D"));
    getTypeNames().insert(ParameterGenerator::UNKNOWN, QString("???"));


    getDefaultValues().insert(ParameterGenerator::INT, QString("0"));
    getDefaultValues().insert(ParameterGenerator::DOUBLE, QString("0.0"));
    getDefaultValues().insert(ParameterGenerator::BOOLEAN, QString("false"));
    getDefaultValues().insert(ParameterGenerator::QSTRING, QString("Hello World !"));

    QString defValue = "";
    QDate today = QDate::currentDate();
    QTextStream inDate(&defValue);
    inDate << "QDate(" << today.year() << ", " << today.month() << ", " << today.day() << ")";
    getDefaultValues().insert(ParameterGenerator::QDATE, QString(defValue));
    defValue = "";
    QTime now = QTime::currentTime();
    QTextStream inTime(&defValue);
    inTime << "QTime(" << now.hour() << ", " << now.minute() << ", " << now.second() << ")";
    getDefaultValues().insert(ParameterGenerator::QTIME, QString(defValue));
    getDefaultValues().insert(ParameterGenerator::QCOLOR, QString("QColor(255, 255, 255, 255)"));
    getDefaultValues().insert(ParameterGenerator::QPOINT, QString("QPoint(0, 0)"));
    getDefaultValues().insert(ParameterGenerator::QPOINTF, QString("QPointF(0.0, 0.0)"));
    getDefaultValues().insert(ParameterGenerator::QVECTOR3D, QString("QVector3D(0.0, 0.0, 0.0)"));
    getDefaultValues().insert(ParameterGenerator::QVECTOR4D, QString("QVector4D(0.0, 0.0, 0.0, 0.0)"));
    getDefaultValues().insert(ParameterGenerator::UNKNOWN, QString("0"));

    isInitialized = true;

}


QMap<ParameterGenerator::ParameterTypeGenerator, QString>& ParameterGenerator::getTypeNames() {
    static QMap<ParameterGenerator::ParameterTypeGenerator, QString> typeNames;

    return typeNames;
}

QMap<ParameterGenerator::ParameterTypeGenerator, QString>& ParameterGenerator::getDefaultValues() {
    static QMap<ParameterGenerator::ParameterTypeGenerator, QString> defaultValues;

    return defaultValues;
}

QString ParameterGenerator::getTypeDefaultValue(QString typeName) {
    ParameterGenerator::initNamesAndValues();
    QString defValue = "";

    ParameterGenerator::ParameterTypeGenerator type;

    if (typeName == "int") {
        type = INT;
    }
    else if (typeName == "bool") {
        type = BOOLEAN;
    }
    else if (typeName == "double") {
        type = DOUBLE;
    }
    else if (typeName == "QString") {
        type = QSTRING;
    }
    else if (typeName == "QDate") {
        type = QDATE;
    }
    else if (typeName == "QTime") {
        type = QTIME;
    }
    else if (typeName == "QColor") {
        type = QCOLOR;
    }
    else if (typeName == "QPoint") {
        type = QPOINT;
    }
    else if (typeName == "QPointF") {
        type = QPOINTF;
    }
    else if (typeName == "QVector3D") {
        type = QVECTOR3D;
    }
    else {
        type = UNKNOWN;
    }

    defValue = getDefaultValues()[type];

    return defValue;
}

QString ParameterGenerator::getDescription() const {
    return this->description;
}

QString ParameterGenerator::getUnit() const {
    return this->unit;
}
