/*  Multimodal Image Segmentation Tool (MIST)  */
/*  Eelke Visser  */

/*  Copyright (c) 2016 University of Oxford  */

/*  CCOPYRIGHT  */

#include "multishapewindow.h"
#include "ui_multishapewindow.h"
#include "setupfilenameswindow.h"
#include "orthoviews.h"
#include "custominteractorstyle.h"
#include "nokeysqvtkwidget.h"
#include "mainwindow.h"
#include "viewshape.h"
#include "viewdata.h"
#include "vtkInteractorStyleSwitch.h"
#include "vtkRenderer.h"
#include "vtkRendererCollection.h"
#include "vtkRenderWindow.h"
#include "vtkSmartPointer.h"
#include <QMessageBox>

#define BLOCK_SIGNALS(object, call) { bool oldstate = object->blockSignals(true); object->call; object->blockSignals(oldstate); }

MultiShapeWindow::MultiShapeWindow(QWidget *parent, const std::vector<std::shared_ptr<ViewData> > &volumes,
                                   boost::shared_ptr<const Transformation> transformation, const std::string &directory) :
    QDialog(parent),
    ui(new Ui::MultiShapeWindow),
    m_volumes(volumes)
{
    ui->setupUi(this);

    m_sagittalWidget = new NoKeysQVTKWidget(this);
    m_sagittalWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_sagittalWidget->setMinimumSize(m_minimumViewWidth, m_minimumViewHeight);
    ui->sagittalLayout->insertWidget(0, m_sagittalWidget);

    m_coronalWidget = new NoKeysQVTKWidget(this);
    m_coronalWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_coronalWidget->setMinimumSize(m_minimumViewWidth, m_minimumViewHeight);
    ui->coronalLayout->insertWidget(0, m_coronalWidget);

    m_transverseWidget = new NoKeysQVTKWidget(this);
    m_transverseWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_transverseWidget->setMinimumSize(m_minimumViewWidth, m_minimumViewHeight);
    ui->transverseLayout->insertWidget(0, m_transverseWidget);

    m_orthoViews = make_shared<OrthoViews>(m_sagittalWidget->GetRenderWindow(),
                                           m_coronalWidget->GetRenderWindow(),
                                           m_transverseWidget->GetRenderWindow());
    m_orthoViews->SetScale(2.0);

    auto originfunc = [this] (const ColumnVector& origin)
                {
                    SetOrigin(origin);
                };

    auto sagittalinteractorstyle = vtkSmartPointer<CustomInteractorStyle>::New();
    sagittalinteractorstyle->SetOrthoViews(m_orthoViews);
    sagittalinteractorstyle->SetOrthoMode(CustomInteractorStyle::OrthoMode::Sagittal);
    sagittalinteractorstyle->SetOriginFunc(originfunc);
    sagittalinteractorstyle->SetDefaultRenderer(
                m_orthoViews->GetSagittalView()->GetRenderers()->GetFirstRenderer());
    m_sagittalWidget->GetInteractor()->SetInteractorStyle(sagittalinteractorstyle);

    auto coronalinteractorstyle = vtkSmartPointer<CustomInteractorStyle>::New();
    coronalinteractorstyle->SetOrthoViews(m_orthoViews);
    coronalinteractorstyle->SetOrthoMode(CustomInteractorStyle::OrthoMode::Coronal);
    coronalinteractorstyle->SetOriginFunc(originfunc);
    coronalinteractorstyle->SetDefaultRenderer(
                m_orthoViews->GetCoronalView()->GetRenderers()->GetFirstRenderer());
    m_coronalWidget->GetInteractor()->SetInteractorStyle(coronalinteractorstyle);

    auto transverseinteractorstyle = vtkSmartPointer<CustomInteractorStyle>::New();
    transverseinteractorstyle->SetOrthoViews(m_orthoViews);
    transverseinteractorstyle->SetOrthoMode(CustomInteractorStyle::OrthoMode::Transverse);
    transverseinteractorstyle->SetOriginFunc(originfunc);
    transverseinteractorstyle->SetDefaultRenderer(
                m_orthoViews->GetTransverseView()->GetRenderers()->GetFirstRenderer());
    m_transverseWidget->GetInteractor()->SetInteractorStyle(transverseinteractorstyle);

    if (m_volumes.size())
        m_orthoViews->SetData(m_volumes[0]);

    SetupFilenamesWindow setupform(this);

    for (int i = 0; i < setupform.GetNumberOfShapes(); i++)
    {
        try
        {
            std::string filename, displacementsbasename;
            float red, green, blue;
            setupform.GetShape(i, filename, displacementsbasename, red, green, blue);

            std::string fullname = directory + "/" + filename;
            std::shared_ptr<ViewShape> shape = std::make_shared<ViewShape>(fullname, transformation, fullname);
            shape->SetColor(red, green, blue);
            shape->SetLineWidth(5.0);

            std::string displacementsfilename = directory + "/" + displacementsbasename + "_displacements.txt";
            std::vector<double> displacements = MainWindow::ReadDisplacements(displacementsfilename, shape->GetNumberOfVertices());

            for (int v = 0; v < shape->GetNumberOfVertices(); v++)
                shape->SetDisplacement(v, displacements[v]);

            m_orthoViews->AddShape(shape);
        }
        catch (Shape::ShapeException& e)
        {
            std::ostringstream message;
            message << "Loading of shape " << i + 1 << " failed. The exception text was:"
                       << std::endl << e.what();
            QMessageBox::warning(this, "Error", message.str().c_str());
            return;
        }
        catch (std::ios_base::failure &e)
        {
            std::ostringstream message;
            message << "Loading of displacements failed for shape " << i + 1 << ". The exception text was:"
                       << std::endl << e.what();
            QMessageBox::warning(this, "Error", message.str().c_str());
            return;
        }
    }

    SetupInterface();

    if (m_volumes.size())
        SelectVolume(0);

    m_orthoViews->ResetCameras();
}

MultiShapeWindow::~MultiShapeWindow()
{
    m_orthoViews.reset();

    delete ui;
}

void MultiShapeWindow::SetupInterface()
{
    ColumnVector bounds = m_orthoViews->GetBounds();
    ColumnVector origin(3);
    origin << (bounds(2) - bounds(1)) / 2.0
           << (bounds(4) - bounds(3)) / 2.0
           << (bounds(6) - bounds(5)) / 2.0;
    m_orthoViews->SetOrigin(origin);

    BLOCK_SIGNALS(ui->sagittalSlider, setMinimum(bounds(1) * m_sliderSubDivision))
    BLOCK_SIGNALS(ui->sagittalSlider, setMaximum(bounds(2) * m_sliderSubDivision))
    BLOCK_SIGNALS(ui->sagittalSlider, setSliderPosition(origin(1) * m_sliderSubDivision))
    BLOCK_SIGNALS(ui->coronalSlider, setMinimum(bounds(3) * m_sliderSubDivision))
    BLOCK_SIGNALS(ui->coronalSlider, setMaximum(bounds(4) * m_sliderSubDivision))
    BLOCK_SIGNALS(ui->coronalSlider, setSliderPosition(origin(2) * m_sliderSubDivision))
    BLOCK_SIGNALS(ui->transverseSlider, setMinimum(bounds(5) * m_sliderSubDivision))
    BLOCK_SIGNALS(ui->transverseSlider, setMaximum(bounds(6) * m_sliderSubDivision))
    BLOCK_SIGNALS(ui->transverseSlider, setSliderPosition(origin(3) * m_sliderSubDivision))

    BLOCK_SIGNALS(ui->modalityComboBox, clear());

    for (auto it = m_volumes.begin(); it != m_volumes.end(); ++it)
        BLOCK_SIGNALS(ui->modalityComboBox, addItem(QString((*it)->GetDisplayName().c_str())));
}

void MultiShapeWindow::SetOrigin(const ColumnVector& origin)
{
    BLOCK_SIGNALS(ui->sagittalSlider, setValue(static_cast<int>(origin(1) * m_sliderSubDivision)))
    BLOCK_SIGNALS(ui->coronalSlider, setValue(static_cast<int>(origin(2) * m_sliderSubDivision)))
    BLOCK_SIGNALS(ui->transverseSlider,setValue(static_cast<int>(origin(3) * m_sliderSubDivision)))

    if (m_orthoViews)
        m_orthoViews->SetOrigin(origin);
}

void MultiShapeWindow::SelectVolume(int index)
{
    m_orthoViews->SetData(m_volumes[index]);
    BLOCK_SIGNALS(ui->modalityComboBox, setCurrentIndex(index))
}

void MultiShapeWindow::on_sagittalSlider_valueChanged(int position)
{
    ColumnVector origin = m_orthoViews->GetOrigin();
    origin(1) = static_cast<double>(position) /  m_sliderSubDivision;
    SetOrigin(origin);
}

void MultiShapeWindow::on_coronalSlider_valueChanged(int position)
{
    ColumnVector origin = m_orthoViews->GetOrigin();
    origin(2) = static_cast<double>(position) /  m_sliderSubDivision;
    SetOrigin(origin);
}

void MultiShapeWindow::on_transverseSlider_valueChanged(int position)
{
    ColumnVector origin = m_orthoViews->GetOrigin();
    origin(3) = static_cast<double>(position) /  m_sliderSubDivision;
    SetOrigin(origin);
}

void MultiShapeWindow::on_modalityComboBox_currentIndexChanged(int index)
{
    if (index >= 0)
        SelectVolume(index);
}

void MultiShapeWindow::on_hideOverlaysComboBox_stateChanged(int value)
{
    m_orthoViews->SetHideOverlays(value ? OrthoViews::HideAllOverlays : OrthoViews::HideNone);
}
