/**
 * UGENE - Integrated Bioinformatics Tools.
 * Copyright (C) 2008-2024 UniPro <ugene@unipro.ru>
 * http://ugene.net
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

#include "BioStruct3DViewPlugin.h"

#include <QMenu>
#include <QMessageBox>

#include <U2Core/BioStruct3DObject.h>
#include <U2Core/DNASequenceObject.h>
#include <U2Core/DocumentModel.h>
#include <U2Core/DocumentSelection.h>
#include <U2Core/GObject.h>
#include <U2Core/GObjectUtils.h>
#include <U2Core/LoadRemoteDocumentTask.h>
#include <U2Core/U2SafePoints.h>

#include <U2Gui/GUIUtils.h>

#include <U2View/ADVConstants.h>
#include <U2View/ADVSequenceObjectContext.h>
#include <U2View/ADVSingleSequenceWidget.h>
#include <U2View/AnnotatedDNAView.h>

#include "BioStruct3DGLWidget.h"
#include "BioStruct3DSplitter.h"

namespace U2 {

/*!
 * \mainpage BioStruct3D Viewer Plugin Documentation
 *
 * \section viewer Introduction
 *
 * BioStruct3D Viewer is a macromolecular viewing / editing tool.
 * It is activated whenever UGENE loads document that contains BioStruct3DObject.
 *
 *
 * \subsection main Main Classes
 *
 * plugin classes:
 * - BioStruct3DGLWidget : Widget for rendering 3d representations of macromolecular structure.
 * - BioStruct3DSplitter : Multiple glWidgets layout and manipulation
 * - GLFrame : Class for manipulating the 3d viewpoint
 *
 * plugin interfaces:
 * - BioStruct3DGLRenderer : General interface for structure 3d graphical styles
 * - BioStruct3DColorScheme : Interface for coloring atoms, bonds, etc.
 *
 *
 */

extern "C" Q_DECL_EXPORT Plugin* U2_PLUGIN_INIT_FUNC() {
    return AppContext::getMainWindow() != nullptr ? new BioStruct3DViewPlugin() : nullptr;
}

extern "C" Q_DECL_EXPORT bool U2_PLUGIN_VERIFY_FUNC() {
    BioStruct3DGLWidget::tryGL();
    return true;
}

extern "C" Q_DECL_EXPORT QString* U2_PLUGIN_FAIL_MASSAGE_FUNC() {
    return new QString(BioStruct3DViewPlugin::tr("Unfortunately, your system does not have OpenGL Support.\n"
                                                 "The 3D Structure Viewer is not available.\n"
                                                 "You may try to upgrade your system by updating the video card driver."));
}

BioStruct3DViewPlugin::BioStruct3DViewPlugin()
    : Plugin(tr("3D Structure Viewer"), tr("Visualizes 3D structures of biological molecules.")) {
    viewContext = new BioStruct3DViewContext(this);
    viewContext->init();
}

BioStruct3DViewPlugin::~BioStruct3DViewPlugin() {
}
BioStruct3DViewContext::BioStruct3DViewContext(QObject* p)
    : GObjectViewWindowContext(p, ANNOTATED_DNA_VIEW_FACTORY_ID) {
}

void BioStruct3DViewContext::initViewContext(GObjectViewController* v) {
    auto av = qobject_cast<AnnotatedDNAView*>(v);
    U2SequenceObject* dna = av->getActiveSequenceContext()->getSequenceObject();

    QList<GObject*> allBiostructs = GObjectUtils::findAllObjects(UOF_LoadedOnly, GObjectTypes::BIOSTRUCTURE_3D);
    QList<GObject*> targetBiostructs = GObjectUtils::findObjectsRelatedToObjectByRole(dna,
                                                                                      GObjectTypes::BIOSTRUCTURE_3D,
                                                                                      ObjectRole_Sequence,
                                                                                      allBiostructs,
                                                                                      UOF_LoadedOnly);
    CHECK(!targetBiostructs.isEmpty(), );

    QList<ADVSequenceWidget*> seqWidgets = av->getSequenceWidgets();
    foreach (ADVSequenceWidget* w, seqWidgets) {
        auto aw = qobject_cast<ADVSingleSequenceWidget*>(w);
        if (aw != nullptr) {
            aw->setDetViewCollapsed(true);
            aw->setOverviewCollapsed(true);
        }
    }
    foreach (GObject* obj, targetBiostructs) {
        v->addObject(obj);
    }
}

bool BioStruct3DViewContext::canHandle(GObjectViewController* v, GObject* o) {
    Q_UNUSED(v);
    bool res = qobject_cast<BioStruct3DObject*>(o) != nullptr;
    return res;
}

void BioStruct3DViewContext::onObjectAdded(GObjectViewController* view, GObject* obj) {
    // todo: add sequence & all objects associated with sequence to the view?

    auto obj3d = qobject_cast<BioStruct3DObject*>(obj);
    if (obj3d == nullptr || view == nullptr) {
        return;
    }

    auto av = qobject_cast<AnnotatedDNAView*>(view);
    BioStruct3DSplitter* splitter = nullptr;
    if (splitterMap.contains(view)) {
        splitter = splitterMap.value(view);
    } else {
        splitter = new BioStruct3DSplitter(getClose3DViewAction(view), av);
    }

    av->insertWidgetIntoSplitter(splitter);
    splitter->addObject(obj3d);
    splitterMap.insert(view, splitter);
}

void BioStruct3DViewContext::onObjectRemoved(GObjectViewController* v, GObject* obj) {
    auto obj3d = qobject_cast<BioStruct3DObject*>(obj);
    if (obj3d == nullptr) {
        return;
    }
    BioStruct3DSplitter* splitter = splitterMap.value(v);
    bool close = splitter->removeObject(obj3d);
    if (close) {
        splitter->close();
        // unregister3DView(v,splitter);
    }
}

void BioStruct3DViewContext::unregister3DView(GObjectViewController* view, BioStruct3DSplitter* splitter) {
    assert(splitter->getChildWidgets().isEmpty());
    splitter->close();
    auto av = qobject_cast<AnnotatedDNAView*>(view);
    av->unregisterSplitWidget(splitter);
    splitterMap.remove(view);
    splitter->deleteLater();
}

QAction* BioStruct3DViewContext::getClose3DViewAction(GObjectViewController* view) {
    QList<QObject*> resources = viewResources.value(view);
    foreach (QObject* r, resources) {
        auto a = qobject_cast<GObjectViewAction*>(r);
        if (a != nullptr) {
            return a;
        }
    }
    QAction* a = new GObjectViewAction(this, view, tr("Close 3D Structure Viewer"));
    connect(a, SIGNAL(triggered()), SLOT(sl_close3DView()));
    resources.append(a);
    return a;
}

void BioStruct3DViewContext::sl_close3DView() {
    auto action = qobject_cast<GObjectViewAction*>(sender());
    GObjectViewController* ov = action->getObjectView();
    QList<GObject*> objects = ov->getObjects();
    foreach (GObject* obj, objects) {
        if (obj->getGObjectType() == GObjectTypes::BIOSTRUCTURE_3D) {
            ov->removeObject(obj);
        }
    }
}

void BioStruct3DViewContext::sl_windowClosing(MWMDIWindow* w) {
    auto gvw = qobject_cast<GObjectViewWindow*>(w);
    if (gvw) {
        GObjectViewController* view = gvw->getObjectView();
        // safe to remove: splitter will be deleted with ADV
        splitterMap.remove(view);
    }

    GObjectViewWindowContext::sl_windowClosing(w);
}

}  // namespace U2
