/*****************************************************************************
 * $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 "FrameOfReference.h"

namespace camitk {

// -------------------- FrameOfReference constructors --------------------
FrameOfReference::FrameOfReference(): index(-1) {}

FrameOfReference::FrameOfReference(QString name, QString description): index(-1) {
    setName(name);
    setDescription(description);
}

FrameOfReference::FrameOfReference(QUuid uuid, QString name, QString description, int numberOfDimensions, const AnatomicalOrientation& ao, std::vector<Unit>& units): uuid(uuid), anatomicalOrientation(ao), numberOfDimensions(numberOfDimensions), units(units), index(-1) {
    setName(name);
    setDescription(description);
}

FrameOfReference::FrameOfReference(const FrameOfReference& fr): FrameOfReference(fr.name, fr.description)  {
    uuid = QUuid::createUuid();
    numberOfDimensions = fr.numberOfDimensions;
    anatomicalOrientation = fr.anatomicalOrientation;
    units = fr.units;
}


// -------------------- setUuid --------------------
bool FrameOfReference::resetUuid(QUuid newId) {
    uuid = newId;
    return true;
}

// -------------------- setNumberOfDimensions --------------------
void FrameOfReference::setNumberOfDimensions(int numberOfDimensions) {
    if (numberOfDimensions > 0 && numberOfDimensions <= 5) {
        this->numberOfDimensions = numberOfDimensions;
        if (units.size() < numberOfDimensions) {
            units.resize(numberOfDimensions, "");
        }
    } // >5 numberOfDimensions is NOT supported
}

// -------------------- getIndex --------------------
int FrameOfReference::getIndex() {
    // ensure index is initialized
    init();
    return index;
}

// -------------------- getUnit --------------------
Unit FrameOfReference::getUnit(int dimension) {
    if (dimension >= 0 && dimension < numberOfDimensions) {
        return units[dimension];
    }
    else {
        return Unit();
    }
}

// -------------------- setUnit --------------------
void FrameOfReference::setUnit(int dimension, Unit u) {
    if (dimension >= 0 && dimension < numberOfDimensions) {
        units[dimension] = u;
    }
}

// -------------------- setUuid --------------------
bool FrameOfReference::setUuid(QUuid newId) {
    if (uuid.isNull()) {
        uuid = newId;
        return true;
    }
    else {
        return false;
    }
}

// -------------------- toVariant --------------------
QVariant FrameOfReference::toVariant() const {
    QStringList unitsList(units.begin(), units.end());
    QVariantMap map = {
        {"uuid", uuid},
        {"name", name},
        {"description", description},
        {"numberOfDimensions", numberOfDimensions},
        {"units", unitsList},
        {"anatomicalOrientation", anatomicalOrientation.toVariant()}
    };
    return map;
}

// -------------------- fromVariant --------------------
void FrameOfReference::fromVariant(const QVariant& variant) {
    QVariantMap data = variant.toMap();
    uuid = data.value("uuid").toUuid();
    name = data.value("name").toString();
    description = data.value("description").toString();
    numberOfDimensions = data.value("numberOfDimensions", 3).toInt();
    QStringList qUnits = data.value("units").toStringList();
    units = std::vector<Unit>(qUnits.constBegin(), qUnits.constEnd());
    anatomicalOrientation.fromVariant(data.value("anatomicalOrientation"));
}
// -------------------- init --------------------
void FrameOfReference::init() {
    if (index == -1) {
        auto [newColor, newIndex] = FrameOfReference::getNextColorAndIndex();
        color = newColor;
        index = newIndex; // world will have index 0, and other subsequent index will start at 1
    }
}

// -------------------- setColor --------------------
void FrameOfReference::setColor(const QColor& color) {
    // ensure index is valid
    init();
    this->color = color;
}

// -------------------- getColor --------------------
const QColor& FrameOfReference::getColor() {
    // ensure color is initialized
    init();
    return color;
}

// -------------------- getNextColorAndIndex --------------------
QPair<QColor, int> FrameOfReference::getNextColorAndIndex() {
    static int index = -1;
    static QList<QColor> colors = QList<QColor>({
        QColorConstants::White, // for world
        // -- R set3 palette
        // from the Qt doc:
        // lighter = multiplies the value (V) component by the given factor
        QColor("#8DD3C7"),
        QColor("#FFFFB3"),
        QColor("#BEBADA"),
        QColor("#FB8072"),
        QColor("#80B1D3"),
        QColor("#FDB462"),
        QColor("#B3DE69"),
        QColor("#FCCDE5"),
        QColor("#D9D9D9"),
        QColor("#BC80BD"),
        QColor("#CCEBC5"),
        QColor("#FFED6F"),
        // -- initial colors from D3 category20
        // QColor("#aec7e8"), QColor("#ffbb78"), QColor("#98df8a"), QColor("#ff9896"), QColor("#c5b0d5"), QColor("#c49c94"), QColor("#f7b6d2"), QColor("#c7c7c7"), QColor("#dbdb8d"), QColor("#9edae5")
        // -- R Accent palette
        // QColor("#7FC97F"), QColor("#BEAED4"), QColor("#FDC086"), QColor("#FFFF99"), QColor("#386CB0"), QColor("#F0027F"), QColor("#BF5B17"), QColor("#666666"), QColor("#B3E2CD")
    });
    // go to next color
    index++;
    QColor currentColor = colors.at(index % colors.size());
    if (index > 0 && currentColor == QColorConstants::White) {
        currentColor.setHsv(currentColor.hue(), currentColor.saturation(), 200);
    }
    // if the first colors are used (index >= 12), add some hue
    currentColor.setHsv(currentColor.hue() + (16 * (index / colors.size())) % 255, currentColor.saturation(), currentColor.value());
    return {currentColor, index};
}

} // namespace