#include <QStringList>
#include <iostream>

#include "attributeitem.h"
#include "branchitem.h"
#include "branchobj.h"
#include "misc.h"
#include "treeitem.h"
#include "vymmodel.h"
#include "xlinkitem.h"
#include "xlinkobj.h"

using namespace std;

extern ulong itemLastID;
extern FlagRowMaster *standardFlagsMaster;
extern FlagRowMaster *systemFlagsMaster;
extern FlagRowMaster *userFlagsMaster;

extern QTextStream vout;

TreeItem::TreeItem(TreeItem *parent)
{
    // qDebug() << "Constructor TreeItem this=" << this << "  parent=" << parent;
    init();
    parentItem = parent;

    rootItem = this;
    if (parentItem)
        rootItem = parentItem->rootItem;
}

TreeItem::~TreeItem()
{
    // qDebug()<<"Destr TreeItem this="<<this<<"
    // childcount="<<childItems.count();
    TreeItem *ti;
    while (!childItems.isEmpty()) {
        ti = childItems.takeFirst();
        delete ti;
    }
}

void TreeItem::init()
{
    model = NULL;

    // Assign ID
    itemLastID++;
    itemID = itemLastID;
    uuid = QUuid::createUuid();

    branchOffset = 0;
    branchCounter = 0;

    imageOffset = 0;
    imageCounter = 0;

    attributeCounter = 0;
    attributeOffset = 0;

    xlinkCounter = 0;
    xlinkOffset = 0;

    target = false;

    heading.clear();
    heading.setText(" ");
    note.setText("");

    hidden = false;
    hideExport = false;

    itemData.clear();
    itemData << "";

    backgroundColor = Qt::transparent;

    standardFlags.setMasterRow(standardFlagsMaster);
    userFlags.setMasterRow(userFlagsMaster);
    systemFlags.setMasterRow(systemFlagsMaster);
}

void TreeItem::setModel(VymModel *m) { model = m; }

VymModel *TreeItem::getModel() { return model; }

int TreeItem::getRowNumAppend(TreeItem *item)
{
    switch (item->type) {
    case Attribute:
        return attributeOffset + attributeCounter;
    case XLink:
        return xlinkOffset + xlinkCounter;
    case Image:
        return imageOffset + imageCounter;
    case MapCenter:
        return branchOffset + branchCounter;
    case Branch:
        return branchOffset + branchCounter;
    default:
        return -1;
    }
}

void TreeItem::appendChild(TreeItem *item)
{
    item->parentItem = this;
    item->rootItem = rootItem;
    item->setModel(model);

    if (item->type == Attribute) {
        // attribute are on top of list
        childItems.insert(attributeCounter, item);
        attributeCounter++;
        xlinkOffset++;
        imageOffset++;
        branchOffset++;
    }

    if (item->type == XLink) {
        childItems.insert(xlinkCounter + xlinkOffset, item);
        xlinkCounter++;
        imageOffset++;
        branchOffset++;
    }

    if (item->type == Image) {
        childItems.insert(imageCounter + imageOffset, item);
        imageCounter++;
        branchOffset++;
    }

    if (item->isBranchLikeType()) {
        // branches are on bottom of list
        childItems.append(item);
        branchCounter++;

        // Set correct type
        if (this == rootItem)
            item->setType(MapCenter);
        else
            item->setType(Branch);
    }
}

void TreeItem::removeChild(int row)
{
    if (row < 0 || row > childItems.size() - 1)
        qWarning("TreeItem::removeChild tried to remove non existing item?!");
    else {
        if (childItems.at(row)->type == Attribute) {
            attributeCounter--;
            xlinkOffset--;
            imageOffset--;
            branchOffset--;
        }
        if (childItems.at(row)->type == XLink) {
            xlinkCounter--;
            imageOffset--;
            branchOffset--;
        }
        if (childItems.at(row)->type == Image) {
            imageCounter--;
            branchOffset--;
        }
        if (childItems.at(row)->isBranchLikeType())
            branchCounter--;

        childItems.removeAt(row);
    }
}

TreeItem *TreeItem::child(int row) { return childItems.value(row); }

int TreeItem::childCount() const { return childItems.count(); }

int TreeItem::childNumber() const
{
    if (parentItem)
        return parentItem->childItems.indexOf(const_cast<TreeItem *>(this));

    return 0;
}

int TreeItem::columnCount() const { return 1; }

int TreeItem::branchCount() const { return branchCounter; }

int TreeItem::imageCount() const { return imageCounter; }

int TreeItem::xlinkCount() const { return xlinkCounter; }

int TreeItem::attributeCount() const { return attributeCounter; }

int TreeItem::row() const
{
    if (parentItem)
        return parentItem->childItems.indexOf(const_cast<TreeItem *>(this));

    qDebug() << "TI::row() pI=NULL this=" << this << "  ***************";
    return 0;
}

int TreeItem::depth()
{
    // Rootitem d=-1
    // MapCenter d=0
    int d = -2;
    TreeItem *ti = this;
    while (ti != NULL) {
        ti = ti->parent();
        d++;
    }
    return d;
}

TreeItem *TreeItem::parent()
{
    // qDebug() << "TI::parent of "<<getHeadingStd()<<"  is "<<parentItem;
    return parentItem;
}

bool TreeItem::isChildOf(TreeItem *ti)
{
    if (this == rootItem)
        return false;
    if (parentItem == ti)
        return true;
    if (parentItem == rootItem)
        return false;
    return parentItem->isChildOf(ti);
}

int TreeItem::childNum() { return parentItem->childItems.indexOf(this); }

int TreeItem::num()
{
    if (!parentItem)
        return -1;
    return parentItem->num(this);
}

int TreeItem::num(TreeItem *item)
{
    if (!item)
        return -1;
    if (!childItems.contains(item))
        return -1;
    switch (item->getType()) {
    case MapCenter:
        return childItems.indexOf(item) - branchOffset;
    case Branch:
        return childItems.indexOf(item) - branchOffset;
    case Image:
        return childItems.indexOf(item) - imageOffset;
    case Attribute:
        return childItems.indexOf(item) - attributeOffset;
    case XLink:
        return childItems.indexOf(item) - xlinkOffset;
    default:
        return -1;
    }
}
void TreeItem::setType(const Type t)
{
    type = t;
}

TreeItem::Type TreeItem::getType()
{
    if (type == Branch && depth() == 0)
        return MapCenter; // should not be necesssary
    return type;
}

bool TreeItem::isBranchLikeType() const
{
    if (type == Branch || type == MapCenter)
        return true;
    else
        return false;
}

QString TreeItem::getTypeName()
{
    switch (type) {
        case Undefined:
            return QString("Undefined");
        case MapCenter:
            return QString("MapCenter");
        case Branch:
            return QString("Branch");
        case Image:
            return QString("Image");
        case Attribute:
            return QString("Attribute");
        case XLink:
            return QString("XLink");
        default:
            return QString("TreeItem::getTypeName no typename defined?!");
    }
}

QVariant TreeItem::data(int column) const { return itemData.value(column); }

void TreeItem::setHeading(const VymText &vt)
{
    heading = vt;
    itemData[0] = getHeadingPlain().replace("\n"," "); // used in TreeEditor
}

void TreeItem::setHeadingPlainText(const QString &s)
{
    VymText vt;

    vt.setPlainText(s);

    if (!heading.isRichText())
        // Keep current color
        vt.setColor(heading.getColor());
    setHeading(vt);
}

Heading TreeItem::getHeading() const { return heading; }

QString TreeItem::getHeadingText() { return heading.getText(); }

std::string TreeItem::getHeadingStd() const
{
    return getHeadingPlain().toStdString();
}

QString TreeItem::getHeadingPlain() const
{
    // strip beginning and tailing WS
    return heading.getTextASCII().trimmed();
}

QString TreeItem::getHeadingPlainWithParents(uint numberOfParents = 0)
{
    QString s = getHeadingPlain();
    if (numberOfParents > 0) {
        TreeItem *ti = this;
        int l = numberOfParents;
        while (l > 0 && ti->depth() > 0) {
            ti = ti->parent();
            if (ti)
                s = ti->getHeadingPlain() + " -> " + s;
            else
                l = 0;
            l--;
        }
    }
    return s;
}

QString TreeItem::getHeadingDepth() // Indent by depth for debugging
{
    QString ds;
    for (int i = 0; i < depth(); i++)
        ds += "  ";
    return ds + getHeadingPlain();
}

void TreeItem::setHeadingColor(QColor color) { heading.setColor(color); }

QColor TreeItem::getHeadingColor() { return heading.getColor(); }

void TreeItem::setBackgroundColor(QColor color) { backgroundColor = color; }

void TreeItem::setURL(const QString &u)
{
    url = u;
    if (!url.isEmpty())
        systemFlags.activate(QString("system-url"));
    else
        systemFlags.deactivate(QString("system-url"));
}

QString TreeItem::getURL() { return url; }

void TreeItem::setVymLink(const QString &vl)
{
    if (!vl.isEmpty()) {
        // We need the relative (from loading)
        // or absolute path (from User event)
        // and build the absolute path.

        QDir d(vl);
        if (d.isAbsolute())
            vymLink = vl;
        else {
            // If we have relative, use path of
            // current map to build absolute path
            // based on path of current map and relative
            // path to linked map
            QString p = dirname(model->getDestPath());
            vymLink = convertToAbs(p, vl);
        }
        systemFlags.activate(QString("system-vymLink"));
    }
    else {
        vymLink.clear();
        systemFlags.deactivate(QString("system-vymLink"));
    }
}

QString TreeItem::getVymLink() { return vymLink; }

void TreeItem::toggleTarget()
{
    systemFlags.toggle(QString("system-target"));
    target = systemFlags.isActive(QString("system-target"));
    model->emitDataChanged(this); // FIXME-4 better call from VM?
}

bool TreeItem::isTarget() { return target; }

bool TreeItem::isNoteEmpty() { return note.isEmpty(); }

bool TreeItem::clearNote()
{
    note.clear();
    return systemFlags.deactivate(QString("system-note"));
}

bool TreeItem::setNote(const VymText &vt)
{
    note = vt;

    if (note.isEmpty()) {
        if (systemFlags.isActive(QString("system-note")))
            return systemFlags.deactivate(QString("system-note"));
    }
    else {
        if (!systemFlags.isActive(QString("system-note")))
            return systemFlags.activate(QString("system-note"));
    }
    return false; // No need to update flag and reposition later
}

bool TreeItem::setNote(const VymNote &vn) { return setNote((VymText)vn); }

bool TreeItem::hasEmptyNote() { return note.isEmpty(); }

VymNote TreeItem::getNote() { return note; }

QString TreeItem::getNoteASCII(const QString &indent, const int &width)
{
    return note.getTextASCII(indent, width);
}

QString TreeItem::getNoteASCII() { return note.getTextASCII(); }

void TreeItem::activateStandardFlagByName(const QString &name)
{
    standardFlags.activate(name);
    //    model->emitDataChanged(this);
}

void TreeItem::deactivateStandardFlagByName(const QString &name)
{
    standardFlags.deactivate(name);
    //    model->emitDataChanged(this);
}

void TreeItem::deactivateAllStandardFlags()
{
    standardFlags.deactivateAll();
    userFlags.deactivateAll();
    //    model->emitDataChanged(this);
}

Flag *TreeItem::findFlagByUid(const QUuid &uid)
{
    Flag *f = standardFlagsMaster->findFlagByUid(uid);
    if (!f)
        f = userFlagsMaster->findFlagByUid(uid);
    return f;
}

Flag *TreeItem::toggleFlagByUid(const QUuid &uid, bool useGroups)
{
    Flag *f = standardFlagsMaster->findFlagByUid(uid);
    if (f) {
        standardFlags.toggle(uid, useGroups);
    }
    else {
        f = userFlagsMaster->findFlagByUid(uid);
        if (f) {
            userFlags.toggle(uid, useGroups);
        }
        else {
            qWarning() << "TI::toggleFlag failed for flag " << uid;
            return nullptr;
        }
    }

    return f;
}

void TreeItem::toggleSystemFlag(const QString &name, FlagRow *master)
{
    systemFlags.toggle(name, master);
    model->emitDataChanged(this);
}

bool TreeItem::hasActiveFlag(const QString &name)
{
    return standardFlags.isActive(name);
}

bool TreeItem::hasActiveSystemFlag(const QString &name)
{
    return systemFlags.isActive(name);
}

QList<QUuid> TreeItem::activeFlagUids()
{
    return standardFlags.activeFlagUids() + userFlags.activeFlagUids();
}

QList<QUuid> TreeItem::activeSystemFlagUids()
{
    return systemFlags.activeFlagUids();
}

bool TreeItem::canMoveDown()
{
    switch (type) {
    case Undefined:
        return false;
    case MapCenter:
    case Branch:
        if (!parentItem)
            return false;
        if (parentItem->num(this) < parentItem->branchCount() - 1)
            return true;
        else
            return false;
        break;
    case Image:
        return false;
    default:
        return false;
    }
}

bool TreeItem::canMoveUp()
{
    switch (type) {
    case MapCenter:
    case Branch:
        if (!parentItem)
            return false;
        if (parentItem->num(this) > 0)
            return true;
        else
            return false;
        break;
    default:
        return false;
    }
}

ulong TreeItem::getID() { return itemID; }

void TreeItem::setUuid(const QString &id) { uuid = QUuid(id); }

QUuid TreeItem::getUuid() { return uuid; }

TreeItem *TreeItem::getChildNum(const int &n)
{
    if (n >= 0 && n < childItems.count())
        return childItems.at(n);
    else
        return NULL;
}

BranchItem *TreeItem::getFirstBranch()
{
    if (branchCounter > 0)
        return getBranchNum(0);
    else
        return NULL;
}

BranchItem *TreeItem::getLastBranch()
{
    if (branchCounter > 0)
        return getBranchNum(branchCounter - 1);
    else
        return NULL;
}

ImageItem *TreeItem::getFirstImage()
{
    if (imageCounter > 0)
        return getImageNum(imageCounter - 1);
    else
        return NULL;
}

ImageItem *TreeItem::getLastImage()
{
    if (imageCounter > 0)
        return getImageNum(imageCounter - 1);
    else
        return NULL;
}

BranchItem *TreeItem::getNextBranch(BranchItem *currentBranch)
{
    if (!currentBranch)
        return NULL;
    int n = num(currentBranch) + 1;
    if (n < branchCounter)
        return getBranchNum(branchOffset + n);
    else
        return NULL;
}

BranchItem *TreeItem::getBranchNum(const int &n)
{
    if (n >= 0 && n < branchCounter)
        return (BranchItem *)getChildNum(branchOffset + n);
    else
        return NULL;
}

BranchObj *TreeItem::getBranchObjNum(const int &n)
{
    if (n >= 0 && n < branchCounter) {
        BranchItem *bi = getBranchNum(n);
        if (bi) {
            BranchObj *bo = (BranchObj *)(bi->getLMO());
            if (bo)
                return bo;
            else
                qDebug() << "TI::getBONum bo=NULL";
        }
    }
    return NULL;
}

ImageItem *TreeItem::getImageNum(const int &n)
{
    if (n >= 0 && n < imageCounter)
        return (ImageItem *)getChildNum(imageOffset + n);
    else
        return NULL;
}

FloatImageObj *TreeItem::getImageObjNum(const int &n)
{
    if (imageCounter > 0)
        return (FloatImageObj *)(getImageNum(n)->getLMO());
    else
        return NULL;
}

AttributeItem *TreeItem::getAttributeNum(const int &n)
{
    if (n >= 0 && n < attributeCounter)
        return (AttributeItem *)getChildNum(attributeOffset + n);
    else
        return NULL;
}

AttributeItem *TreeItem::getAttributeByKey(const QString &k)
{
    AttributeItem *ai;
    for (int i = 0; i < attributeCount(); i++) {
        ai = getAttributeNum(i);
        if (ai->getKey() == k) return ai;
    }
    return nullptr;
}

XLinkItem *TreeItem::getXLinkItemNum(const int &n)
{
    if (n >= 0 && n < xlinkCounter)
        return (XLinkItem *)getChildNum(xlinkOffset + n);
    else
        return NULL;
}

XLinkObj *TreeItem::getXLinkObjNum(const int &n)
{
    if (xlinkCounter > 0) {
        XLinkItem *xli = getXLinkItemNum(n);
        if (xli) {
            Link *l = xli->getLink();
            if (l)
                return l->getXLinkObj();
        }
    }
    return NULL;
}

void TreeItem::setHideTmp(HideTmpMode mode)
{
    if (type == Image || type == Branch || type == MapCenter)
    //	((ImageItem*)this)->updateVisibility();
    {
        // LinkableMapObj* lmo=((MapItem*)this)->getLMO();

        if (mode == HideExport &&
            (hideExport ||
             hasHiddenExportParent())) // FIXME-4  try to avoid calling
                                       // hasScrolledParent repeatedly

            // Hide stuff according to hideExport flag and parents
            hidden = true;
        else
            // Do not hide, but still take care of scrolled status
            hidden = false;
        updateVisibility();
        // And take care of my children
        for (int i = 0; i < branchCount(); ++i)
            getBranchNum(i)->setHideTmp(mode);
    }
}

bool TreeItem::hasHiddenExportParent()
{
    // Calls parents recursivly to
    // find out, if we or parents are temp. hidden

    if (hidden || hideExport)
        return true;

    if (parentItem)
        return parentItem->hasHiddenExportParent();
    else
        return false;
}

void TreeItem::setHideInExport(bool b)
{
    if (type == MapCenter || type == Branch || type == Image) {
        hideExport = b;
        if (b)
            systemFlags.activate(QString("system-hideInExport"));
        else
            systemFlags.deactivate(QString("system-hideInExport"));
    }
}

bool TreeItem::hideInExport() { return hideExport; }

void TreeItem::updateVisibility()
{
    // overloaded in derived objects
}

bool TreeItem::isHidden() { return hidden; }

QString TreeItem::getGeneralAttr()
{
    QString s;
    if (hideExport)
        s += attribut("hideInExport", "true");
    if (!url.isEmpty())
        s += attribut("url", url);
    if (!vymLink.isEmpty())
        s += attribut("vymLink", convertToRel(model->getDestPath(), vymLink));

    if (target)
        s += attribut("localTarget", "true");
    return s;
}
