/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2024 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$
 ****************************************************************************/

// Local
#include "FrameEditor.h"

// CamiTK
#include <Log.h>

using namespace camitk;

// --------------- constructor -------------------
FrameEditor::FrameEditor(ActionExtension* extension) : Action(extension) {
    setName("Frame Editor");
    setDescription(tr("Modify the component's frame translation and rotation."));
    setComponentClassName("Component");

    // Setting classification family and tags
    setFamily("Frame");
    addTag(tr("Edit"));
    addTag(tr("Move"));
    addTag(tr("Translate"));
    addTag(tr("Rotate"));
    addTag(tr("Visualization"));

    myWidget = nullptr;
}


// --------------- init -------------------
void FrameEditor::init() {
    myWidget = new QWidget();
    ui.setupUi(myWidget);

    transfromMatrixElements.resize(4);
    for (int j = 0; j < 4; j++) {
        transfromMatrixElements[j].resize(4);
        for (int i = 0; i < 4; i++) {
            auto* tmpLineEdit = new QLineEdit();
            tmpLineEdit->setText("line: " + QString::number(i) + ", column: " + QString::number(j));
            transfromMatrixElements[j][i] = tmpLineEdit;
            ui.transformationGridLayout->addWidget(tmpLineEdit, i + 1, j + 1);
        }
    }

    // Reset tab order
    // First Line
    myWidget->setTabOrder(transfromMatrixElements[0][0], transfromMatrixElements[1][0]);
    myWidget->setTabOrder(transfromMatrixElements[1][0], transfromMatrixElements[2][0]);
    myWidget->setTabOrder(transfromMatrixElements[2][0], transfromMatrixElements[3][0]);
    myWidget->setTabOrder(transfromMatrixElements[3][0], transfromMatrixElements[0][1]);
    // Second Line
    myWidget->setTabOrder(transfromMatrixElements[0][1], transfromMatrixElements[1][1]);
    myWidget->setTabOrder(transfromMatrixElements[1][1], transfromMatrixElements[2][1]);
    myWidget->setTabOrder(transfromMatrixElements[2][1], transfromMatrixElements[3][1]);
    myWidget->setTabOrder(transfromMatrixElements[3][1], transfromMatrixElements[0][2]);
    // Third Line
    myWidget->setTabOrder(transfromMatrixElements[0][2], transfromMatrixElements[1][2]);
    myWidget->setTabOrder(transfromMatrixElements[1][2], transfromMatrixElements[2][2]);
    myWidget->setTabOrder(transfromMatrixElements[2][2], transfromMatrixElements[3][2]);
    myWidget->setTabOrder(transfromMatrixElements[3][2], transfromMatrixElements[0][3]);
    // Forth Line
    myWidget->setTabOrder(transfromMatrixElements[0][3], transfromMatrixElements[1][3]);
    myWidget->setTabOrder(transfromMatrixElements[1][3], transfromMatrixElements[2][3]);
    myWidget->setTabOrder(transfromMatrixElements[2][3], transfromMatrixElements[3][3]);
    myWidget->setTabOrder(transfromMatrixElements[3][3], ui.setTrasformationPushButton);

    myWidget->setTabOrder(ui.setTrasformationPushButton, ui.xRotationLineEdit);
    myWidget->setTabOrder(ui.xRotationLineEdit, ui.yRotationLineEdit);
    myWidget->setTabOrder(ui.yRotationLineEdit, ui.zRotationLineEdit);
    myWidget->setTabOrder(ui.zRotationLineEdit, ui.rotatePushButton);
    myWidget->setTabOrder(ui.rotatePushButton, ui.setRotationPushButton);

    myWidget->setTabOrder(ui.setRotationPushButton, ui.xTranslationLineEdit);
    myWidget->setTabOrder(ui.xTranslationLineEdit, ui.yTranslationLineEdit);
    myWidget->setTabOrder(ui.yTranslationLineEdit, ui.zTranslationLineEdit);
    myWidget->setTabOrder(ui.zTranslationLineEdit, ui.translatePushButton);
    myWidget->setTabOrder(ui.translatePushButton, ui.setTranslationPushButton);

    // Connect to this...
    connect(ui.setTrasformationPushButton, SIGNAL(clicked()), this, SLOT(setTransformation()));
    connect(ui.rotatePushButton, SIGNAL(clicked()), this, SLOT(rotate()));
    connect(ui.setRotationPushButton, SIGNAL(clicked()), this, SLOT(setRotation()));
    connect(ui.translatePushButton, SIGNAL(clicked()), this, SLOT(translate()));
    connect(ui.setTranslationPushButton, SIGNAL(clicked()), this, SLOT(setTranslation()));

    for (int j = 0; j < 4; j++) {
        for (int i = 0; i < 4; i++) {
            connect(transfromMatrixElements[j][i], SIGNAL(returnPressed()), this, SLOT(transformationChanged()));
        }
    }

    connect(ui.xTranslationLineEdit, SIGNAL(returnPressed()), this, SLOT(translationChanged()));
    connect(ui.yTranslationLineEdit, SIGNAL(returnPressed()), this, SLOT(translationChanged()));
    connect(ui.zTranslationLineEdit, SIGNAL(returnPressed()), this, SLOT(translationChanged()));

    connect(ui.xRotationLineEdit, SIGNAL(returnPressed()), this, SLOT(rotationChanged()));
    connect(ui.yRotationLineEdit, SIGNAL(returnPressed()), this, SLOT(rotationChanged()));
    connect(ui.zRotationLineEdit, SIGNAL(returnPressed()), this, SLOT(rotationChanged()));

}


// --------------- destructor -------------------
FrameEditor::~FrameEditor() {
    if (myWidget) {
        delete myWidget;
    }
}

// --------------- getWidget -------------------
QWidget* FrameEditor::getWidget() {
    if (!myWidget) {
        init();
    }

    currentComponent = getTargets().last();
    initializeDialogWithCurrentComponent();

    return myWidget;
}


// --------------- initializeDialogWithCurrentComponent -------------------
void FrameEditor::initializeDialogWithCurrentComponent() {

    // Update the transformation Matrix
    vtkSmartPointer<vtkMatrix4x4> currentMatrix = currentComponent->getTransform()->GetMatrix();
    bool transformSignalState = ui.transformationGroupBox->blockSignals(true);
    for (int j = 0; j < 4; j++) {
        for (int i = 0; i < 4; i++) {
            if (fabs(currentMatrix->GetElement(i, j)) < 1e-6) {
                currentMatrix->SetElement(i, j, 0.0);
            }
            transfromMatrixElements[j][i]->setText(QString::number(currentMatrix->GetElement(i, j), 'g', 6));
        }
    }

    ui.transformationGroupBox->blockSignals(transformSignalState);

    auto* pos = new double[3];
    currentComponent->getTransform()->GetPosition(pos);

    bool translationSignalState = ui.translationGroupBox->blockSignals(true);
    for (int i = 0; i < 3; i++) {
        if (fabs(pos[i]) < 1e-6) {
            pos[i] = 0.0;
        }
    }
    ui.xTranslationLineEdit->setText(QString::number(pos[0], 'g', 6));
    ui.yTranslationLineEdit->setText(QString::number(pos[1], 'g', 6));
    ui.zTranslationLineEdit->setText(QString::number(pos[2], 'g', 6));
    ui.translationGroupBox->blockSignals(translationSignalState);

    auto* rotation = new double[3];
    currentComponent->getTransform()->GetOrientation(rotation);

    bool rotationSignalState = ui.rotationGroupBox->blockSignals(true);
    for (int i = 0; i < 3; i++) {
        if (fabs(rotation[i]) < 1e-6) {
            rotation[i] = 0.0;
        }
    }
    ui.xRotationLineEdit->setText(QString::number(rotation[0], 'g', 6));
    ui.yRotationLineEdit->setText(QString::number(rotation[1], 'g', 6));
    ui.zRotationLineEdit->setText(QString::number(rotation[2], 'g', 6));
    ui.rotationGroupBox->blockSignals(rotationSignalState);
}

// --------------- transformationChanged -------------------
void FrameEditor::transformationChanged() {
    // Update corresponding translation and rotation
    vtkSmartPointer<vtkMatrix4x4> matrix = vtkSmartPointer<vtkMatrix4x4>::New();
    for (int j = 0; j < 4; j++) {
        for (int i = 0; i < 4; i++) {
            matrix->SetElement(i, j, transfromMatrixElements[j][i]->text().toDouble());
        }
    }
    vtkSmartPointer<vtkTransform> matrixTransform = vtkSmartPointer<vtkTransform>::New();
    matrixTransform->SetMatrix(matrix);

    auto* pos = new double[3];
    matrixTransform->GetPosition(pos);

    bool translationSignalState = ui.translationGroupBox->blockSignals(true);
    ui.xTranslationLineEdit->setText(QString::number(pos[0]));
    ui.yTranslationLineEdit->setText(QString::number(pos[1]));
    ui.zTranslationLineEdit->setText(QString::number(pos[2]));
    ui.translationGroupBox->blockSignals(translationSignalState);

    auto* rotation = new double[3];
    matrixTransform->GetOrientation(rotation);

    bool rotationSignalState = ui.rotationGroupBox->blockSignals(true);
    ui.xRotationLineEdit->setText(QString::number(rotation[0]));
    ui.yRotationLineEdit->setText(QString::number(rotation[1]));
    ui.zRotationLineEdit->setText(QString::number(rotation[2]));
    ui.rotationGroupBox->blockSignals(rotationSignalState);

}

// --------------- setTransformation -------------------
void FrameEditor::setTransformation() {
    vtkSmartPointer<vtkMatrix4x4> matrix = vtkSmartPointer<vtkMatrix4x4>::New();
    for (int j = 0; j < 4; j++) {
        for (int i = 0; i < 4; i++) {
            matrix->SetElement(i, j, transfromMatrixElements[j][i]->text().toDouble());
        }
    }
    vtkSmartPointer<vtkTransform> matrixTransform = vtkSmartPointer<vtkTransform>::New();
    matrixTransform->SetMatrix(matrix);
    currentComponent->setTransform(matrixTransform);

    Application::refresh();
    initializeDialogWithCurrentComponent();
}

// --------------- translationChanged -------------------
void FrameEditor::translationChanged() {
    CAMITK_TRACE(tr("Frame translation changed"))
}

// --------------- translate -------------------
void FrameEditor::translate() {
    currentComponent->translate(ui.xTranslationLineEdit->text().toDouble(),
                                ui.yTranslationLineEdit->text().toDouble(),
                                ui.zTranslationLineEdit->text().toDouble());

    Application::refresh();
    initializeDialogWithCurrentComponent();
}

// --------------- setTranslation -------------------
void FrameEditor::setTranslation() {
    currentComponent->setTransformTranslationVTK(ui.xTranslationLineEdit->text().toDouble(),
            ui.yTranslationLineEdit->text().toDouble(),
            ui.zTranslationLineEdit->text().toDouble());

    Application::refresh();
    initializeDialogWithCurrentComponent();
}

// --------------- rotationChanged -------------------
void FrameEditor::rotationChanged() {
    CAMITK_TRACE(tr("Frame rotation changed"))
}

// --------------- rotate -------------------
void FrameEditor::rotate() {
    if (ui.setToOriginFirstCheckBox->isChecked()) {
        currentComponent->rotate(ui.xRotationLineEdit->text().toDouble(),
                                 ui.yRotationLineEdit->text().toDouble(),
                                 ui.zRotationLineEdit->text().toDouble());

    }
    else {
        currentComponent->rotateVTK(ui.xRotationLineEdit->text().toDouble(),
                                    ui.yRotationLineEdit->text().toDouble(),
                                    ui.zRotationLineEdit->text().toDouble());
    }

    Application::refresh();
    initializeDialogWithCurrentComponent();
}

// --------------- setRotation -------------------
void FrameEditor::setRotation() {
    if (ui.setToOriginFirstCheckBox->isChecked()) {
        currentComponent->setTransformRotation(ui.xRotationLineEdit->text().toDouble(),
                                               ui.yRotationLineEdit->text().toDouble(),
                                               ui.zRotationLineEdit->text().toDouble());
    }
    else {
        currentComponent->setTransformRotationVTK(ui.xRotationLineEdit->text().toDouble(),
                ui.yRotationLineEdit->text().toDouble(),
                ui.zRotationLineEdit->text().toDouble());

    }
    Application::refresh();
    initializeDialogWithCurrentComponent();
}


// --------------- apply -------------------
Action::ApplyStatus FrameEditor::apply() {
    return SUCCESS;
}

