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

// Includes Qt
#include <QFileDialog>
#include <QLabel>

// Includes CamiTK
#include <Application.h>
#include <SingleImageComponent.h>
#include <Log.h>

using namespace camitk;

#define HEADER_TABLE_VOXEL "i,j,k,value,view,edit,remove"
#define HEADER_TABLE_IMG "x,y,z,value,view,edit,remove"
#define HEADER_TABLE_REAL "rx,ry,rz,value,view,edit,remove"
#define X_TABLE_INDEX 0
#define Y_TABLE_INDEX 1
#define Z_TABLE_INDEX 2
#define VALUE_TABLE_INDEX 3
#define VIEW_TABLE_INDEX 4
#define EDIT_TABLE_INDEX 5
#define REMOVE_TABLE_INDEX 6

#define NB_TABLE_COLUMNS 7

#define VIEW_TABLE_FILE   ":/resources/viewmagfit.png"
#define EDIT_TABLE_FILE   ":/resources/system_software_update.png"
#define REMOVE_TABLE_FILE ":/resources/editdelete.png"


//------------------------------constructor--------------------------
MultiPickingWidget::MultiPickingWidget(QWidget* parent) : QWidget(parent) {
    ui.setupUi(this);
    img = nullptr;
    pPM = nullptr;
}

//------------------------------updateComponent--------------------------
void MultiPickingWidget::updateComponent(ImageComponent* image) {
    if (image != img) {
        QMap<ImageComponent*, PickedPixelMap*>::const_iterator mapIterator;
        mapIterator =  map.constFind(image);

        // If this is the first time we meet this image
        if (mapIterator == map.constEnd()) {
            img = image;
            pPM = new PickedPixelMap(img);
            map.insert(image, pPM);
        }
        else {   // This image already presents seedpickedcommand and pixelmap
            img = mapIterator.key();
            pPM = (*mapIterator);
        }

        updateTable();
    }

}

PickedPixelMap* MultiPickingWidget::getPickedPixelMap(ImageComponent* image) {
    QMap<ImageComponent*, PickedPixelMap*>::const_iterator mapIterator;
    mapIterator =  map.constFind(image);
    PickedPixelMap* pickedPixelMap;
    if (mapIterator == map.constEnd()) {
        pickedPixelMap = nullptr;
    }
    else {
        pickedPixelMap = (*mapIterator);
    }

    return pickedPixelMap;
}


//-----------------------------updateTable-----------------------------
void MultiPickingWidget::updateTable() {

    ui.pointsTableWidget->setEnabled(false);
    ui.pointsTableWidget->blockSignals(true);

    // initialize QTableWidget
    ui.pointsTableWidget->setRowCount(0);

    // Update the QtableWidget with the pixelIndexList
    if (pPM->getPixelIndexList() != nullptr) {
        ui.pointsTableWidget->setColumnCount(NB_TABLE_COLUMNS);
        ui.pointsTableWidget->horizontalHeaderItem(NB_TABLE_COLUMNS);
        QString headerString; //(HEADER_TABLE);
        QStringList header; // = headerString.split(",");

        if (ui.indexRadioButton->isChecked()) {
            // case of voxel index
            headerString = QString(HEADER_TABLE_VOXEL);
            header = headerString.split(",");
            ui.pointsTableWidget->setHorizontalHeaderLabels(header);
            createItems(pPM->getPixelIndexList(), pPM->getPixelValueList());
        }
        else if (ui.coordinatesRadioButton->isChecked()) {
            // case of image coordinates
            headerString = QString(HEADER_TABLE_IMG);
            header = headerString.split(",");
            ui.pointsTableWidget->setHorizontalHeaderLabels(header);
            createItems(pPM->getCoordIndexList(), pPM->getPixelValueList());
        }
        else {   // ui.worldRadioButton->isChecked()
            headerString = QString(HEADER_TABLE_REAL);
            header = headerString.split(",");
            ui.pointsTableWidget->setHorizontalHeaderLabels(header);
            createItems(pPM->getRealWorldList(), pPM->getPixelValueList());
        }
    }

    resizeGraphicalTable();

    ui.pointsTableWidget->setEnabled(true);
    ui.pointsTableWidget->blockSignals(false);
}

//------------------------addPixelinTABLE_----------------------------
void MultiPickingWidget::addPixelinTable() {
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    pPM->addPixel();
    updateTable();
    // restore the normal cursor
    QApplication::restoreOverrideCursor();
}

//---------------------removePixelFromTable----------------------------
void MultiPickingWidget::removePixelFromTable() {
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    int index = ui.pointsTableWidget->currentRow();
    pPM->removePixel(index);

    // update tableWidget
    updateTable();

    Application::refresh();

    // restore the normal cursor
    QApplication::restoreOverrideCursor();
}

void MultiPickingWidget::removeSeedNumber(int i, int j) {
    QVector3D point;
    double* spacing = img->getImageData()->GetSpacing();
    switch (j) {
        case REMOVE_TABLE_INDEX:
            pPM->removePixel(i);
            // update tableWidget
            updateTable();
            Application::refresh();
            break;
        case EDIT_TABLE_INDEX:
            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
            pPM->modifyPixel(ui.pointsTableWidget->currentRow());
            // update tableWidget
            updateTable();
            // restore the normal cursor
            QApplication::restoreOverrideCursor();
            break;
        case VIEW_TABLE_INDEX:
            point = pPM->getPixelIndexList()->at(ui.pointsTableWidget->currentRow());
            img->pixelPicked(point.x() * spacing[0], point.y() * spacing[1], point.z() * spacing[2]);// the voxel coordinates ar needed here (not the voxel index, nor the voxel world coordinates)
            Application::refresh();
            break;
        default:
            break;
    }
}


//------------------openPixelList------------------
void MultiPickingWidget::openPixelList() {
    // Open a file dialog to ask filename and path where to read the points
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open a pixel list file"), "", tr("CSV Files (*.csv)"));

    if (fileName.isEmpty()) {
        return;
    }
    else {
        pPM->openPixelList(fileName);
        updateTable();
    }


}
//------------------savePixelList------------------
void MultiPickingWidget::savePixelList() {
    // Open a file dialog to ask filename and path where to save the points
    QString fileName = QFileDialog::getSaveFileName(this,
                       tr("Save the picked points"), "" + img->getName(),
                       tr("CSV Files (*.csv)"));

    // Test if the textField for the filename is empty. If true, we do nothing
    if (fileName.isEmpty()) {
        return;
    }
    else {
        //else a CSV file is save at filename location
        pPM->savePixelList(fileName);
    }
}

//------------------setPointTypeTable-----------------
void MultiPickingWidget::setPointTypeTable() {
    //Update the table to have the requested type of points (pixels or coordinates)
    updateTable();
}

//----------------------------createItems-------------------------------
void MultiPickingWidget::createItems(QList< QVector3D >* liste, QList<double>* valueList) {
    QTableWidgetItem* itemTable;
    ui.pointsTableWidget->setRowCount(liste->size());
    QString value;

    for (int i = 0; i < liste->size(); i++) {
        itemTable = new QTableWidgetItem(liste->value(i).x());
        itemTable->setText(value.setNum(liste->value(i).x()));
        ui.pointsTableWidget->setItem(i, X_TABLE_INDEX, itemTable);

        itemTable = new QTableWidgetItem(liste->value(i).y());
        itemTable->setText(value.setNum(liste->value(i).y()));
        ui.pointsTableWidget->setItem(i, Y_TABLE_INDEX, itemTable);

        itemTable = new QTableWidgetItem(liste->value(i).z());
        itemTable->setText(value.setNum(liste->value(i).z()));
        ui.pointsTableWidget->setItem(i, Z_TABLE_INDEX, itemTable);

        itemTable = new QTableWidgetItem(valueList->value(i));
        itemTable->setText(value.setNum(valueList->value(i)));
        ui.pointsTableWidget->setItem(i, VALUE_TABLE_INDEX, itemTable);

        QLabel* lbl = new QLabel();
        lbl->setAlignment(Qt::AlignCenter);
        lbl->setPixmap(QPixmap(VIEW_TABLE_FILE));
        ui.pointsTableWidget->setCellWidget(i, VIEW_TABLE_INDEX, lbl);

        lbl = new QLabel();
        lbl->setAlignment(Qt::AlignCenter);
        lbl->setPixmap(QPixmap(EDIT_TABLE_FILE));
        ui.pointsTableWidget->setCellWidget(i, EDIT_TABLE_INDEX, lbl);

        lbl = new QLabel();
        lbl->setAlignment(Qt::AlignCenter);
        lbl->setPixmap(QPixmap(REMOVE_TABLE_FILE));
        ui.pointsTableWidget->setCellWidget(i, REMOVE_TABLE_INDEX, lbl);

    }
}

//--------------------resizeGraphicalTable-------------------------------
void MultiPickingWidget::resizeGraphicalTable() {
    // Style sheet of the table
    int widthT, widthVH;
    double widthC;
    widthT = ui.pointsTableWidget->size().width();
    widthVH = 25.0;
    widthC = (widthT - widthVH) / (double) NB_TABLE_COLUMNS;

    for (int i = 0; i < NB_TABLE_COLUMNS; i++) {
        ui.pointsTableWidget->setColumnWidth(i, widthC);
    }
}

//--------------------manuallyModified-------------------------------
void MultiPickingWidget::manuallyModified(int i, int j) {
    if ((j != VALUE_TABLE_INDEX) && (j != X_TABLE_INDEX) && (j != Y_TABLE_INDEX) && (j != Z_TABLE_INDEX)) {
        return;
    }
    // If the user tries to change the voxel value, tell him it is not possible
    if (j == VALUE_TABLE_INDEX) {
        CAMITK_WARNING(tr("Impossible Operation: you cannot change the value of a voxel."))
        updateTable();
        return;
    }
    else if (ui.worldRadioButton->isChecked()) {
        CAMITK_WARNING(tr("Impossible Operation: you cannot change the real world coordinate values, they are just here for display.\nYou can manually change image indices and image coordinates if you want, though..."))
        updateTable();
        return;
    }
    else if (ui.indexRadioButton->isChecked()) {
        int* imageDims = img->getImageData()->GetDimensions();
        double* voxSize = img->getImageData()->GetSpacing();

        // Change the pixel index at this row
        // 3 possible case, either x, or y or z of one point have been manually changed
        // We thus pick this new point
        // For item number i, re-pick
        int iPick = ui.pointsTableWidget->item(i, X_TABLE_INDEX)->text().toInt();
        int jPick = ui.pointsTableWidget->item(i, Y_TABLE_INDEX)->text().toInt();
        int kPick = ui.pointsTableWidget->item(i, Z_TABLE_INDEX)->text().toInt();

        // Exceptionnally, we do not make this calculation by image picking and use image voxel size
        // to mimick a picking from the axial slice.
        double x = iPick * voxSize[0];
        double y = jPick * voxSize[1];
        double z = kPick * voxSize[2];

        // check that this point is within image dimensions
        if (((x < 0) || (x > imageDims[0]*voxSize[0])) ||
                ((y < 0) || (y > imageDims[1]*voxSize[1])) ||
                ((z < 0) || (z > imageDims[2]*voxSize[2]))) {
            CAMITK_WARNING(tr("Impossible Operation: the point (%1,%2,%3) does not belong to the image \"%4\".").arg(QString::number(x), QString::number(y), QString::number(z), img->getName()))
            updateTable();
            return;
        }

        // If right pixel, pick the image at this point
        // Cheat ! Say that your are its axial slice...
        img->getAxialSlices()->setSlice(z);
        img->pixelPicked(x, y, z);
        Application::refresh();
        pPM->modifyPixel(i);

        updateTable();

    }
    else {   // Image Coordinates
        int* imageDims = img->getImageData()->GetDimensions();
        double* voxSize = img->getImageData()->GetSpacing();

        // Change the pixel index at this row
        // 3 possible case, either x, or y or z of one point have been manually changed
        // We thus pick this new point
        // For item number i, re-pick
        double x = ui.pointsTableWidget->item(i, X_TABLE_INDEX)->text().toDouble();
        double y = ui.pointsTableWidget->item(i, Y_TABLE_INDEX)->text().toDouble();
        double z = ui.pointsTableWidget->item(i, Z_TABLE_INDEX)->text().toDouble();

        // check that this point is within image dimensions
        if (((x < 0) || (x > imageDims[0]*voxSize[0])) ||
                ((y < 0) || (y > imageDims[1]*voxSize[1])) ||
                ((z < 0) || (z > imageDims[2]*voxSize[2]))) {
            CAMITK_WARNING(tr("Impossible Operation: the point (%1,%2,%3) does not belong to the image \"%4\".").arg(QString::number(x), QString::number(y), QString::number(z), img->getName()))
            updateTable();
            return;
        }

        // If right pixel, pick the image at this point
        // Cheat ! Say that your are its axial slice...
        img->getAxialSlices()->setSlice(z);
        img->pixelPicked(x, y, z);
        Application::refresh();
        pPM->modifyPixel(i);

        updateTable();

    }
}
