#include <iostream>
#include <QStringList>

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

using namespace std;

extern uint itemLastID;
extern FlagRow* standardFlagsMaster;
extern FlagRow* systemFlagsMaster;

extern QTextStream vout;

TreeItem::TreeItem()
{
    //qDebug() << "Constr. TI  this="<<this;
    init();
    itemData.clear();
    rootItem=this;
    parentItem=NULL;
}

TreeItem::TreeItem(const QList<QVariant> &data, TreeItem *parent)
{
    //qDebug() << "Constructor TreeItem this="<<this<<"  parent="<<parent;
    init();
    parentItem = parent;
    itemData = data;
    
    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++;
    id = 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;

    backgroundColor = Qt::transparent;

    standardFlags.setMasterRow (standardFlagsMaster);
    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;
    itemData[1]=getTypeName();
}

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;
    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]= heading.getTextASCII();  // 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;
}

QColor TreeItem::getBackgroundColor() 
{
    return backgroundColor;
}

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

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("system-vymLink");
    }
    else
    {
        vymLink.clear();
        systemFlags.deactivate("system-vymLink");
    }
}

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

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

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

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

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

void TreeItem::setNote(const VymText &vt)
{
    note = vt;
    if (!note.isEmpty() && !systemFlags.isActive ("system-note"))
	systemFlags.activate ("system-note");
    if (note.isEmpty() && systemFlags.isActive ("system-note"))
	systemFlags.deactivate ("system-note");
}

void TreeItem::setNote(const VymNote &vn)
{
    note = vn;
    if (!note.isEmpty() && !systemFlags.isActive ("system-note"))
    systemFlags.activate ("system-note");
    if (note.isEmpty() && systemFlags.isActive ("system-note"))
    systemFlags.deactivate ("system-note");
}

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::activateStandardFlag (const QString &name)
{
    standardFlags.activate (name);
    model->emitDataChanged(this);
}

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

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

void TreeItem::toggleStandardFlag(const QString &name, FlagRow *master)
{
    standardFlags.toggle (name,master);
}

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

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

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

QStringList TreeItem::activeStandardFlagNames ()
{
    return standardFlags.activeFlagNames();
}

FlagRow* TreeItem::getStandardFlagRow()
{
    return &standardFlags;
}

QStringList TreeItem::activeSystemFlagNames ()
{
    return systemFlags.activeFlagNames();
}

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;
    }
}

uint TreeItem::getID()
{
    return id;
}

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;
}

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("system-hideInExport");
	else	
	    systemFlags.deactivate("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;	
}


