// Qt includes
#include "tabbar.h"
#include "tabwidget.h"
#include <QMoveEvent>
#include <QApplication>
#include <QDesktopWidget>

//////////////////////////////////////////////////////////////
// Default Constructor
//////////////////////////////////////////////////////////////
MHTabWidget::MHTabWidget(QWidget* parent, bool allowDetach, bool allowDelete) : QTabWidget(parent)
{
    mHistoryPopup = new MultiItemsSelectWindow(this, parentWidget(), true);

    mTabBar = new MHTabBar(this, allowDetach, allowDelete);
    connect(mTabBar, SIGNAL(OnDetachTab(int, const QPoint &)), this, SLOT(DetachTab(int, const QPoint &)));
    connect(mTabBar, SIGNAL(OnDeleteTab(int)), this, SLOT(DeleteTab(int)));

    connect(mTabBar, SIGNAL(tabMoved(int, int)), this, SLOT(tabMoved(int, int)));
    connect(mTabBar, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int)));

    setTabBar(mTabBar);
    setMovable(true);

    mWindows.clear();
}

//////////////////////////////////////////////////////////////
// Default Destructor
//////////////////////////////////////////////////////////////
MHTabWidget::~MHTabWidget()
{
    disconnect(mTabBar, SIGNAL(OnDetachTab(int, const QPoint &)), this, SLOT(DetachTab(int, const QPoint &)));
    disconnect(mTabBar, SIGNAL(OnDeleteTab(int)), this, SLOT(DeleteTab(int)));
    disconnect(mTabBar, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int)));
    delete mTabBar;
}

QWidget* MHTabWidget::widget(int index) const
{
    int baseCount = QTabWidget::count();

    // Check if it's just a normal tab
    if(index < baseCount)
        return QTabWidget::widget(index);

    // Otherwise it's going to be a window
    return mWindows.at(index - baseCount);
}

int MHTabWidget::count() const
{
    return QTabWidget::count() + mWindows.size();
}

QList<QWidget*> MHTabWidget::windows()
{
    return mWindows;
}

// Add a tab
int MHTabWidget::addTabEx(QWidget* widget, const QIcon & icon, const QString & label, const QString & nativeName)
{
    mNativeNames.append(nativeName);
    mHistory.push_back((MIDPKey)widget);
    widget->setAccessibleName(label);
    return this->addTab(widget, icon, label);
}

// Convert an external window to a widget tab
void MHTabWidget::AttachTab(QWidget* parent)
{
    // Retrieve widget
    MHDetachedWindow* detachedWidget = reinterpret_cast<MHDetachedWindow*>(parent);
    QWidget* tearOffWidget = detachedWidget->centralWidget();

    // Reattach the tab
    auto newIndex = addTabEx(tearOffWidget, detachedWidget->windowIcon(), detachedWidget->windowTitle(), detachedWidget->mNativeName);

    // Remove it from the windows list
    for(int i = 0; i < mWindows.size(); i++)
    {
        if(mWindows.at(i) == tearOffWidget)
        {
            mWindows.removeAt(i);
        }
    }

    // Cleanup Window
    disconnect(detachedWidget, SIGNAL(OnClose(QWidget*)), this, SLOT(AttachTab(QWidget*)));
    disconnect(detachedWidget, SIGNAL(OnFocused(QWidget*)), this, SLOT(OnDetachFocused(QWidget*)));
    detachedWidget->hide();
    detachedWidget->close();

    // Move the tab back to the previous index
    if(detachedWidget->mPreviousIndex >= 0)
        mTabBar->moveTab(newIndex, detachedWidget->mPreviousIndex);
}

// Convert a tab to an external window
void MHTabWidget::DetachTab(int index, const QPoint & dropPoint)
{
    Q_UNUSED(dropPoint);
    // Create the window
    MHDetachedWindow* detachedWidget = new MHDetachedWindow(parentWidget());
    detachedWidget->setWindowModality(Qt::NonModal);

    // Find Widget and connect
    connect(detachedWidget, SIGNAL(OnClose(QWidget*)), this, SLOT(AttachTab(QWidget*)));
    connect(detachedWidget, SIGNAL(OnFocused(QWidget*)), this, SLOT(OnDetachFocused(QWidget*)));

    detachedWidget->setWindowTitle(tabText(index));
    detachedWidget->setWindowIcon(tabIcon(index));
    detachedWidget->mNativeName = mNativeNames[index];
    detachedWidget->mPreviousIndex = index;
    mNativeNames.removeAt(index);

    // Remove from tab bar
    QWidget* tearOffWidget = widget(index);
    tearOffWidget->setParent(detachedWidget);

    // Add it to the windows list
    mWindows.append(tearOffWidget);

    // Make first active
    if(count() > 0)
        setCurrentIndex(0);

    // Create and show
    detachedWidget->setCentralWidget(tearOffWidget);

    // Needs to be done explicitly
    tearOffWidget->showNormal();
    QRect screenGeometry = QApplication::desktop()->screenGeometry();
    int w = 640;
    int h = 480;
    int x = (screenGeometry.width() - w) / 2;
    int y = (screenGeometry.height() - h) / 2;
    detachedWidget->showNormal();
    detachedWidget->setGeometry(x, y, w, h);
    detachedWidget->showNormal();
}

// Remove a tab, while still keeping the widget intact
void MHTabWidget::DeleteTab(int index)
{
    QWidget* w = widget(index);
    mHistory.removeAll((MIDPKey)w);
    removeTab(index);
    mNativeNames.removeAt(index);
}

void MHTabWidget::tabMoved(int from, int to)
{
    std::swap(mNativeNames[from], mNativeNames[to]);
    emit tabMovedTabWidget(from, to);
}

void MHTabWidget::OnDetachFocused(QWidget* parent)
{
    MHDetachedWindow* detachedWidget = reinterpret_cast<MHDetachedWindow*>(parent);
    QWidget* tearOffWidget = detachedWidget->centralWidget();
    setLatestFocused(tearOffWidget);
}

void MHTabWidget::currentChanged(int index)
{
    setLatestFocused(widget(index));
}

void MHTabWidget::setCurrentIndex(int index)
{
    // Check if it's just a normal tab
    if(index < QTabWidget::count())
    {
        QTabWidget::setCurrentIndex(index);
    }
    else
    {
        // Otherwise it's going to be a window (just bring it up)
        MHDetachedWindow* window = dynamic_cast<MHDetachedWindow*>(widget(index)->parent());
        window->activateWindow();
        window->showNormal();
        window->setFocus();
    }

    setLatestFocused(widget(index));
}

MHTabBar* MHTabWidget::tabBar() const
{
    return mTabBar;
}

const QList<MIDPKey> & MHTabWidget::MIDP_getItems() const
{
    return mHistory;
}

QString MHTabWidget::MIDP_getItemName(MIDPKey index)
{
    return reinterpret_cast<QWidget*>(index)->windowTitle();
}

void MHTabWidget::MIDP_selected(MIDPKey index)
{
    setLatestFocused((QWidget*)index);

    // check in tabbar
    for(auto i = 0; i < QTabWidget::count(); ++i)
    {
        if(reinterpret_cast<QWidget*>(index) == QTabWidget::widget(i))
        {
            QTabWidget::setCurrentIndex(i);
            parentWidget()->activateWindow();
            parentWidget()->setFocus();
            return;
        }
    }

    // should be a detached window
    MHDetachedWindow* window = dynamic_cast<MHDetachedWindow*>(reinterpret_cast<QWidget*>(index)->parent());
    window->activateWindow();
    window->showNormal();
    window->setFocus();
}

QIcon MHTabWidget::MIDP_getIcon(MIDPKey index)
{
    // check in tabbar
    for(auto i = 0; i < QTabWidget::count(); ++i)
    {
        if(reinterpret_cast<QWidget*>(index) == QTabWidget::widget(i))
        {
            return mTabBar->tabIcon(i);
        }
    }

    // should be a detached window
    MHDetachedWindow* window = dynamic_cast<MHDetachedWindow*>(reinterpret_cast<QWidget*>(index)->parent());
    return window->windowIcon();
}

void MHTabWidget::setLatestFocused(QWidget* w)
{
    mHistory.removeAll((MIDPKey)w);
    mHistory.push_front((MIDPKey)w);
}

QString MHTabWidget::getNativeName(int index)
{
    if(index < QTabWidget::count())
    {
        return mNativeNames.at(index);
    }
    else
    {
        MHDetachedWindow* window = dynamic_cast<MHDetachedWindow*>(mWindows.at(index - QTabWidget::count())->parent());
        if(window)
            return window->mNativeName;
        else
            return QString();
    }
}

void MHTabWidget::showPreviousTab()
{
    if(QTabWidget::count() <= 1)
    {
        return;
    }

    int previousTabIndex = QTabWidget::currentIndex();
    if(previousTabIndex == 0)
    {
        previousTabIndex = QTabWidget::count() - 1;
    }
    else
    {
        previousTabIndex--;
    }

    QTabWidget::setCurrentIndex(previousTabIndex);
}

void MHTabWidget::showNextTab()
{
    if(QTabWidget::count() <= 1)
    {
        return;
    }

    QTabWidget::setCurrentIndex((QTabWidget::currentIndex() + 1) % QTabWidget::count());
}

void MHTabWidget::showPreviousView()
{
    mHistoryPopup->gotoPreviousItem();
}

void MHTabWidget::showNextView()
{
    mHistoryPopup->gotoNextItem();
}

void MHTabWidget::deleteCurrentTab()
{
    if(QTabWidget::count() == 0)
    {
        return;
    }

    int index = QTabWidget::currentIndex();
    DeleteTab(index);
    if(index < count())
    {
        // open the tab to the right of the deleted tab
        setCurrentIndex(index);
    }
}

//----------------------------------------------------------------------------

MHDetachedWindow::MHDetachedWindow(QWidget* parent) : QMainWindow(parent)
{
}

MHDetachedWindow::~MHDetachedWindow()
{
}

void MHDetachedWindow::closeEvent(QCloseEvent* event)
{
    Q_UNUSED(event);

    emit OnClose(this);
}

bool MHDetachedWindow::event(QEvent* event)
{
    if(event->type() == QEvent::WindowActivate && this->isActiveWindow())
    {
        emit OnFocused(this);
    }
    return QMainWindow::event(event);
}
