/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2016 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
 *
 * 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 "MainWindow.h"
#include "Core.h"
#include "Application.h"
#include "ui_Console.h"
#include "Viewer.h"
#include "Action.h"
#include "Component.h"
#include "Log.h"

// -- QT stuff
#include <QAction>
#include <QTextEdit>
#include <QRegExp>
#include <QFileDialog>
#include <QPixmap>
#include <QWhatsThis>
#include <QMenu>
#include <QToolBar>
#include <QProgressBar>
#include <QMenuBar>
#include <QStatusBar>
#include <QMessageBox>
#include <QDockWidget>
#include <QSettings>
#include <QUrl>
#include <QCloseEvent>
#include <QMimeData>


namespace camitk {
// ------------- constructor -----------------
MainWindow::MainWindow(QString title) {
    // set window title
    mainTitle = title;
    setWindowTitle ( title );

    setWindowIcon(QIcon(":/camiTKIcon"));

    // prepare GUI
    centralViewer = NULL;
    showStatusBar(false);
    // add the permanent progress bar
    myProgressBar = new QProgressBar();
    myProgressBar->setMaximum ( 100 );
    statusBar()->addPermanentWidget ( myProgressBar, 0);
    // set the ready message
    statusBar()->showMessage ( tr ( "Ready." ), 10000 );

    // create the console window
    consoleWindow = new QDialog;
    Ui::ui_Console ui;
    ui.setupUi ( consoleWindow );
    consoleWindowTextEdit = ui.consoleTextEdit;

    // accept drag and drop events
    setAcceptDrops(true);
}

// ------------- destructor -----------------
MainWindow::~MainWindow() {
    // delete all the viewers
    while ( !viewers.isEmpty() ) {
        delete viewers.takeFirst();
    }

    delete consoleWindow;
    consoleWindow = NULL;
}

// ------------- setWindowSubtitle -----------------
void MainWindow::setWindowSubtitle(QString subtitle) {
    setWindowTitle( mainTitle + " - [ " + subtitle + " ]" );
}

// ------------- addViewer -----------------
bool MainWindow::addViewer ( Viewer* theViewer ) {
    if ( !viewers.contains ( theViewer ) ) {
        viewers.append ( theViewer );
        // connect
        connect ( theViewer, SIGNAL ( selectionChanged() ), this, SLOT ( refresh() ) );

        // add the toolbar
        if ( theViewer->getToolBar() ) {
            QToolBar *viewerToolBar = theViewer->getToolBar();
            addToolBar ( viewerToolBar );
        }

        return true;
    }

    return false;
}

// ------------- removeViewer -----------------
bool MainWindow::removeViewer(Viewer *viewer) {
    if (!viewers.contains(viewer))
        return false;

    // tell all component not to be visible anymore in this viewer
    foreach(Component* comp, Application::getAllComponents()) {
        comp->setVisibility(viewer, false);
    }

    // remove it properly
    disconnect(viewer, SIGNAL (selectionChanged()), this, SLOT(refresh()));
    viewers.removeAll(viewer);
    delete viewer;

    return true;
}

// ------------- showViewer -----------------
void MainWindow::showViewer(Viewer* theViewer, bool visible) {
    if (viewers.contains(theViewer)) {
        dockWidgetMap[theViewer]->setVisible(visible);
    }
}

// -------------------- refreshViewers --------------------
void MainWindow::refreshViewers() {
    foreach ( Viewer *v, viewers ) {
        v->refresh();
    }
}

// ------------- addDockViewer -----------------
void MainWindow::addDockViewer ( Qt::DockWidgetArea dockingArea, Viewer* theViewer ) {
    if ( addViewer ( theViewer ) ) {
        // create the dock widget and insert it
        QDockWidget * viewerDock = new QDockWidget ( theViewer->objectName(), this );
        viewerDock->setObjectName(theViewer->objectName());
        QWidget *viewerWidget = theViewer->getWidget ( viewerDock );

        if ( viewerWidget ) {
            viewerDock->setWidget ( viewerWidget );
            addDockWidget ( dockingArea, viewerDock );
            // update map
            dockWidgetMap.insert ( theViewer, viewerDock );
        } else {
            delete viewerDock;
        }
    } else {
        dockWidgetMap.value ( theViewer )->show();
    }
}


// ------------- setCentralViewer -----------------
void MainWindow::setCentralViewer ( Viewer* theViewer ) {
    if ( theViewer->getWidget ( this ) && addViewer ( theViewer ) ) {
        // remove actual central viewer properly
        if (centralViewer && (theViewer != centralViewer))
            removeViewer(centralViewer);

        // set the central widget
        setCentralWidget ( theViewer->getWidget ( this ) );
        centralViewer = theViewer;
    }
}

// ------------- getCentralViewer -----------------
const Viewer & MainWindow::getCentralViewer() const {
    return *centralViewer;
}

// ------------- refresh -----------------
void MainWindow::refresh() {
    Viewer *whoIsAsking = qobject_cast<Viewer *> ( sender() );
    foreach ( Viewer * v, viewers ) {
        if ( v != whoIsAsking ) {
            v->refresh();
        }
    }
}

// ------------- showStatusBar -----------------
void MainWindow::showStatusBar ( bool b ) {
    statusBar()->setVisible ( b );
}

//-------------------------- getProgressBar -------------------------------
QProgressBar * MainWindow::getProgressBar() {
    return myProgressBar;
}


// ------------- setApplicationConsole -----------------
void MainWindow::redirectToConsole ( bool visible ) {
    if ( visible ) {
        // plug the console to the textedit
        cout.init ( &std::cout, consoleWindowTextEdit );
        cerr.init ( &std::cerr, consoleWindowTextEdit );
        std::cerr << Core::version << std::endl;
    } else {
        cout.free();
        cerr.free();
        // unplug it
        std::cerr << Core::version << std::endl;
        // @TODO here TODO @@@ change the console button, to "redirect to app console" and do the plugin "live"
    }
}

// ------------- showConsole -----------------
void MainWindow::showConsole(bool show) {
    consoleWindow->setVisible(show);
}

// ------------- show -----------------
void MainWindow::show() {
    refresh();
    QMainWindow::show();
}

// ------------- aboutToShow -----------------
void MainWindow::aboutToShow() {
    initSettings();
}

// ------------- initSettings -----------------
void MainWindow::initSettings() {
    QSettings & settings = Application::getSettings();
    settings.beginGroup(Application::getName() + ".MainWindow");
    restoreState(settings.value("windowState").toByteArray());
    QRect geom = settings.value("geometry", QRect(8, 30, 1024, 768)).toRect();
    // ensure ok size for non-compliant window managers
    if (geom.width()<=0)
        geom.setWidth(1024);
    if (geom.height()<=0)
        geom.setHeight(768);
    setGeometry(geom);
    settings.endGroup();
}

// ------------- closeEvent -----------------
void MainWindow::closeEvent(QCloseEvent* event) {
    statusBar()->showMessage ( tr ( "Exiting application..." ) );

    if (Application::getAction("Close All")->apply()==Action::SUCCESS) {

        QSettings & settings = Application::getSettings();
        settings.beginGroup(Application::getName() + ".MainWindow");
        settings.setValue("geometry", geometry());
        settings.setValue("windowState", saveState());
        settings.endGroup();

        event->accept();
    } else
        event->ignore();

}

// ------------- dragEnterEvent -----------------
void MainWindow::dragEnterEvent(QDragEnterEvent* event) {
    // from chapter9 of "C++ GUI Programming with Qt4, 2nd Edition", http://www.informit.com/articles/article.aspx?p=1405546
    // MIME type text/uri-list is used to store a list of uniform resource
    // identifiers (URIs), which can be file names, URLs (such as HTTP or FTP paths), or other global resource identifiers.
    // Standard MIME types are defined by the Internet Assigned Numbers Authority (IANA).
    // They consist of a type and a subtype separated by a slash.
    // The clipboard and the drag and drop system use MIME types to identify different types of data.
    // The official list of MIME types is available at http://www.iana.org/assignments/media-types/.
    if (event->mimeData()->hasUrls())
        event->acceptProposedAction();
}


// ------------- dragMoveEvent -----------------
void MainWindow::dragMoveEvent(QDragMoveEvent* event) {
    if (event->mimeData()->hasUrls())
        event->acceptProposedAction();
}


// ------------- dragLeaveEvent -----------------
void MainWindow::dragLeaveEvent(QDragLeaveEvent* event) {
    event->accept();
}

// ------------- dropEvent -----------------
void MainWindow::dropEvent(QDropEvent* event) {
    // get all URL of the file
    QList<QUrl> urls = event->mimeData()->urls();

    if (urls.isEmpty())
        return;

    // open all given url (or try anyway)
    QList<QUrl>::const_iterator fileIterator = urls.begin();

    while (fileIterator != urls.constEnd() && Application::open(fileIterator->toLocalFile()) !=NULL)
        ++fileIterator;

    event->acceptProposedAction();
}

}

