/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2016 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
 *
 * 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
    foreach(QString s, cppNameList) {
        cppName += s.left(1).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 INT:
    case BOOLEAN:
    case DOUBLE:
    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 QCOLOR:
        toString = cppName + ".name()";
        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;
}
