#include "IEditor.h"

#include "Editor/Undo/IUndoCommand.h"

#include <QMenu>
#include <QMessageBox>
#include <QToolBar>

IEditor::IEditor(QWidget* pParent)
    : QMainWindow(pParent)
{
    // Register the editor window
    gpEdApp->AddEditor(this);

    // Create undo actions
    QAction *pUndoAction = mUndoStack.createUndoAction(this);
    QAction *pRedoAction = mUndoStack.createRedoAction(this);
    pUndoAction->setShortcut(QKeySequence::Undo);
    pRedoAction->setShortcut(QKeySequence::Redo);
    pUndoAction->setIcon(QIcon(":/icons/Undo.svg"));
    pRedoAction->setIcon(QIcon(":/icons/Redo.svg"));
    mUndoActions.push_back(pUndoAction);
    mUndoActions.push_back(pRedoAction);

    connect(&mUndoStack, SIGNAL(indexChanged(int)), this, SLOT(OnUndoStackIndexChanged()));
}

QUndoStack& IEditor::UndoStack()
{
    return mUndoStack;
}

void IEditor::AddUndoActions(QToolBar* pToolBar, QAction* pBefore /*= 0*/)
{
    pToolBar->insertActions(pBefore, mUndoActions);
}

void IEditor::AddUndoActions(QMenu* pMenu, QAction* pBefore /*= 0*/)
{
    pMenu->insertActions(pBefore, mUndoActions);
}

bool IEditor::CheckUnsavedChanges()
{
    // Check whether the user has unsaved changes, return whether it's okay to clear the scene
    bool OkToClear = !isWindowModified();

    if (!OkToClear)
    {
        int Result = QMessageBox::warning(this, "Save", "You have unsaved changes. Save?", QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel);

        if (Result == QMessageBox::Yes)
            OkToClear = Save();

        else if (Result == QMessageBox::No)
        {
            mUndoStack.setIndex(0); // Revert all changes
            OkToClear = true;
        }

        else if (Result == QMessageBox::Cancel)
            OkToClear = false;
    }

    return OkToClear;
}

/** QMainWindow overrides */
void IEditor::closeEvent(QCloseEvent* pEvent)
{
    if (CheckUnsavedChanges())
    {
        mUndoStack.clear();
        pEvent->accept();
        emit Closed();
    }
    else
    {
        pEvent->ignore();
    }
}

/** Non-virtual slots */
bool IEditor::SaveAndRepack()
{
    if (Save())
    {
        gpEdApp->CookAllDirtyPackages();
        return true;
    }
    else return false;
}


void IEditor::OnUndoStackIndexChanged()
{
    // Check the commands that have been executed on the undo stack and find out whether any of them affect the clean state.
    // This is to prevent commands like select/deselect from altering the clean state.
    int CurrentIndex = mUndoStack.index();
    int CleanIndex = mUndoStack.cleanIndex();

    if (CleanIndex == -1)
    {
        if (!isWindowModified())
            mUndoStack.setClean();

        return;
    }

    if (CurrentIndex == CleanIndex)
        setWindowModified(false);

    else
    {
        bool IsClean = true;
        int LowIndex = (CurrentIndex > CleanIndex ? CleanIndex : CurrentIndex);
        int HighIndex = (CurrentIndex > CleanIndex ? CurrentIndex - 1 : CleanIndex - 1);

        for (int i = LowIndex; i <= HighIndex; i++)
        {
            const QUndoCommand *pkQCmd = mUndoStack.command(i);

            if (const IUndoCommand* pkCmd = dynamic_cast<const IUndoCommand*>(pkQCmd))
            {
                if (pkCmd->AffectsCleanState())
                    IsClean = false;
            }

            else if (pkQCmd->childCount() > 0)
            {
                for (int ChildIdx = 0; ChildIdx < pkQCmd->childCount(); ChildIdx++)
                {
                    const IUndoCommand *pkCmd = static_cast<const IUndoCommand*>(pkQCmd->child(ChildIdx));

                    if (pkCmd->AffectsCleanState())
                    {
                        IsClean = false;
                        break;
                    }
                }
            }

            if (!IsClean) break;
        }

        setWindowModified(!IsClean);
    }
}
