#include <QtWidgets>
#include "Utilities.h"
#include "MdiWindow.h"
#include "ui_ExtensibleObjectModelUI.h"


MdiChild::MdiChild()
{
    setAttribute(Qt::WA_DeleteOnClose);
    isUntitled = true;
    connect(document(), SIGNAL(contentsChanged()), this, SIGNAL(documentContentsChanged()));
}

MdiChild::~MdiChild()
{
    closing();
}

void MdiChild::newFile()
{
    static int sequenceNumber = 1;
    isUntitled = true;
    curFile = tr("document%1.txt").arg(sequenceNumber++);
    setWindowTitle(curFile + "[*]");
    connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified()));
}

bool MdiChild::loadFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("MDI"),
                 tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString()));
        return false;
    }
    QTextStream in(&file);
    QApplication::setOverrideCursor(Qt::WaitCursor);
    setPlainText(in.readAll());
    QApplication::restoreOverrideCursor();

    setCurrentFile(fileName);
    connect(document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified()));
    return true;
}

bool MdiChild::save()
{
    return (isUntitled) ? saveAs() : saveFile(curFile);
}

bool MdiChild::saveAs()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), curFile);
    if (fileName.isEmpty())
        return false;
    return saveFile(fileName);
}

bool MdiChild::saveFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::WriteOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("MDI"),
              tr("Cannot write file %1:\n%2.").arg(fileName).arg(file.errorString()));
        return false;
    }
    QTextStream out(&file);
    QApplication::setOverrideCursor(Qt::WaitCursor);
    out << toPlainText();
    QApplication::restoreOverrideCursor();

    setCurrentFile(fileName);
    return true;
}

QString MdiChild::userFriendlyCurrentFile()
{
    return strippedName(curFile);
}

void MdiChild::closeEvent(QCloseEvent *event)
{
    if (maybeSave()) {
        event->accept();
    } else {
        event->ignore();
    }
}

void MdiChild::documentWasModified()
{
    setWindowModified(document()->isModified());
}

bool MdiChild::maybeSave()
{
    if (document()->isModified()) {
        QMessageBox::StandardButton ret;
        ret = QMessageBox::warning(this, tr("MDI"),
                     tr("'%1' has been modified.\n"
                        "Do you want to save your changes?")
                     .arg(userFriendlyCurrentFile()),
                     QMessageBox::Save | QMessageBox::Discard
                     | QMessageBox::Cancel);
        if (ret == QMessageBox::Save)
            return save();
        else if (ret == QMessageBox::Cancel)
            return false;
    }
    return true;
}

void MdiChild::setCurrentFile(const QString &fileName)
{
    curFile = QFileInfo(fileName).canonicalFilePath();
    isUntitled = false;
    document()->setModified(false);
    setWindowModified(false);
    setWindowTitle(userFriendlyCurrentFile() + "[*]");
}

QString MdiChild::strippedName(const QString &fullFileName)
{
    return QFileInfo(fullFileName).fileName();
}



void MdiChild::markdownSelection(const char* preText, const char* postText)
{
    QTextCursor cur = textCursor();
    cur.beginEditBlock();
    QString selectedText = cur.selection().toPlainText();
    cur.removeSelectedText();
    cur.insertText(preText + selectedText + postText);
    cur.endEditBlock();
}



void MdiChild::bold()
{
    markdownSelection("**", "**");
}
void MdiChild::italic()
{
    markdownSelection("*", "*");
}
void MdiChild::quote()
{
    markdownSelection("`", "`");
}
void MdiChild::code()
{
    //markdownSelection("*", "*");
    // Need to add indentation of text with 4 spaces each line
}
void MdiChild::heading1()
{
    markdownSelection("# ", " #");
}
void MdiChild::heading2()
{
    markdownSelection("## ", " ##");
}
void MdiChild::heading3()
{
    markdownSelection("### ", " ###");
}
void MdiChild::heading4()
{
    markdownSelection("#### ", " ####");
}
void MdiChild::hyperlink()
{
}
void MdiChild::image()
{
}
void MdiChild::unorderedList()
{
}
void MdiChild::orderedList()
{
}
void MdiChild::horizontalRule()
{
}
void MdiChild::timestamp()
{
}
void MdiChild::pageNumber()
{
}
void MdiChild::pageCount()
{
}
void MdiChild::del()
{
    //QTextEdit::cursor().pos()
}
void MdiChild::undo()
{
}
void MdiChild::redo()
{
}
void MdiChild::find()
{
}
void MdiChild::findNext()
{
}
void MdiChild::findPrevious()
{
}
void MdiChild::goToLine()
{
}






MdiWindow::MdiWindow(QWidget *parent) :
    QMainWindow(parent), appWindow(nullptr)
{
}


void MdiWindow::init(Ui_MainWindow* ui)
{
    appWindow = ui;
    mdiArea = ui->mdiArea;//new QMdiArea;
    connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updateActions()));
    connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(updatePreview()));

    windowMapper = new QSignalMapper(this);
    connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*)));

    QObject::connect(appWindow->actionNew,          SIGNAL(triggered(bool)), this, SLOT(newFile()));
    QObject::connect(appWindow->actionOpen,         SIGNAL(triggered(bool)), this, SLOT(open()));
    QObject::connect(appWindow->actionSave,         SIGNAL(triggered(bool)), this, SLOT(save()));
    QObject::connect(appWindow->actionSave_As,      SIGNAL(triggered(bool)), this, SLOT(saveAs()));
    QObject::connect(appWindow->actionSwitchRTL,    SIGNAL(triggered(bool)), this, SLOT(switchLayoutDirection()));
#ifndef QT_NO_CLIPBOARD
    QObject::connect(appWindow->actionCut,          SIGNAL(triggered(bool)), this, SLOT(cut()));
    QObject::connect(appWindow->actionCopy,         SIGNAL(triggered(bool)), this, SLOT(copy()));
    QObject::connect(appWindow->actionPaste,        SIGNAL(triggered(bool)), this, SLOT(paste()));
#endif
    QObject::connect(appWindow->actionBold,         SIGNAL(triggered(bool)), this, SLOT(bold()));
    QObject::connect(appWindow->actionItalic,       SIGNAL(triggered(bool)), this, SLOT(italic()));
    QObject::connect(appWindow->actionQuote,        SIGNAL(triggered(bool)), this, SLOT(quote()));
    QObject::connect(appWindow->actionCode,         SIGNAL(triggered(bool)), this, SLOT(code()));
    QObject::connect(appWindow->actionHeading_1,    SIGNAL(triggered(bool)), this, SLOT(heading1()));
    QObject::connect(appWindow->actionHeading_2,    SIGNAL(triggered(bool)), this, SLOT(heading2()));
    QObject::connect(appWindow->actionHeading_3,    SIGNAL(triggered(bool)), this, SLOT(heading3()));
    QObject::connect(appWindow->actionHeading_4,    SIGNAL(triggered(bool)), this, SLOT(heading4()));
    QObject::connect(appWindow->actionHyperlink,    SIGNAL(triggered(bool)), this, SLOT(hyperlink()));
    QObject::connect(appWindow->actionImage,        SIGNAL(triggered(bool)), this, SLOT(image()));
    QObject::connect(appWindow->actionUnordered_List, SIGNAL(triggered(bool)), this, SLOT(unorderedList()));
    QObject::connect(appWindow->actionOrdered_List,   SIGNAL(triggered(bool)), this, SLOT(orderedList()));
    QObject::connect(appWindow->actionHorizontal_Rule,SIGNAL(triggered(bool)), this, SLOT(horizontalRule()));
    QObject::connect(appWindow->actionTimestamp,    SIGNAL(triggered(bool)), this, SLOT(timestamp()));
    QObject::connect(appWindow->actionPage_Number,  SIGNAL(triggered(bool)), this, SLOT(pageNumber()));
    QObject::connect(appWindow->actionPage_Count,   SIGNAL(triggered(bool)), this, SLOT(pageCount()));
    QObject::connect(appWindow->actionOptions,      SIGNAL(triggered(bool)), this, SLOT(options()));
    QObject::connect(appWindow->actionSave_All,     SIGNAL(triggered(bool)), this, SLOT(saveAll()));
    QObject::connect(appWindow->actionSelect_All,   SIGNAL(triggered(bool)), this, SLOT(selectAll()));
    QObject::connect(appWindow->actionDelete,       SIGNAL(triggered(bool)), this, SLOT(del()));
    QObject::connect(appWindow->actionUndo,         SIGNAL(triggered(bool)), this, SLOT(undo()));
    QObject::connect(appWindow->actionRedo,         SIGNAL(triggered(bool)), this, SLOT(redo()));
    QObject::connect(appWindow->actionFind,         SIGNAL(triggered(bool)), this, SLOT(find()));
    QObject::connect(appWindow->actionFind_Next,    SIGNAL(triggered(bool)), this, SLOT(findNext()));
    QObject::connect(appWindow->actionFind_Previous,SIGNAL(triggered(bool)), this, SLOT(findPrevious()));
    QObject::connect(appWindow->actionGo_to_Line,   SIGNAL(triggered(bool)), this, SLOT(goToLine()));
    QObject::connect(appWindow->action_Tile_Windows,SIGNAL(triggered(bool)), this, SLOT(tileSubWindows()));
    QObject::connect(appWindow->action_Windowed,    SIGNAL(triggered(bool)), this, SLOT(cascadeSubWindows()));
    QObject::connect(appWindow->actionTabbed,       SIGNAL(triggered(bool)), this, SLOT(tabSubWindows()));
    QObject::connect(appWindow->actionClose,        SIGNAL(triggered(bool)), mdiArea, SLOT(closeActiveSubWindow()));
    QObject::connect(appWindow->actionClose_All,    SIGNAL(triggered(bool)), mdiArea, SLOT(closeAllSubWindows()));
    QObject::connect(appWindow->actionNext,         SIGNAL(triggered(bool)), mdiArea, SLOT(activateNextSubWindow()));
    QObject::connect(appWindow->actionPrevious,     SIGNAL(triggered(bool)), mdiArea, SLOT(activatePreviousSubWindow()));

    QObject::connect(appWindow->actionSupport,       SIGNAL(triggered(bool)), this, SLOT(support()));
    QObject::connect(appWindow->actionWebsite,      SIGNAL(triggered(bool)), this, SLOT(website()));

    statusBar()->showMessage(tr("Ready"));
    updateActions();
    readSettings();
    setUnifiedTitleAndToolBarOnMac(true);
#if QT_VERSION >= 0x050000
    menuBar()->setNativeMenuBar(false);
#endif
    tabifyDockWidget(appWindow->projectDock, appWindow->objectsDock);
    tabifyDockWidget(appWindow->projectDock, appWindow->propertiesDock);
    appWindow->projectDock->raise();
    appWindow->eventLogDock->hide();
    appWindow->consoleDock->hide();
}


void MdiWindow::closeEvent(QCloseEvent *event)
{
    YQ_LOG_DEBUG("Closing all documents (received MdiWindow::closeEvent)");
    mdiArea->closeAllSubWindows();
    if (mdiArea->currentSubWindow()) {
        YQ_LOG_DEBUG("Ignoring MdiWindow::closeEvent");
        event->ignore();
    } else {
        writeSettings();
        YQ_LOG_DEBUG("Accepting MdiWindow::closeEvent");
        event->accept();
    }
}


void MdiWindow::newFile()
{
    YQ_LOG_DEBUG("newFile");
    MdiChild *child = createMdiChild();
    child->newFile();
    child->show();
}


void MdiWindow::open()
{
    YQ_LOG_DEBUG("open");
    QString fileName = QFileDialog::getOpenFileName(this);
    if (!fileName.isEmpty()) {
        QMdiSubWindow *existing = findMdiChild(fileName);
        if (existing) {
            mdiArea->setActiveSubWindow(existing);
            return;
        }

        if (openFile(fileName))
            statusBar()->showMessage(tr("File loaded"), 2000);
    }
}


bool MdiWindow::openFile(const QString &fileName)
{
    YQ_LOG_DEBUG("openFile %s", fileName.toLatin1().data());
    MdiChild *child = createMdiChild();
    const bool succeeded = child->loadFile(fileName);
    if (succeeded)
        child->show();
    else
        child->close();
    return succeeded;
}


void MdiWindow::save()
{
    YQ_LOG_DEBUG("save");
    if (activeMdiChild() && activeMdiChild()->save())
        statusBar()->showMessage(tr("File saved"), 2000);
}


void MdiWindow::saveAs()
{
    YQ_LOG_DEBUG("saveAs");
    if (activeMdiChild() && activeMdiChild()->saveAs())
        statusBar()->showMessage(tr("File saved"), 2000);
}


#ifndef QT_NO_CLIPBOARD
void MdiWindow::cut()
{
    if (activeMdiChild())
        activeMdiChild()->cut();
}


void MdiWindow::copy()
{
    if (activeMdiChild())
        activeMdiChild()->copy();
}


void MdiWindow::paste()
{
    if (activeMdiChild())
        activeMdiChild()->paste();
}
#endif


void MdiWindow::support()
{
    QDesktopServices::openUrl(QUrl("http://stackoverflow.org/"));
}


void MdiWindow::website()
{
    QDesktopServices::openUrl(QUrl("http://slashdot.org/"));
}


void MdiWindow::bold()
{
    if (activeMdiChild()) activeMdiChild()->bold();
}
void MdiWindow::italic()
{
    if (activeMdiChild()) activeMdiChild()->italic();
}
void MdiWindow::quote()
{
    if (activeMdiChild()) activeMdiChild()->quote();
}
void MdiWindow::code()
{
    if (activeMdiChild()) activeMdiChild()->code();
}
void MdiWindow::heading1()
{
    if (activeMdiChild()) activeMdiChild()->heading1();
}
void MdiWindow::heading2()
{
    if (activeMdiChild()) activeMdiChild()->heading2();
}
void MdiWindow::heading3()
{
    if (activeMdiChild()) activeMdiChild()->heading3();
}
void MdiWindow::heading4()
{
    if (activeMdiChild()) activeMdiChild()->heading4();
}
void MdiWindow::hyperlink()
{
    if (activeMdiChild()) activeMdiChild()->hyperlink();
}
void MdiWindow::image()
{
    if (activeMdiChild()) activeMdiChild()->image();
}
void MdiWindow::unorderedList()
{
    if (activeMdiChild()) activeMdiChild()->unorderedList();
}
void MdiWindow::orderedList()
{
    if (activeMdiChild()) activeMdiChild()->orderedList();
}
void MdiWindow::horizontalRule()
{
    if (activeMdiChild()) activeMdiChild()->horizontalRule();
}
void MdiWindow::timestamp()
{
    if (activeMdiChild()) activeMdiChild()->timestamp();
}
void MdiWindow::pageNumber()
{
    if (activeMdiChild()) activeMdiChild()->pageNumber();
}
void MdiWindow::pageCount()
{
    if (activeMdiChild()) activeMdiChild()->pageCount();
}
void MdiWindow::options()
{
}
void MdiWindow::saveAll()
{
}
void MdiWindow::selectAll()
{
    if (activeMdiChild()) activeMdiChild()->selectAll();
}
void MdiWindow::del()
{
    if (activeMdiChild()) activeMdiChild()->del();
}
void MdiWindow::undo()
{
    if (activeMdiChild()) activeMdiChild()->undo();
}
void MdiWindow::redo()
{
    if (activeMdiChild()) activeMdiChild()->redo();
}
void MdiWindow::find()
{
    if (activeMdiChild()) activeMdiChild()->find();
}
void MdiWindow::findNext()
{
    if (activeMdiChild()) activeMdiChild()->findNext();
}
void MdiWindow::findPrevious()
{
    if (activeMdiChild()) activeMdiChild()->findPrevious();
}
void MdiWindow::goToLine()
{
    if (activeMdiChild()) activeMdiChild()->goToLine();
}


#include "html.h"
#include "document.h"

void MdiWindow::updatePreview()
{
    MdiChild* child = activeMdiChild();
    if (child)
    {
        QByteArray text = child->toPlainText().toLatin1();
        hoedown_html_flags flags = (hoedown_html_flags)(HOEDOWN_HTML_ESCAPE | HOEDOWN_HTML_HARD_WRAP | HOEDOWN_HTML_USE_XHTML);
        hoedown_renderer *renderer = hoedown_html_renderer_new(flags, 0);
        hoedown_buffer *ob = hoedown_buffer_new(1024);
        hoedown_document *document = hoedown_document_new(renderer, HOEDOWN_EXT_SPACE_HEADERS, 64);
        hoedown_document_render(document, ob, (uint8_t*)text.data(), text.size());
        hoedown_document_free(document);
        hoedown_html_renderer_free(renderer);
        appWindow->previewText->setText(QString(QLatin1String((char*)ob->data, ob->size)));
        hoedown_buffer_free(ob);
    }
}


void MdiWindow::updateActions()
{
    bool hasMdiChild = (activeMdiChild() != 0);
    bool hasSelection = (hasMdiChild && activeMdiChild()->textCursor().hasSelection());

    appWindow->actionSave->setEnabled(hasMdiChild);
    appWindow->actionSave_As->setEnabled(hasMdiChild);
    appWindow->actionSave_All->setEnabled(hasMdiChild);
    appWindow->actionClose->setEnabled(hasMdiChild);
#ifndef QT_NO_CLIPBOARD
    appWindow->actionCut->setEnabled(hasSelection);
    appWindow->actionCopy->setEnabled(hasSelection);
    appWindow->actionPaste->setEnabled(hasMdiChild);
#endif
    appWindow->actionSelect_All->setEnabled(hasMdiChild);
    appWindow->actionDelete->setEnabled(hasMdiChild);
    appWindow->actionUndo->setEnabled(hasMdiChild);
    appWindow->actionRedo->setEnabled(hasMdiChild);
    appWindow->actionFind->setEnabled(hasMdiChild);
    appWindow->actionFind_Next->setEnabled(hasMdiChild);
    appWindow->actionFind_Previous->setEnabled(hasMdiChild);
    appWindow->actionGo_to_Line->setEnabled(hasMdiChild);
    appWindow->actionBold->setEnabled(hasMdiChild);
    appWindow->actionItalic->setEnabled(hasMdiChild);
    appWindow->actionQuote->setEnabled(hasMdiChild);
    appWindow->actionCode->setEnabled(hasMdiChild);
    appWindow->actionHeading_1->setEnabled(hasMdiChild);
    appWindow->actionHeading_2->setEnabled(hasMdiChild);
    appWindow->actionHeading_3->setEnabled(hasMdiChild);
    appWindow->actionHeading_4->setEnabled(hasMdiChild);
    appWindow->actionHyperlink->setEnabled(hasMdiChild);
    appWindow->actionImage->setEnabled(hasMdiChild);
    appWindow->actionUnordered_List->setEnabled(hasMdiChild);
    appWindow->actionOrdered_List->setEnabled(hasMdiChild);
    appWindow->actionHorizontal_Rule->setEnabled(hasMdiChild);
    appWindow->actionTimestamp->setEnabled(hasMdiChild);
    appWindow->actionPage_Number->setEnabled(hasMdiChild);
    appWindow->actionPage_Count->setEnabled(hasMdiChild);
    appWindow->actionClose_All->setEnabled(hasMdiChild);
    appWindow->actionNext->setEnabled(hasMdiChild);
    appWindow->actionPrevious->setEnabled(hasMdiChild);
    appWindow->action_Tile_Windows->setEnabled(hasMdiChild);
    appWindow->action_Windowed->setEnabled(hasMdiChild);

    //appWindow->actionTabbed->setEnabled(hasMdiChild);
    //separatorAct->setVisible(hasMdiChild);

    appWindow->menuView->clear();
    appWindow->menuView->addAction(appWindow->action_Tile_Windows);
    appWindow->menuView->addAction(appWindow->action_Windowed);
    appWindow->menuView->addAction(appWindow->actionTabbed);
    appWindow->menuView->addSeparator();
    appWindow->menuView->addAction(appWindow->actionNext);
    appWindow->menuView->addAction(appWindow->actionPrevious);

    QList<QMdiSubWindow *> windows = mdiArea->subWindowList();
    if (!windows.isEmpty())
        appWindow->menuView->addSeparator();

    for (int i = 0; i < windows.size(); ++i) {
        MdiChild *child = qobject_cast<MdiChild *>(windows.at(i)->widget());
        QString text = QString("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile());
        if (i < 9)
            text = "&" + text;
        QAction *action = appWindow->menuView->addAction(text);
        action->setCheckable(true);
        action->setChecked(child == activeMdiChild());
        connect(action, SIGNAL(triggered(bool)), windowMapper, SLOT(map()));
        windowMapper->setMapping(action, windows.at(i));
    }
}


MdiChild *MdiWindow::createMdiChild()
{
    MdiChild *child = new MdiChild;
    mdiArea->addSubWindow(child);
    connect(child, SIGNAL(documentContentsChanged()), this, SLOT(updatePreview()));
    connect(child, SIGNAL(closing()), this,  SLOT(updateActions()));
#ifndef QT_NO_CLIPBOARD
    connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCut,  SLOT(setEnabled(bool)));
    connect(child, SIGNAL(copyAvailable(bool)), appWindow->actionCopy, SLOT(setEnabled(bool)));
#endif
    return child;
}


void MdiWindow::cascadeSubWindows()
{
    mdiArea->setViewMode(QMdiArea::SubWindowView);
    mdiArea->cascadeSubWindows();
}


void MdiWindow::tileSubWindows()
{
    mdiArea->setViewMode(QMdiArea::SubWindowView);
    mdiArea->tileSubWindows();
}


void MdiWindow::tabSubWindows()
{
    mdiArea->setViewMode(QMdiArea::TabbedView);
}


void MdiWindow::readSettings()
{
    YQ_LOG_DEBUG("Reading settings");
    QSettings settings("QtProject", "MDI Example");
    QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
    QSize size = settings.value("size", QSize(400, 400)).toSize();
    move(pos);
    resize(size);
}


void MdiWindow::writeSettings()
{
    YQ_LOG_DEBUG("Saving settings");
    QSettings settings("QtProject", "MDI Example");
    settings.setValue("pos", pos());
    settings.setValue("size", size());
}


MdiChild *MdiWindow::activeMdiChild()
{
    if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow())
        return qobject_cast<MdiChild *>(activeSubWindow->widget());
    return 0;
}


QMdiSubWindow *MdiWindow::findMdiChild(const QString &fileName)
{
    QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();

    foreach (QMdiSubWindow *window, mdiArea->subWindowList()) {
        MdiChild *mdiChild = qobject_cast<MdiChild *>(window->widget());
        if (mdiChild && mdiChild->currentFile() == canonicalFilePath)
            return window;
    }
    return 0;
}


void MdiWindow::switchLayoutDirection()
{
    if (layoutDirection() == Qt::LeftToRight)
        qApp->setLayoutDirection(Qt::RightToLeft);
    else
        qApp->setLayoutDirection(Qt::LeftToRight);
}


void MdiWindow::setActiveSubWindow(QWidget *window)
{
    if (!window)
        return;
    mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow *>(window));
}
