/*****************************************************************************
 * $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$
 ****************************************************************************/

// -- Core stuff
#include "MedicalImageViewer.h"
#include <Action.h>
#include <Application.h>
#include <InteractiveSliceViewer.h>
#include <InteractiveGeometryViewer.h>
#include <Log.h>

// -- Qt stuff
#include <QColorDialog>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QToolBar>
#include <QActionGroup>

// -- Std stuff
#include <utility> // as_const

using namespace camitk;


// -------------------- Constructor --------------------
MedicalImageViewer::MedicalImageViewer(QString name) : Viewer(name) {
    setDescription("The Medical Image Viewer embeds all classical medical image views (axial, sagittal, coronal, 3D and arbitrary orientation)");
    // init view members
    frame = nullptr;
    frameLayout = nullptr;
    viewerMenu = nullptr;

    // view only 3D scene by default
    visibleLayout = VIEWER_3D;

    displayedTopLevelComponents = 0;

    autoUpdateToolbarVisibility = true;

}


// -------------------- Destructor --------------------
MedicalImageViewer::~MedicalImageViewer() {
    if (viewerMenu != nullptr) {
        viewerMenu->clear();
        delete viewerMenu;
    }

    // do not delete the frame as it will automatically be deleted
    // when the embedder widget will be deleted
}

// -------------------- refresh --------------------
void MedicalImageViewer::refresh(Viewer* whoIsAsking) {
    // just tell everyone to update!
    for (Viewer* v : viewers) {
        v->refresh(this);
    }

    // view everything?
    if (displayedTopLevelComponents != (unsigned) Application::getTopLevelComponents().size()) {
        // Check if there is ImageComponent, if yes show all viewers
        int i = 0;

        while (i < Application::getTopLevelComponents().size() && !Application::getTopLevelComponents().at(i)->isInstanceOf("ImageComponent")) {
            i++;
        }

        if (i < Application::getTopLevelComponents().size()) {
            visibleLayout = VIEWER_ALL;
        }
        else {
            visibleLayout = VIEWER_3D;
        }

        updateLayout();

        // update the counter
        displayedTopLevelComponents = Application::getTopLevelComponents().size();
    }
}

// -------------------- getWidget --------------------
QWidget* MedicalImageViewer::getWidget() {
    if (frame == nullptr) {
        frame = new QFrame();
        frame->setObjectName("MedicalImageViewer Frame");
        frame->setFrameShape(QFrame::StyledPanel);
        frame->setFrameShadow(QFrame::Plain);

        // gulp the enum
        viewerVisibility.append(VIEWER_3D);
        viewerVisibility.append(VIEWER_AXIAL);
        viewerVisibility.append(VIEWER_CORONAL);
        viewerVisibility.append(VIEWER_SAGITTAL);
        viewerVisibility.append(VIEWER_ARBITRARY); // ARBITRARY has to be before ALL
        viewerVisibility.append(VIEWER_ALL); // ALL has to be the end of visible viewers

        // list of viewer name
        InteractiveGeometryViewer* default3DViewer = dynamic_cast<InteractiveGeometryViewer*>(Application::getViewer("3D Viewer"));
        if (default3DViewer != nullptr) {
            viewers.insert(VIEWER_3D, default3DViewer);
        }
        else {
            CAMITK_ERROR(tr("Cannot find \"3D Viewer\". This viewer is mandatory for building the medical image viewer."))
        }

        InteractiveSliceViewer* sliceViewer = dynamic_cast<InteractiveSliceViewer*>(Application::getViewer("Axial Viewer"));
        if (sliceViewer != nullptr) {
            viewers.insert(VIEWER_AXIAL, sliceViewer);
        }
        else {
            CAMITK_ERROR(tr("Cannot find \"Axial Viewer\". This viewer is mandatory for building the medical image viewer."))
        }

        sliceViewer = dynamic_cast<InteractiveSliceViewer*>(Application::getViewer("Coronal Viewer"));
        if (sliceViewer != nullptr) {
            viewers.insert(VIEWER_CORONAL, sliceViewer);
        }
        else {
            CAMITK_ERROR(tr("Cannot find \"Coronal Viewer\". This viewer is mandatory for building the medical image viewer."))
        }

        sliceViewer = dynamic_cast<InteractiveSliceViewer*>(Application::getViewer("Sagittal Viewer"));
        if (sliceViewer != nullptr) {
            viewers.insert(VIEWER_SAGITTAL, sliceViewer);
        }
        else {
            CAMITK_ERROR(tr("Cannot find \"Sagittal Viewer\". This viewer is mandatory for building the medical image viewer."))
        }

        sliceViewer = dynamic_cast<InteractiveSliceViewer*>(Application::getViewer("Arbitrary Viewer"));
        if (sliceViewer != nullptr) {
            viewers.insert(VIEWER_ARBITRARY, sliceViewer);
        }
        else {
            CAMITK_ERROR(tr("Cannot find \"Arbitrary Viewer\". This viewer is mandatory for building the medical image viewer."))
        }

        //-- init layout
        frameLayout = new QGridLayout(frame);
        frameLayout->setSpacing(0);
        frameLayout->setContentsMargins(0, 0, 0, 0);

        /// the layout for the arbitrary/axial at the top left position of frameLayout
        northWestLayout = new QVBoxLayout();
        frameLayout->addLayout(northWestLayout, 0, 0); // north-west
        southWestLayout = new QVBoxLayout();
        frameLayout->addLayout(southWestLayout, 1, 0); // south-west
        southEastLayout = new QVBoxLayout();
        frameLayout->addLayout(southEastLayout, 1, 1); // south-east
        northEastLayout = new QVBoxLayout();
        frameLayout->addLayout(northEastLayout, 0, 1); // north-east

        //-- connect
        for (LayoutVisibility v : viewerVisibility) {
            if (viewers.value(v)) { // prevent ALL
                connect(viewers.value(v), SIGNAL(selectionChanged()), this, SLOT(synchronizeSelection()));
            }
        }

    }

    // always re-embed the viewers (in case another viewer embedded them somewhere else in the meanwhile)
    Application::getViewer("Axial Viewer")->setEmbedder(northWestLayout);
    Application::getViewer("Coronal Viewer")->setEmbedder(southWestLayout);
    Application::getViewer("Sagittal Viewer")->setEmbedder(southEastLayout);
    Application::getViewer("3D Viewer")->setEmbedder(northEastLayout);
    Application::getViewer("Arbitrary Viewer")->setEmbedder(northEastLayout);

    //-- show the correct viewer
    updateLayout();

    return frame;
}

// ---------------------- getPropertyObject ----------------------------
PropertyObject* MedicalImageViewer::getPropertyObject() {
    return Application::getViewer("3D Viewer")->getPropertyObject();
}

// -------------------- getMenu --------------------
QMenu* MedicalImageViewer::getMenu() {
    if (viewerMenu == nullptr) {
        //-- create the main menu
        viewerMenu = new QMenu(objectName());
        viewerMenu->setTearOffEnabled(true);

        //-- build up menu
        QMenu* layoutMenu = new QMenu("Visible Viewers");
        viewerMenu->addMenu(layoutMenu);
        layoutMenu->addAction(Application::getAction("Show All Viewers")->getQAction());
        layoutMenu->addSeparator();

        //-- add the visibility action
        auto* viewerGroup = new QActionGroup(this);
        Application::getAction("Show All Viewers")->getQAction()->setActionGroup(viewerGroup);
        Application::getAction("Show 3D Viewer")->getQAction()->setActionGroup(viewerGroup);
        layoutMenu->addAction(Application::getAction("Show 3D Viewer")->getQAction());
        Application::getAction("Show Axial Viewer")->getQAction()->setActionGroup(viewerGroup);
        layoutMenu->addAction(Application::getAction("Show Axial Viewer")->getQAction());
        Application::getAction("Show Sagittal Viewer")->getQAction()->setActionGroup(viewerGroup);
        layoutMenu->addAction(Application::getAction("Show Sagittal Viewer")->getQAction());
        Application::getAction("Show Coronal Viewer")->getQAction()->setActionGroup(viewerGroup);
        layoutMenu->addAction(Application::getAction("Show Coronal Viewer")->getQAction());
        Application::getAction("Show Arbitrary Viewer")->getQAction()->setActionGroup(viewerGroup);
        layoutMenu->addAction(Application::getAction("Show Arbitrary Viewer")->getQAction());

        for (unsigned int i = 0; viewerVisibility[i] != VIEWER_ALL; i++) {
            // and viewer menu as submenu
            viewerMenu->addMenu(viewers.value(viewerVisibility[i])->getMenu());
        }

    }

    return viewerMenu;
}

// -------------------- getToolBar --------------------
QToolBar* MedicalImageViewer::getToolBar() {
    return nullptr;
}


// -------------------- setVisibleViewer --------------------
void MedicalImageViewer::setVisibleViewer(LayoutVisibility visibleViewer) {
    visibleLayout = visibleViewer;
    updateLayout();
}

// -------------------- getVisibleViewer --------------------
MedicalImageViewer::LayoutVisibility MedicalImageViewer::getVisibleViewer() const {
    return visibleLayout;
}

// -------------------- updateLayout --------------------
void MedicalImageViewer::updateLayout() {

    if (viewers.empty()) {
        return;
    }

    if (visibleLayout == VIEWER_ALL) {
        viewers.value(VIEWER_3D)->setVisible(true);
        viewers.value(VIEWER_AXIAL)->setVisible(true);
        viewers.value(VIEWER_CORONAL)->setVisible(true);
        viewers.value(VIEWER_SAGITTAL)->setVisible(true);
        viewers.value(VIEWER_ARBITRARY)->setVisible(false);
    }
    else {
        for (unsigned int i = 0; viewerVisibility[i] != VIEWER_ALL; i++) {
            viewers.value(viewerVisibility[i])->setVisible(visibleLayout == viewerVisibility[i]);
        }
    }

    if (autoUpdateToolbarVisibility) {
        if (visibleLayout == VIEWER_3D || visibleLayout == VIEWER_ALL) {
            setToolBarVisibility(true);
        }
        else {
            setToolBarVisibility(false);
        }
    }

}

// -------------------- synchronizeSelection --------------------
void MedicalImageViewer::synchronizeSelection() {
    auto* whoIsAsking = qobject_cast<Viewer*>(sender());

    for (auto v : std::as_const(viewers)) {
        if (v != whoIsAsking) {
            v->refresh(whoIsAsking);
        }
    }

    // lets inform the extern viewers that there was a modification
    emit selectionChanged();
}

// -------------------- setToolBarVisibility --------------------
void MedicalImageViewer::setToolBarVisibility(bool toolbarVisibility) {
    autoUpdateToolbarVisibility = toolbarVisibility;
    Viewer::setToolBarVisibility(toolbarVisibility);
}