//
// Demo Editor -- Qt GUI setup parts and callbacks
// (C) Copyright 2012 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
//
#include "edmain.h"

#include <QCloseEvent>
#include <QToolBar>
#include <QMenuBar>
#include <QStatusBar>
#include <QMenu>
#include <QProgressBar>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QVBoxLayout>



void DemoEditor::updateMenuStates()
{
    // Set window title based on document filename and changed status
    QString name;

    if (!currTimeline || currTimeline->filename.isEmpty())
        name = DOC_DEF_FILENAME;
    else
        name = currTimeline->filename;
    
    if (currTimeline != NULL)
    {
        if (currTimeline->tl != NULL && currTimeline->tl->name != NULL)
            name += " (" + QString(currTimeline->tl->name) + ")";

        if (currTimeline->touched())
            name = "*" + name;
    }

    setWindowTitle(name + " - " + QCoreApplication::applicationName());

    // Enable menu items based on states
    menuActSave->setEnabled(currTimeline != NULL&& currTimeline->touched());
    menuActSaveAs->setEnabled(currTimeline != NULL);
    
    // Enable undo/redo items and set their texts based on history status
    int historyLevels = undoHistory.size();
    QString itemText;
    bool itemEnabled;
    
    if (undoHistoryPos >= 0 && undoHistoryPos < historyLevels)
    {
        itemText = " " + undoHistory.at(undoHistoryPos)->state();
        itemEnabled = true;
    }
    else
    {
        itemText = "";
        itemEnabled = false;
    }
    
    menuActRedo->setEnabled(itemEnabled);
    menuActRedo->setText("&Redo" + itemText);

    if (undoHistoryPos > 0 && historyLevels > 0)
    {
        itemText = " " + undoHistory.at(undoHistoryPos - 1)->state();
        itemEnabled = true;
    }
    else
    {
        itemText = "";
        itemEnabled = false;
    }
    
    menuActUndo->setEnabled(itemEnabled);
    menuActUndo->setText("&Undo" + itemText);

    update();
}


//
// Show about dialog
//
void DemoEditor::actionAboutBox()
{
    QMessageBox::about(this,
    "About "+ QCoreApplication::applicationName(),
    "<h1>" + QCoreApplication::applicationName() +
    " v"+ QCoreApplication::applicationVersion() +"</h1><br>\n"
    "(C) Copyright 2012 Matti 'ccr' H&auml;m&auml;l&auml;inen<br>\n"
    "<br>\n"
    "<b>A demo editor TNSP dmlib engine.</b><br>\n");
}


//
// Show a dialog inquiring the user whether to save the current
// document if it has been modified since last save/load.
//
QMessageBox::StandardButton DemoEditor::showDocumentModifiedDialog()
{
    return QMessageBox::question(this,
        "The document has been modified.",
        "Do you want to save your changes?",
        QMessageBox::Discard | QMessageBox::Cancel | QMessageBox::Save,
        QMessageBox::Save);
}


//
// Generic error/warning dialog
//
void DemoEditor::showFileErrorDialog(QString operation, int code)
{
/*
    QString msg;
    
    QMessageBox::error(this,
    "A non-critical error occured",
    operation
*/
}


void DemoEditor::actionFileNew()
{
    bool okToCreate = true;

    if (currTimeline != NULL && currTimeline->touched())
    {
        okToCreate = false;
        switch (showDocumentModifiedDialog())
        {
            case QMessageBox::Discard:
                okToCreate = true;
                break;
            
            case QMessageBox::Save:
                actionFileSave();
                if (!currTimeline->touched())
                {
                    QMessageBox::information(this, 
                        "Document saved",
                        "The document was saved as " + currTimeline->filename);

                    okToCreate = true;
                }
                break;

            default:
                break;
        }
    }
    
    if (okToCreate)
        createNewFile();
}


void DemoEditor::actionFileOpen()
{
    bool okToOpen = true;

    if (currTimeline != NULL && currTimeline->touched())
    {
        okToOpen = false;

        switch (showDocumentModifiedDialog())
        {
            case QMessageBox::Discard:
                okToOpen = true;
                break;
            
            case QMessageBox::Save:
                actionFileSave();
                if (!currTimeline->touched())
                {
                    QMessageBox::information(this, 
                        "Document saved",
                        "The document was saved as " + currTimeline->filename);
                    
                    okToOpen = true;
                }
                break;

            default:
                break;
        }
    }
        
    if (okToOpen)
    {
        QFileDialog fdialog(this);

        fdialog.setAcceptMode(QFileDialog::AcceptOpen);
        fdialog.setFileMode(QFileDialog::ExistingFile);
        fdialog.setNameFilter("Demo timeline files (*.demo)");
        fdialog.setDefaultSuffix("demo");

        if (fdialog.exec())
            readFromFile(fdialog.selectedFiles()[0]);
    }
}


void DemoEditor::actionFileSaveAs()
{
    if (!currTimeline)
    {
        qDebug() << "DemoEditor::actionFileSaveAs(): currTimeline == null";
        return;
    }

    QFileDialog fdialog(this);

    fdialog.setAcceptMode(QFileDialog::AcceptSave);
    fdialog.setFileMode(QFileDialog::AnyFile);
    fdialog.setNameFilter("Demo files (*.demo)");
    fdialog.setDefaultSuffix("demo");
    fdialog.setConfirmOverwrite(true);

    fdialog.selectFile(currTimeline->filename.isEmpty() ? currTimeline->filename : DOC_DEF_FILENAME);
    
    if (fdialog.exec())
        saveToFile(fdialog.selectedFiles()[0]);
}


void DemoEditor::actionFileSave()
{
    if (!currTimeline)
    {
        qDebug() << "DemoEditor::actionFileSave(): currTimeline == null";
        return;
    }

    // If filename has been set, save .. otherwise go to save as
    if (!currTimeline->filename.isEmpty())
        saveToFile(currTimeline->filename);
    else
        actionFileSaveAs();
}


void DemoEditor::closeEvent(QCloseEvent *event)
{
    bool okToClose = true;

    if (currTimeline && currTimeline->touched())
    {
        okToClose = false;
        switch (showDocumentModifiedDialog())
        {
            case QMessageBox::Discard:
                okToClose = true;
                break;
            
            case QMessageBox::Save:
                actionFileSave();
                if (!currTimeline->touched())
                    okToClose = true;
                break;
            
            default:
                break;
        }
    }
    
    if (okToClose)
        event->accept();
}


//
// Various menu actions
//
void DemoEditor::actionControlChanged(QAction *act)
{
//    demoView->setToolMode(act->data().toInt());
}


//
// Update statusbar message text
//
void DemoEditor::statusMsg(QString message)
{
    statusBar()->showMessage(message, 0);
}


//
// Set active element of an action group based on matching the data
//
void DemoEditor::setActionGroupChecked(QActionGroup *group, QVariant data)
{
    QList<QAction *> items = group->actions();

    for (int i = 0; i < items.size(); i++)
    {
        QAction *act = items.at(i);
        if (act->data() == data)
        {
            act->setChecked(true);
            return;
        }
    }
}


//
// Helper functions for creating GUI elements
//
QAction * DemoEditor::createToolButton(QActionGroup *group, QString name, QIcon icon, QString statustip, QVariant data)
{
    QAction *action = new QAction(icon, name, group);
    
    action->setStatusTip(statustip + ".");
    action->setCheckable(true);
    action->setData(data);
    
    return action;
}


QAction * DemoEditor::createMenuAction(QString name, const QKeySequence &shortcut, QString tooltip)
{
    QAction *action = new QAction(name, this);
    
    if (shortcut != QKeySequence(QKeySequence::UnknownKey))
        action->setShortcut(shortcut);
    
    if (!tooltip.isNull())
        action->setStatusTip(tooltip + ".");

    return action;
}


QAction * DemoEditor::createMenuGroupAction(QMenu *menu, QActionGroup *group, QString name, const QKeySequence &shortcut, QString tooltip, QVariant data)
{
    QAction *action = createMenuAction(name, shortcut, tooltip);
    
    action->setCheckable(true);
    action->setData(data);

    menu->addAction(action);
    group->addAction(action);

    return action;
}


#define MCONNECT(menu, act, slot) do { connect(act, SIGNAL(triggered()), this, SLOT(slot)); menu->addAction(act); } while(0)


//
// Create GUI elements
//
void DemoEditor::createMainGUI()
{
    QAction *act;
    QMenu *fileMenu, *editMenu, *viewMenu, *helpMenu;

    qDebug() << "- Constructing menus";

    //
    // File menu
    //
    fileMenu = menuBar()->addMenu("&File");

    act = createMenuAction("&New", QKeySequence::New, "Create a new demo timeline");
    MCONNECT(fileMenu, act, actionFileNew());

    menuActOpen = createMenuAction("&Open", QKeySequence::Open, "Open a demo timeline file");
    MCONNECT(fileMenu, menuActOpen, actionFileOpen());

    menuActSave = createMenuAction("&Save", QKeySequence::Save, "Save demo timeline");
    MCONNECT(fileMenu, menuActSave, actionFileSave());

    menuActSaveAs = createMenuAction("Save &as", QKeySequence::SaveAs, "Save demo timeline as a new file");
    MCONNECT(fileMenu, menuActSaveAs, actionFileSaveAs());

    fileMenu->addSeparator();

    QKeySequence qseq(Qt::CTRL + Qt::Key_Q);
    act = createMenuAction("&Quit", qseq, "Exit application");
    MCONNECT(fileMenu, act, close());


    //
    // Edit menu
    //
    editMenu = menuBar()->addMenu("&Edit");

    menuActUndo = createMenuAction("&Undo",  QKeySequence::Undo, "Undo last change");
    MCONNECT(editMenu, menuActUndo, performUndo());

    menuActRedo = createMenuAction("&Redo",  QKeySequence::Redo, "Redo last change");
    MCONNECT(editMenu, menuActRedo, performRedo());

#if 0
    editMenu->addSeparator();

    menuActCut = createMenuAction("Cu&t",  QKeySequence::Cut, "Cut object");
    MCONNECT(editMenu, menuActCut, actionCut());

    menuActCopy = createMenuAction("&Copy",  QKeySequence::Copy, "Copy object");
    MCONNECT(editMenu, menuActCopy, actionCopy());

    menuActPaste = createMenuAction("&Paste", QKeySequence::Paste, "Paste object");
    MCONNECT(editMenu, menuActPaste, actionPaste());

    menuActDelete = createMenuAction("&Delete", QKeySequence::Delete, "Delete object");
    MCONNECT(editMenu, menuActDelete, actionDelete());
#endif

    //
    // Help menu
    //
    helpMenu = menuBar()->addMenu("&Help");
    act = createMenuAction("About", 0, "Show information about application");
    MCONNECT(helpMenu, act, actionAboutBox());



    //
    // Controls toolbar
    //
    qDebug() << "- Constructing toolbars";

    actGroupControls = new QActionGroup(this);
    actGroupControls->setExclusive(true);
    connect(actGroupControls, SIGNAL(triggered(QAction*)), this, SLOT(actionControlChanged(QAction *)));
    
    createToolButton(actGroupControls, "Rewind", QIcon("rewind.png"),
        "Rewind to start of the timeline", CTRL_REWIND);

    createToolButton(actGroupControls, "Play start", QIcon("play1.png"),
        "Play from start", CTRL_PLAY_START);

    createToolButton(actGroupControls, "Play current", QIcon("play2.png"),
        "Play from current position", CTRL_PLAY_CURRENT);

    createToolButton(actGroupControls, "Pause", QIcon("pause.png"),
        "Pause", CTRL_PAUSE);


    QToolBar *controlButtons = new QToolBar("Player controls", this);
    controlButtons->setMovable(false);
    controlButtons->setFloatable(false);
    controlButtons->setIconSize(QSize(CTRL_ICON_SIZE, CTRL_ICON_SIZE));
    controlButtons->setToolButtonStyle(Qt::ToolButtonIconOnly);
    controlButtons->addActions(actGroupControls->actions());


    //
    // Effect resource view
    //
    qDebug() << "- Constructing effects resource view";

    resourceView = new QTableView(this);
    resourceView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    resourceView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    resourceView->setSelectionMode(QAbstractItemView::SingleSelection);
    resourceView->setSelectionBehavior(QAbstractItemView::SelectRows);
    resourceModel = new QEDResourceModel(this);
    resourceView->setModel(resourceModel);

    //
    // Construct the main screen
    //
    qDebug() << "- Constructing main screen layout";

    QWidget *sideVBoxContainer = new QWidget();
    QSizePolicy sideVBoxPolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
    sideVBoxPolicy.setHeightForWidth(sideVBoxContainer->sizePolicy().hasHeightForWidth());
    sideVBoxContainer->setSizePolicy(sideVBoxPolicy);

    QVBoxLayout *sideVBox = new QVBoxLayout(sideVBoxContainer);
    sideVBox->setSpacing(0);
    sideVBox->setContentsMargins(0, 0, 0, 0);
    sideVBox->addWidget(resourceView);
    sideVBox->addWidget(controlButtons);


    QWidget *holder = new QWidget();
    QVBoxLayout *verticalSplitter = new QVBoxLayout(holder);
    QHBoxLayout *horizSplitter = new QHBoxLayout();


    timelineScrollBar = new QScrollBar(Qt::Horizontal);
    connect(timelineScrollBar, SIGNAL(valueChanged(int)), this, SLOT(actionTimelineScrollChanged(int)));
    
    timelineAudioTrack = new QEDWaveTrackView();
    connect(timelineAudioTrack, SIGNAL(offsetChanged(float)), this, SLOT(actionOffsetChanged(float)));
    connect(timelineAudioTrack, SIGNAL(timeChanged(float)), this, SLOT(actionTimeChanged(float)));

    timelineView = new QEDTimelineView();
    connect(timelineView, SIGNAL(timelineChanged()), this, SLOT(actionTimelineChanged()));

    QScrollArea *scrollArea = new QScrollArea();
    scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
    scrollArea->setWidget(timelineView);
    scrollArea->setWidgetResizable(true);


    demoView = new QEDSWDemoView(this);

    verticalSplitter->addLayout(horizSplitter);
    verticalSplitter->addWidget(scrollArea);
    verticalSplitter->addWidget(timelineAudioTrack);
    verticalSplitter->addWidget(timelineScrollBar);

    horizSplitter->addWidget(sideVBoxContainer);
    horizSplitter->addWidget(demoView);


    updateTimelineView();

    setCentralWidget(holder);
}
