#include "vymmodelwrapper.h"

#include <QMessageBox>
#include <QQmlEngine>

#include "attributeitem.h"
#include "attribute-wrapper.h"
#include "branchitem.h"
#include "branch-container.h"
#include "branch-wrapper.h"
#include "imageitem.h"
#include "image-wrapper.h"
#include "itemlist-wrapper.h"
#include "mainwindow.h"
#include "mapeditor.h"
#include "misc.h"
#include "vym-wrapper.h"
#include "scripting-xlink-wrapper.h"
#include "vymmodel.h"
#include "xlink.h"
#include "xlinkitem.h"

extern Main *mainWindow;

///////////////////////////////////////////////////////////////////////////
VymModelWrapper::VymModelWrapper(VymModel *m)
{
    //std::cout << "Constr VMWrapper" << this << endl;
    QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
    modelInt = m;
}

VymModelWrapper::~VymModelWrapper()
{
    //std::cout << "Destr VMWrapper" << this << endl;
}

void VymModelWrapper::addMapCenterAtPos(qreal x, qreal y)
{
    if (!modelInt->addMapCenterAtPos(QPointF(x, y)))
        mainWindow->abortScript(
                QJSValue::GenericError,
                "Couldn't add mapcenter");
}

void VymModelWrapper::addSlide() { modelInt->addSlide(); }

int VymModelWrapper::centerCount()
{
    int r = modelInt->centerCount();
    mainWindow->setScriptResult(r);
    return r;
}

void VymModelWrapper::centerOnID(const QString &id)
{
    if (!modelInt->centerOnID(id))
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Could not center on ID %1").arg(id));
}

void VymModelWrapper::copy() { modelInt->copy(); }

void VymModelWrapper::cut() { modelInt->cut(); }

bool VymModelWrapper::exportMap(QJSValueList args)
{
    int argumentsCount = args.count();

    bool r = false;

    if (argumentsCount == 0) {
        mainWindow->abortScript(
                QJSValue::GenericError,
                "Not enough arguments");
        mainWindow->setScriptResult(r);
	return r;
    }

    QString format;
    format = args[0].toString();

    if (argumentsCount == 1) {
        if (format == "Last") {
            modelInt->exportLast();
            r = true;
        } else
            mainWindow->abortScript(
                    QJSValue::GenericError,
                    "Filename missing");
        mainWindow->setScriptResult(r);
	return r;
    }

    QString filePath;

    filePath = args[1].toString();

    if (format == "AO") {
        modelInt->exportAO(filePath, false);
    }
    else if (format == "ASCII") {
        bool listTasks = false;
        if (argumentsCount == 3 && args[2].toString() == "true")
            listTasks = true;
        modelInt->exportASCII(filePath, listTasks, false);
    }
    else if (format == "ConfluenceNewPage") {
        // 0: General export format
        // 1: URL of parent page (required)
        // 2: page title (required)
        if (argumentsCount < 3) {
            mainWindow->abortScript(
                    QJSValue::GenericError,
                    QString("Confluence export new page: Only %1 instead of 3 parameters given")
                     .arg(argumentsCount));
            mainWindow->setScriptResult(r);
	    return r;
        }

        QString url = args[2].toString();
        QString pageName = args[3].toString();

        modelInt->exportConfluence(true, url, pageName, false);
    }
    else if (format == "ConfluenceUpdatePage") {
        // 0: General export format
        // 1: URL of  page to be updated
        // 2: page title (optional)
        if (argumentsCount == 1) {
            mainWindow->abortScript(
                    QJSValue::GenericError,
                    "URL of new page missing in Confluence export");
            mainWindow->setScriptResult(r);
	    return r;
        }
        QString url = args[1].toString();

        QString title = "";
        if (argumentsCount == 3) {
            title = args[2].toString();
        }

        modelInt->exportConfluence(false, url, title, false);
    } else if (format == "CSV") {
        modelInt->exportCSV(filePath, false);
    } else if (format == "HTML") {
        if (argumentsCount < 3) {
            mainWindow->abortScript(
                    QJSValue::GenericError,
                    "Path missing in HTML export");
            mainWindow->setScriptResult(r);
	    return r;
        }
        QString dpath = args[2].toString();
        modelInt->exportHTML(filePath, dpath, false);
    } else if (format == "Image") {
        QString imgFormat;
        if (argumentsCount == 2)
            imgFormat = "PNG";
        else if (argumentsCount == 3)
            imgFormat = args[2].toString();

        QStringList formats;
        formats << "PNG";
        formats << "GIF";
        formats << "JPG";
        formats << "JPEG", formats << "PNG", formats << "PBM", formats << "PGM",
            formats << "PPM", formats << "TIFF", formats << "XBM",
            formats << "XPM";
        if (formats.indexOf(imgFormat) < 0) {
            mainWindow->abortScript(
                    QJSValue::GenericError,
                    QString("%1 not one of the known export formats: ")
                         .arg(imgFormat, formats.join(",")));
            mainWindow->setScriptResult(r);
	    return r;
        }
        modelInt->exportImage(filePath, false, imgFormat);
    } else if (format == "Impress") {
        if (argumentsCount < 3) {
            mainWindow->abortScript(
                    QJSValue::GenericError,
                    "Template file  missing in export to Impress");
            mainWindow->setScriptResult(r);
	    return r;
        }
        QString templ = args[2].toString();
        modelInt->exportImpress(filePath, templ, false);
    } else if (format == "LaTeX") {
        modelInt->exportLaTeX(filePath, false);
    } else if (format == "Markdown") {
        modelInt->exportMarkdown(filePath, false);
    } else if (format == "OrgMode") {
        modelInt->exportOrgMode(filePath, false);
    } else if (format == "PDF") {
        modelInt->exportPDF(filePath, false);
        r = true;
    } else if (format == "SVG") {
        modelInt->exportSVG(filePath, false);
        r = true;
    } else if (format == "TaskJuggler") {
        modelInt->exportTaskJuggler(filePath, false);
    } else if (format == "XML") {
        modelInt->exportXML(filePath, false);
        r = true;
    } else {
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Unknown export format: %1").arg(format));
        mainWindow->setScriptResult(r);
	return r;
    }
    mainWindow->setScriptResult(r);
    return r;
}

BranchWrapper* VymModelWrapper::findBranchByAttribute(
        const QString &key,
        const QString &value)
{
    BranchItem *bi = modelInt->findBranchByAttribute(key, value);
    if (bi)
        return bi->branchWrapper();
    else
        return nullptr;
}

AttributeWrapper* VymModelWrapper::findAttributeById(const QString &u)
{
    TreeItem *ti = modelInt->findUuid(QUuid(u));
    if (ti && ti->hasTypeAttribute())
        return ((AttributeItem*)ti)->attributeWrapper();
    else
        return nullptr;
}

BranchWrapper* VymModelWrapper::findBranchById(const QString &u)
{
    TreeItem *ti = modelInt->findUuid(QUuid(u));
    if (ti && ti->hasTypeBranch())
        return ((BranchItem*)ti)->branchWrapper();
    else
        return nullptr;
}

BranchWrapper* VymModelWrapper::findBranchBySelection(const QString &s)
{
    TreeItem *ti = modelInt->findBySelectString(s);
    if (ti && ti->hasTypeBranch())
        return ((BranchItem*)ti)->branchWrapper();
    else
        return nullptr;
}

ImageWrapper* VymModelWrapper::findImageById(const QString &u)
{
    TreeItem *ti = modelInt->findUuid(QUuid(u));
    if (ti && ti->hasTypeImage())
        return ((ImageItem*)ti)->imageWrapper();
    else
        return nullptr;
}

ImageWrapper* VymModelWrapper::findImageBySelection(const QString &s)
{
    TreeItem *ti = modelInt->findBySelectString(s);
    if (ti && ti->hasTypeImage())
        return ((ImageItem*)ti)->imageWrapper();
    else
        return nullptr;
}

XLinkWrapper* VymModelWrapper::findXLinkById(const QString &u)
{
    TreeItem *ti = modelInt->findUuid(QUuid(u));
    if (ti && ti->hasTypeXLink())
        return ((XLinkItem*)ti)->getXLink()->xlinkWrapper();
    else
        return nullptr;
}

QString VymModelWrapper::getBackgroundColor()
{
    QString r = modelInt->backgroundColor().name();
    mainWindow->setScriptResult(r);
    return r;
}

QString VymModelWrapper::getBackgroundImageName()
{
    QString r = modelInt->backgroundImageName();
    mainWindow->setScriptResult(r);
    return r;
}

QString VymModelWrapper::getDestPath()
{
    QString r = modelInt->getDestPath();
    mainWindow->setScriptResult(r);
    return r;
}

QString VymModelWrapper::getFileDir()
{
    QString r = modelInt->getFileDir();
    mainWindow->setScriptResult(r);
    return r;
}

QString VymModelWrapper::getFileName()
{
    QString r = modelInt->getFileName();
    mainWindow->setScriptResult(r);
    return r;
}

QString VymModelWrapper::getAuthor()
{
    QString r = modelInt->mapAuthor();
    mainWindow->setScriptResult(r);
    return r;
}

QString VymModelWrapper::getComment()
{
    QString r = modelInt->mapComment();
    mainWindow->setScriptResult(r);
    return r;
}

QString VymModelWrapper::getLinkColorHint()
{
    LinkObj::ColorHint hint = modelInt->mapDesign()->linkColorHint();
    return LinkObj::linkColorHintName(hint);
}

QString VymModelWrapper::getTitle()
{
    QString r = modelInt->mapTitle();
    mainWindow->setScriptResult(r);
    return r;
}

QString VymModelWrapper::getSelectionString()
{
    QString r = modelInt->getSelectString();
    mainWindow->setScriptResult(r);
    return r;
}

double VymModelWrapper::getZoom()
{
    return modelInt->getMapEditor()->zoomFactorTarget();
}

bool VymModelWrapper::hasBackgroundImage()
{
    bool r = modelInt->hasBackgroundImage();
    mainWindow->setScriptResult(r);
    return r;
}

bool VymModelWrapper::loadBackgroundImage(const QString &imagePath)
{
    bool r =modelInt->loadBackgroundImage(imagePath);
    if (!r)
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Failed to load background image \"%1\"").arg(imagePath));
    return r;
}

bool VymModelWrapper::loadBranchReplace(QString fileName, BranchWrapper *bw)
{
    if (QDir::isRelativePath(fileName))
        fileName = QDir::currentPath() + "/" + fileName;

    bool r = modelInt->addMapReplace(fileName, bw->branchItem());
    mainWindow->setScriptResult(r);
    return r;
}

bool VymModelWrapper::loadDataInsert(QString fileName, int pos, BranchWrapper *bw)
{
    if (QDir::isRelativePath(fileName))
        fileName = QDir::currentPath() + "/" + fileName;

    BranchItem * bi = bw ? bw->branchItem() : nullptr;
    bool r = modelInt->addMapInsert(fileName, pos, bi);
    mainWindow->setScriptResult(r);
    return r;
}

ItemListWrapper* VymModelWrapper::itemList()
{
    return new ItemListWrapper(modelInt);
}

void VymModelWrapper::moveSlideDown(int n)
{
    if (!modelInt->moveSlideDown(n))
        mainWindow->abortScript(
                QJSValue::GenericError,
                "Could not move slide down");
}

void VymModelWrapper::moveSlideDown() { moveSlideDown(-1); }

void VymModelWrapper::moveSlideUp(int n)
{
    if (!modelInt->moveSlideUp(n))
        mainWindow->abortScript(
                QJSValue::GenericError,
                "Could not move slide up");
}

void VymModelWrapper::moveSlideUp() { moveSlideUp(-1); }

void VymModelWrapper::paste() { modelInt->paste(); }

void VymModelWrapper::redo() { modelInt->redo(); }

void VymModelWrapper::remove() { modelInt->deleteSelection(); }

void VymModelWrapper::removeAttribute(AttributeWrapper *aw)
{
    if (!aw) {
        mainWindow->abortScript(
                QJSValue::GenericError,
                "VymModelWrapper::removeAttribute(a) a is invalid");
        return;
    }
    modelInt->deleteSelection(aw->attributeItem()->getID());
}

void VymModelWrapper::removeBranch(BranchWrapper *bw)
{
    if (!bw) {
        mainWindow->abortScript(
                QJSValue::GenericError,
                "VymModelWrapper::removeBranch(b) b is invalid");
        return;
    }
    modelInt->deleteSelection(bw->branchItem()->getID());
}

void VymModelWrapper::removeImage(ImageWrapper *iw)
{
    if (!iw) {
        mainWindow->abortScript(
                QJSValue::GenericError,
                "VymModelWrapper::removeImage(i) i is invalid");
        return;
    }
    modelInt->deleteSelection(iw->imageItem()->getID());
}

void VymModelWrapper::removeKeepChildren(BranchWrapper *bw)
{
    modelInt->deleteKeepChildren(bw->branchItem());
}

void VymModelWrapper::removeSlide(int n)
{
    if (n < 0 || n >= modelInt->slideCount() - 1)
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Slide '%1' not available.").arg(n));
}

void VymModelWrapper::removeXLink(XLinkWrapper *xlw)
{
    if (!xlw) {
        mainWindow->abortScript(
                QJSValue::GenericError,
                "VymModelWrapper::removeXLink(xl) xlink is invalid");
        return;
    }
    modelInt->deleteXLink(xlw->xlink());
}

bool VymModelWrapper::saveSelection(const QString &filename)
{
    QString filename_org = modelInt->getFilePath(); // Restore filename later
    if (!modelInt->renameMap(filename)) {
        QString s = tr("Saving the selection in map failed:\nCouldn't rename map to %1").arg(filename);
        QMessageBox::critical(0,
            tr("Critical Error"), s);
        mainWindow->abortScript(QJSValue::GenericError, s);
        return false;
    }

    bool r = modelInt->saveMap(File::PartOfMap);

    if (!modelInt->renameMap(filename_org)) {
        QString s = tr("Saving the selection in map failed:\nCouldn't rename map to %1").arg(filename);
        QMessageBox::critical(0,
            tr("Critical Error"), s);
        mainWindow->abortScript(QJSValue::GenericError, s);
        return false;
    }

    return r;
}

bool VymModelWrapper::select(const QString &s)
{
    bool r = modelInt->select(s);
    if (!r)
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Couldn't select %1").arg(s));
    mainWindow->setScriptResult(r);
    return r;
}

AttributeWrapper* VymModelWrapper::selectedAttribute()
{
    AttributeItem *ai = modelInt->getSelectedAttribute();

    if (ai)
        return ai->attributeWrapper();
    else
        return nullptr;
}

BranchWrapper* VymModelWrapper::selectedBranch()
{
    BranchItem *selbi = modelInt->getSelectedBranch();

    if (selbi)
        return selbi->branchWrapper();
    else
        return nullptr; // caught by QJSEngine
}

QList <BranchWrapper*> VymModelWrapper::selectedBranches()
{
    QList <BranchItem*> selbis = modelInt->getSelectedBranches();

    QList <BranchWrapper*> rlist;
    foreach (BranchItem *selbi, selbis)
        rlist << selbi->branchWrapper();

    return rlist;
}

XLinkWrapper* VymModelWrapper::selectedXLink()
{
    XLinkItem *xli = modelInt->getSelectedXLinkItem();

    if (xli)
        return xli->getXLink()->xlinkWrapper();
    else
        return nullptr;
}

bool VymModelWrapper::selectUids(QJSValueList args)
{
    int argumentsCount = args.count();

    bool r = false;
    if (argumentsCount == 0) {
        mainWindow->abortScript(
                QJSValue::GenericError,
                "Not enough arguments");
	mainWindow->setScriptResult(r);
	return r;
    }

    QStringList uids;
    foreach (auto arg, args)
        uids << arg.toString();

    r = modelInt->selectUids(uids);
    if (!r)
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Couldn't select Uuids: %1").arg(uids.join(",")));
    mainWindow->setScriptResult(r);
    return r;
}

bool VymModelWrapper::selectLatestAdded()
{
    bool r = modelInt->selectLatestAdded();
    if (!r)
        mainWindow->abortScript(
                QJSValue::GenericError,
                "Couldn't select latest added item");
    mainWindow->setScriptResult(r);
    return r;
}

void VymModelWrapper::setAnimCurve(int n)
{
    if (n < 0 || n > QEasingCurve::OutInBounce)
        mainWindow->abortScript(
                QJSValue::RangeError,
                QString("Unknown animation curve type: ").arg(n));
    else {
        QEasingCurve c;
        c.setType((QEasingCurve::Type)n);
        modelInt->setMapAnimCurve(c);
    }
}

void VymModelWrapper::setAnimDuration(int n)
{
    modelInt->setMapAnimDuration(n);
}

void VymModelWrapper::setAuthor(const QString &s) { modelInt->setMapAuthor(s); }

void VymModelWrapper::setBackgroundColor(const QString &color)
{
    QColor col(color);
    if (col.isValid()) {
        modelInt->setBackgroundColor(col);
    }
    else
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Could not set color to %1").arg(color));
}

void VymModelWrapper::setBackgroundImageName(const QString &name)
{
    modelInt->setBackgroundImageName(name);
}

void VymModelWrapper::setDefaultLinkColor(const QString &color)
{
    QColor col(color);
    if (col.isValid()) {
        modelInt->setDefaultLinkColor(col);
    }
    else
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Could not set color to %1").arg(color));
}

void VymModelWrapper::setComment(const QString &s) { modelInt->setMapComment(s); }

void VymModelWrapper::setLinkColorHint(const QString &hintName)
{
    LinkObj::ColorHint hint = LinkObj::linkColorHint(hintName);
    modelInt->setLinkColorHint(hint);
}

void VymModelWrapper::setLinkStyle(const QString &style, int depth)
{
    if (!modelInt->setLinkStyle(style, depth))
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Could not set linkstyle to %1 with d=%2").arg(style, depth));
}

void VymModelWrapper::setRotationView(float a) { modelInt->setMapRotation(a); }

void VymModelWrapper::setTitle(const QString &s) { modelInt->setMapTitle(s); }

void VymModelWrapper::setZoom(float z) { modelInt->setMapZoomFactor(z); }

void VymModelWrapper::setSelectionBrushColor(const QString &color)
{
    QColor col(color);
    if (!col.isValid())
        //logErrorOld(context(), QScriptContext::SyntaxError,
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Could not set color to %1").arg(color));
    else
        modelInt->setSelectionBrushColor(col);
}

void VymModelWrapper::setSelectionPenColor(const QString &color)
{
    QColor col(color);
    if (!col.isValid())
        mainWindow->abortScript(
                QJSValue::GenericError,
                QString("Could not set color to %1").arg(color));
    else
        modelInt->setSelectionPenColor(col);
}

void VymModelWrapper::setSelectionPenWidth(const qreal &w)
{
    modelInt->setSelectionPenWidth(w);
}

void VymModelWrapper::sleep(int n)
{
    // FIXME-5 sleep is not avail on windows VCEE, workaround could be using
    // this->thread()->wait(x ms)
    sleep(n);
}

int VymModelWrapper::slideCount()
{
    int r = modelInt->slideCount();
    mainWindow->setScriptResult(r);
    return r;
}

void VymModelWrapper::undo() { modelInt->undo(); }

void VymModelWrapper::unselectAll() { modelInt->unselectAll(); }

void VymModelWrapper::unsetBackgroundImage()
{
    modelInt->unsetBackgroundImage();
}
