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

#include <Component.h>
#include <Property.h>

#include <QtVariantPropertyManager>
#include <QVector3D>
#include <QtGui/QIcon>
#include <QRegularExpression>

using namespace camitk;


// --------------- Constructor -------------------
PropAction::PropAction(ActionExtension* extension) : Action(extension) {
    // Setting name, description and input component
    setName("Add Lots Of Properties");
    setDescription("Add lots of dynamic property to the currently selected component.");
    setComponentClassName("Component");

    // Setting classification family and tags
    setFamily("Tutorial");
    addTag("Dynamic Property");
    addTag("Component");

    // Setting the action's parameters
    addParameter(new Property("Bool Prop", false, "This a <i>normal</i> bool property.<br/><b>Note:</b> Rich text description!<br/>See also: <a href=\"http://camitk.imag.fr\">CamiTK web page</a>", ""));

    Property* readOnlyBool = new Property("Read Only Bool", true, "This a read-only boolean", "");
    readOnlyBool->setReadOnly(true);
    readOnlyBool->setGroupName("Read Only Properties");
    addParameter(readOnlyBool); // the action takes ownership of the Property pointer

    Property* boundedInt = new Property("Bounded Int", 12, "An integer bounded between 0 and 20", "");
    boundedInt->setAttribute("minimum", 0);
    boundedInt->setAttribute("maximum", 20);
    boundedInt->setGroupName("Numeric Properties");
    addParameter(boundedInt);

    Property* doubleWithMax = new Property("Double With Max", -10.0, "A double with a max value of -4.2", "");
    doubleWithMax->setAttribute("maximum", -4.2);
    doubleWithMax->setGroupName("Numeric Properties");
    addParameter(doubleWithMax);

    Property* intWithSingleStep = new Property("Int With Single Step", -10, "An integer with a single step of <i>5</i>", "");
    intWithSingleStep->setAttribute("singleStep", 5);
    intWithSingleStep->setGroupName("Numeric Properties");
    addParameter(intWithSingleStep);

    Property* doubleWithStepAndDecimal = new Property("Double With Single Step And Precision", 3.14159, "A double with 5 decimals and a single step of 1.10<sup>-5</sup>", "");
    doubleWithStepAndDecimal->setAttribute("singleStep", 10e-6);
    doubleWithStepAndDecimal->setAttribute("decimals", 5);
    doubleWithStepAndDecimal->setGroupName("Numeric Properties");
    addParameter(doubleWithStepAndDecimal);

    Property* intWithDecimal = new Property("Int With Precision", 4, "An integer with a precision set to 5 decimals: this should not affect it.", "");
    intWithDecimal->setAttribute("decimals", 5);
    intWithDecimal->setGroupName("Numeric Properties");
    addParameter(intWithDecimal);

    Property* readOnlyQVector3D = new Property("Read Only QVector3D", QVector3D(-4.0, 2.0, 0.1), "A read-only QVector3D", "");
    readOnlyQVector3D->setReadOnly(true);
    readOnlyQVector3D->setGroupName("Read Only Properties");
    addParameter(readOnlyQVector3D);

    Property* stringWithRegExp = new Property("QString Constrained by RegExp", QString("loweronly"), "A QString contrained to lowercase characters only (no separators, numbers...)", "");
    stringWithRegExp->setAttribute("regExp", QRegularExpression("[a-z]*"));
    addParameter(stringWithRegExp);

    Property* constrainedQRect = new Property("Constrained QRect", QRect(10, 10, 20, 20), "A QRect constrained to (0,0,50,50)", "");
    constrainedQRect->setAttribute("constraint", QRect(0, 0, 50, 50));
    addParameter(constrainedQRect);

    Property* constrainedQVector3D = new Property("Constrained QVector3D", QVector3D(1.1, 2.2, 3.3), "A constrained QVector3D (not yet implemented)", "");
    constrainedQVector3D->setAttribute("constraint", QVector3D(10.0, 10.0, 10.0));
    addParameter(constrainedQVector3D);

    // build the list of QString that corresponds to the possible enum values
    Property* enumList = new Property("Enumeration", PropAction::JACK, "An enum using different GUI strings", "");

    // auto set the name from the enum literals
    enumList->setEnumTypeName("Enumeration", this);

    // Change the icons
    QtIconMap enumIcons;
    enumIcons[0] = QIcon(":/ace");
    enumIcons[1] = QIcon(":/king");
    enumIcons[2] = QIcon(":/queen");
    enumIcons[3] = QIcon(":/jack");
    enumIcons[4] = QIcon(":/ten");
    enumIcons[5] = QIcon(":/nine");
    enumIcons[6] = QIcon(":/eight");
    enumIcons[7] = QIcon(":/seven");
    enumList->setEnumIcons(enumIcons);
    addParameter(enumList);
}

// --------------- apply -------------------
Action::ApplyStatus PropAction::apply() {
    // loop on all dynamic properties of this action (i.e. the action parameters) and
    // add each of them as a property of the currently selected components

    for (Component* comp : getTargets()) {
        QList<QByteArray> dynProp = dynamicPropertyNames();

        // loop on all dynamic properties
        for (int idx = 0; idx < dynProp.size(); ++idx) {
            QString propName = QString(dynProp.at(idx));
            QVariant propValue = property(dynProp.at(idx));
            Property* actionParam = getProperty(propName);
            if (actionParam) {
                // copy the property
                Property* newProp = nullptr;
                if (actionParam->getEnumTypeName().isNull()) {
                    newProp = new Property(propName, propValue, actionParam->getDescription(), "");
                }
                else {
                    // specific for enums
                    newProp = new Property(propName, (Enumeration) propValue.toInt(), actionParam->getDescription(), "");
                }

                // copy all attributes
                newProp->setReadOnly(actionParam->getReadOnly());
                for (QString attributeName : actionParam->getAttributeList()) {
                    newProp->setAttribute(attributeName, actionParam->getAttribute(attributeName));
                }
                newProp->setEnumTypeName(actionParam->getEnumTypeName(), this);
                newProp->setEnumIcons(actionParam->getEnumIcons());
                newProp->setGroupName(actionParam->getGroupName());
                // add the prop to the component (the component takes ownership of the Property pointer)
                comp->addProperty(newProp);
            }

        }
        // refresh viewers
        comp->refresh();
    }

    return SUCCESS;
}


