//============================================================================
// Copyright 2009- ECMWF.
// This software is licensed under the terms of the Apache Licence version 2.0
// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
// In applying this licence, ECMWF does not waive the privileges and immunities
// granted to it by virtue of its status as an intergovernmental organisation
// nor does it submit to any jurisdiction.
//
//============================================================================

#include "VNode.hpp"

#include "Suite.hpp"
#include "Expression.hpp"
#include "Limit.hpp"
#include "Variable.hpp"

#include "AstCollateVNodesVisitor.hpp"
#include "ConnectState.hpp"
#include "ServerDefsAccess.hpp"
#include "ServerHandler.hpp"
#include "TriggerCollector.hpp"
#include "TriggeredScanner.hpp"
#include "VAttribute.hpp"
#include "VAttributeType.hpp"
#include "VDateAttr.hpp"
#include "VEventAttr.hpp"
#include "VFileInfo.hpp"
#include "VFilter.hpp"
#include "VLabelAttr.hpp"
#include "VLateAttr.hpp"
#include "VLimitAttr.hpp"
#include "VLimiterAttr.hpp"
#include "VMeterAttr.hpp"
#include "VRepeatAttr.hpp"
#include "VNState.hpp"
#include "VSState.hpp"
#include "VTaskNode.hpp"
#include "VTimeAttr.hpp"
#include "VTriggerAttr.hpp"
#include "VGenVarAttr.hpp"
#include "VUserVarAttr.hpp"
#include "UiLog.hpp"

#include <boost/algorithm/string.hpp>

#define _UI_VNODE_DEBUG

//For a given node this class stores all the nodes that this node itself triggers.
//For memory efficiency we only store the AttributeFilterindex of the nodes not the pointers themselves.
class VNodeTriggerData
{
public:
    std::vector<int> data_;   
    std::map<std::string, std::vector<int> > eventData_;

    //std::vector<int> attr_;

    void get(VNode* node ,TriggerCollector* tc)
    {
        VServer* s=node->root();
        std::size_t num=data_.size();
        for(std::size_t i=0; i < num; i++)
        {
            VItem* triggered=s->nodeAt(data_[i]);
            tc->add(triggered,nullptr,TriggerCollector::Normal);
        }
    }

    void getEvent(VNode* node ,const std::string& eventName,std::vector<std::string> &res)
    {
        std::map<std::string,std::vector<int> >::const_iterator it=eventData_.find(eventName);
        if(it != eventData_.end())
        {
            VServer* s=node->root();
            for(size_t i=0; i < it->second.size(); i++)
            {
                if(VNode* triggeredNode=s->nodeAt((it->second)[i]))
                    res.push_back(triggeredNode->absNodePath());
            }
        }
    }

    void add(VItem* triggered)
    {
        assert(triggered);
        VNode* triggeredNode=triggered->isNode();
        assert(triggeredNode);
        data_.push_back(triggeredNode->index());
    }

    void add(VItem* triggered,VAttribute* trigger)
    {
        static VAttributeType *eventType=nullptr;
        if(!eventType)
            eventType=VAttributeType::find("event");

        assert(trigger);
        assert(triggered);
        VNode* triggeredNode=triggered->isNode();
        assert(triggeredNode);

        //We only store the events
        if(trigger->type() == eventType)
        {
            eventData_[trigger->strName()].push_back(triggeredNode->index());
        }
    }
};

#if 0
class VNodeTriggerData
{
public:
    std::vector<std::pair<int,int> > data_;

    void get(VNode* node ,TriggerCollector* tc)
    {
        VServer* s=node->root();

        for(size_t i=0; i < data_.size(); i++)
        {
            VItemTmp_ptr triggered(VItemTmp::create(s->nodeAt(data_[i].first)));
            VItemTmp_ptr trigger;
            if(data_[i].second == -2)
            {
                trigger=VItemTmp::creaate(node);
            else
            {
                trigger=VItemTmp::create(VAttribute::makeFromId(node,data_[i].second));
            }

            tc->add(triggered,0,TriggerCollector::Normal,trigger);
            VItemTmp_ptr nullItem;
            tc->add(triggered,nullItem,TriggerCollector::Normal);
        }
    }
AttributeFilter
    void add(VItem* n)
    {
        assert(n);
        VNode* node=n->isNode();
        assert(node);
        data_.push_back(std::make_pair(node->index(),-2));
    }

    void add(VItem* triggered,VItem* trigger)
    {
        assert(triggered);
        assert(trigger);
        VNode* node=triggered->isNode();
        assert(node);
        VAttribute* attr=trigger->isAttribute();
        assert(attr);
        if(attr->id() >=0)
            data_.push_back(std::make_pair(node->index(),attr->id()));
    }
};
#endif

//=================================================
// VNode
//=================================================

VNode::VNode(VNode* parent,node_ptr node) :
    VItem(parent),
    node_(node),
    //parent_(parent),
#if 0
    attrNum_(-1),
    cachedAttrNum_(-1),
#endif
    index_(-1),
    data_(nullptr)
{
	if(parent_)
		parent_->addChild(this);

	if(node_)
		node_->set_graphic_ptr(this);

    // do not scan for attributes in a server
    if (parent_)
        scanAttr();
}

VNode::~VNode()
{
    if(data_)
        delete data_;

    for(auto & i : attr_)
        delete i;
}

VServer* VNode::root() const
{
    return server()->vRoot();
}

ServerHandler* VNode::server() const
{
	return (parent_)?(parent_->server()):nullptr;
}

VNode* VNode::suite() const
{
    if(isTopLevel())
        return const_cast<VNode*>(this);

    VNode* p=parent();
    while(p)
    {
        if(p->isTopLevel())
            return p;
        p=p->parent();
    }

    assert(0);

    return nullptr;
}

const ecf::Calendar& VNode::calendar() const
{
    if(VNode* sn = suite())
    {
        if(Suite *s = sn->node()->isSuite())
        {
            return s->calendar();
        }
    }

    static ecf::Calendar emptyCal;
    return emptyCal;
}


bool VNode::isTopLevel() const
{
    return isSuite();
    //return (parent_ && parent_->isServer());
	//return (node_)?(node_->isSuite() != NULL):false;
}

void VNode::clear()
{
	children_.clear();
#if 0
    attrNum_=-1,
	cachedAttrNum_=-1;
#endif
}

bool VNode::hasAccessed() const
{
	return true; //!name_.empty();
}

//------------------------
// Attributes
//------------------------

void VNode::scanAttr()
{  
    std::vector<VAttribute*> v;
    VAttributeType::scan(this,v);
    std::size_t n=v.size();
    attr_.reserve(n);
    for(std::size_t i=0; i < n; i++)
        attr_.push_back(v[i]);
}

void VNode::rescanAttr()
{
    for(auto & i : attr_)
        delete i;

    attr_=std::vector<VAttribute*>();
    scanAttr();
}

int VNode::attrNum(AttributeFilter *filter) const
{     
    if(filter)
    {
        int n=0;
        for(auto i : attr_)
        {
            if(filter->isSet(i->type()) || filter->forceShowAttr() == i)
                n++;
        }
        return n;
    }

    return attr_.size();
}

VAttribute* VNode::attribute(int row,AttributeFilter *filter) const
{
    assert(row>=0);

    if(filter)
    {
        int n=0;
        auto cnt=static_cast<int>(attr_.size());
        if(row >= cnt)
            return nullptr;

        for(int i=0; i < cnt; i++)
        {
            if(filter->isSet(attr_[i]->type()) || filter->forceShowAttr() == attr_[i] )
            {
                if(n == row)
                {
                    return attr_[i];
                }
                n++;               
            }
        }
    }
    else if(row < static_cast<int>(attr_.size()))
    {
        return attr_[row];
    }

    return nullptr;
}

VAttribute* VNode::attributeForType(int row,VAttributeType *t) const
{
    assert(row>=0);

    auto cnt=static_cast<int>(attr_.size());
    if(row >= cnt)
        return nullptr;

    bool hasIt=false;
    int rowCnt=0;
    for(int i=0; i < cnt; i++)
    {
        if(attr_[i]->type() == t)
        {
            if(rowCnt == row)
            {
                return attr_[i];
            }
            rowCnt++;
            hasIt=true;
        }
        else if(hasIt)
        {
            return nullptr;
        }
    }

    return nullptr;
}

int VNode::indexOfAttribute(const VAttribute* a, AttributeFilter *filter) const
{
    if(filter)
    {
        int n=0;
        auto cnt=static_cast<int>(attr_.size());
        for(int i=0; i < cnt; i++)
        {
            if(filter->isSet(attr_[i]->type())  || filter->forceShowAttr() == attr_[i]  )
            {
                if(a==attr_[i])
                    return n;

                n++;
            }
        }
    }
    else
    {        
        auto cnt=static_cast<int>(attr_.size());
        for(int i=0; i < cnt; i++)
        {
            if(a == attr_[i])
                return i;
        }
    }

    return -1;
}

VAttribute* VNode::findAttribute(const std::string& typeName,const std::string& name)
{
    VAttributeType *t=VAttributeType::find(typeName);
    Q_ASSERT(t);
    return (t)?findAttribute(t,name):nullptr;
}

VAttribute* VNode::findAttribute(VAttributeType *t,const std::string& name)
{
    Q_ASSERT(t);

    auto cnt=static_cast<int>(attr_.size());
    bool hasType=false;
    for(int i=0; i < cnt; i++)
    {
        if(attr_[i]->type() == t)
        {
            if(attr_[i]->strName() == name)
                return attr_[i];
            hasType=true;
        }
        else if(hasType)
            return nullptr;
    }
    return nullptr;
}


VAttribute* VNode::findAttribute(QStringList aData)
{
    std::size_t cnt=attr_.size();
    for(std::size_t i=0; i < cnt; i++)
    {
        if(attr_[i]->sameAs(aData))
            return attr_[i];
    }
    return nullptr;
}

void VNode::findAttributes(VAttributeType *t,std::vector<VAttribute*>& v)
{
    std::size_t cnt=attr_.size();
    bool hasType=false;
    for(std::size_t i=0; i < cnt; i++)
    {
        if(attr_[i]->type() == t)
        {
            v.push_back(attr_[i]);
            hasType=true;
        }
        else if(hasType)
            return;
    }
}

void VNode::addChild(VNode* vn)
{
	children_.push_back(vn);
}

void VNode::removeChild(VNode* vn)
{
	auto it=std::find(children_.begin(),children_.end(),vn);
	if(it != children_.end())
	{
		children_.erase(it);
	}
}

VNode* VNode::childAt(int index) const
{
    assert(index>=0 && index < static_cast<int>(children_.size()));
    return children_[index];
}

int VNode::indexOfChild(const VNode* vn) const
{
	for(unsigned int i=0; i < children_.size(); i++)
	{
        if(children_[i] == vn)
			return i;
	}

	return -1;
}

int VNode::indexOfChild(node_ptr n) const
{
	for(unsigned int i=0; i < children_.size(); i++)
	{
        if(children_[i]->node() == n)
			return i;
    }

	return -1;
}

VNode *VNode::findChild(const std::string& name) const
{
	for(auto i : children_)
	{
        if(i->sameName(name))
			return i;
	}
	return nullptr;
}

void VNode::collect(std::vector<VNode*>& vec) const
{
	for(int i=0; i < numOfChildren(); i++)
	{
		vec.push_back(children_.at(i));
        children_[i]->collect(vec);
	}
}

void VNode::collectAbortedTasks(std::vector<VNode*>& vec) const
{
    for(int i=0; i < numOfChildren(); i++)
    {
        if(children_[i]->isTask() && children_[i]->isAborted())
        {
            vec.push_back(children_[i]);
        }
        children_[i]->collectAbortedTasks(vec);
    }
}

int VNode::tryNo() const
{
	std::string v=genVariable("ECF_TRYNO");
	if(v.empty())
		return 0;

	return boost::lexical_cast<int>(v);
}


VNode* VNode::find(const std::vector<std::string>& pathVec)
{
	if(pathVec.size() == 0)
		return this;

	if(pathVec.size() == 1)
	{
		return findChild(pathVec.at(0));
	}

	std::vector<std::string> rest(pathVec.begin()+1,pathVec.end());
	VNode *n = findChild(pathVec.at(0));

	return n?n->find(rest):nullptr;
}

std::string VNode::genVariable(const std::string& key) const
{
    std::string val;
    if(node_ )
    	node_->findGenVariableValue(key,val);
    return val;
}

std::string VNode::findVariable(const std::string& key,bool substitute) const
{
	std::string val;
	if(!node_ )
	    return val;

    //should set the def mutex because variableSubsitution
    //might need information from the defs

	const Variable& var=node_->findVariable(key);
	if(!var.empty())
	{
		val=var.theValue();
		if(substitute)
		{
		    node_->variableSubsitution(val);
		}
		return val;
	}
	const Variable& gvar=node_->findGenVariable(key);
	if(!gvar.empty())
	{
	   val=gvar.theValue();
	   if(substitute)
	   {
		   node_->variableSubsitution(val);
	   }
	   return val;
	}

	return val;
}

std::string VNode::findInheritedVariable(const std::string& key,bool substitute) const
{
    std::string val;
    if(!node_ )
    	return val;

    //should set the def mutex because it might need information from the defs
    //but it would be hang the GUI e.g. in the table view

    if(node_->findParentVariableValue(key,val))
    {
        if(substitute)
        {
            //this must resolve ECF_MICRO all the time
            node_->variableSubsitution(val);
        }
        return val;
    }

    return val;
}

void VNode::collectInheritedVariableNames(std::set<std::string>& vars) const
{
    if(!node_)
        return;

    std::vector<Variable> v,gv;
    variables(v);
    genVariables(gv);

    for(size_t i=0; i < v.size(); i++)
    {
        vars.insert(v[i].name());
    }

    for(size_t i=0; i < gv.size(); i++)
    {
        vars.insert(gv[i].name());
    }

    //Try to find it in the parent
    if(parent())
    {
        parent()->collectInheritedVariableNames(vars);
    }
}

bool VNode::substituteVariableValue(std::string& val) const
{
    if(!node_ )
        return false;

    //should set the def mutex because variableSubsitution
    //might need information from the defs
    return node_->variableSubsitution(val);
}

int VNode::variablesNum() const
{
	if(node_.get())
		return static_cast<int>(node_->variables().size());

	return 0;
}

int VNode::genVariablesNum() const
{
    std::vector<Variable> gv;

    if(node_)
	{
		node_->gen_variables(gv);
		return static_cast<int>(gv.size());
	}

	return 0;
}

void VNode::variables(std::vector<Variable>& vars) const
{
	vars.clear();
    if(node_)
		vars=node_->variables();
}

void VNode::genVariables(std::vector<Variable>& genVars) const
{
	genVars.clear();
    if(node_)
		node_->gen_variables(genVars);
}

std::string VNode::fullPath() const
{
    return absNodePath();
}

std::string VNode::absNodePath() const
{
	return (node_)?node_->absNodePath():"";
}

bool VNode::pathEndMatch(const std::string &relPath) const
{
    std::string pPath = absNodePath();
    if (relPath.empty() || pPath.size() < relPath.size()) {
        return false;
    } else {
        return
            (pPath.substr(pPath.size()-relPath.size(), std::string::npos) == relPath);
    }
    return false;
}

bool VNode::sameContents(VItem* item) const
{
    return item == this;
}

bool VNode::sameName(const std::string& name) const
{
	return(strName() == name)?true:false;
	//return (node_)?(node_->name() == name):false;
}

std::string VNode::strName() const
{
	if(node_ && node_.get())
		return node_->name();

	return std::string();
	/*
	if(name_.empty())
	{
		if(node_)
			name_=node_->name();
	}
	return name_;*/
}

QString VNode::name() const
{
	return QString::fromStdString(strName());
	//return (node_)?QString::fromStdString(node_->name()):QString();
}

std::string VNode::serverName() const
{
	if(ServerHandler* s=server())
	{
		return s->name();
	}
	return std::string();
}

QString VNode::stateName()
{
	return VNState::toName(this);
}

bool VNode::isDefaultStateComplete()
{
	if(node_)
		return (node_->defStatus() == DState::COMPLETE);

	return false;
}

QString VNode::defaultStateName()
{
	return VNState::toDefaultStateName(this);
}

QString VNode::serverStateName()
{
	return QString("");
}

bool VNode::isSuspended() const
{
	return (node_ && node_->isSuspended());
}

bool VNode::isAborted() const
{
	return (node_ && node_->state() == NState::ABORTED && !node_->isSuspended());
}

bool VNode::isSubmitted() const
{
    return (node_ && node_->state() == NState::SUBMITTED);
}

bool VNode::isActive() const
{
    return (node_ && node_->state() == NState::ACTIVE);
}

QColor  VNode::stateColour() const
{
	return VNState::toColour(this);
}

QColor  VNode::realStateColour() const
{
	return VNState::toRealColour(this);
}

QColor  VNode::stateFontColour() const
{
	return VNState::toFontColour(this);
}

QColor  VNode::typeFontColour() const
{
    return VNState::toTypeColour(this);
}

bool VNode::userLogServer(std::string& host,std::string& port)
{
    if(ServerHandler* sh = server()) {
        host = sh->conf()->stringValue(VServerSettings::UserLogServerHost).toStdString();
        if (!host.empty()) {
            port = sh->conf()->stringValue(VServerSettings::UserLogServerPort).toStdString();
            return !port.empty();
        }
    }
    return false;
}

bool VNode::logServer(std::string& host,std::string& port)
{
	if(!node_)
		return false;

	host=findInheritedVariable("ECF_LOGHOST",true);
	port=findInheritedVariable("ECF_LOGPORT");
	std::string micro=findInheritedVariable("ECF_MICRO");
	if(!host.empty() && !port.empty() &&
	  (micro.empty() || host.find(micro) ==  std::string::npos))
	{
		return true;
	}

	return false;
}

std::vector<VNode*> VNode::ancestors(SortMode sortMode)
{
	std::vector<VNode*> nodes;

	VNode* n=this;

	nodes.push_back(n);
	n=n->parent();

	if(sortMode == ChildToParentSort)
	{
		while(n)
		{
			nodes.push_back(n);
			n=n->parent();
		}
	}

	else if (sortMode == ParentToChildSort)
	{
		while(n)
		{
			nodes.insert(nodes.begin(),n);
			n=n->parent();
		}
	}

	return nodes;
}

VNode* VNode::ancestorAt(int idx,SortMode sortMode)
{
	if(sortMode == ChildToParentSort &&  idx==0)
		return this;

	std::vector<VNode*> nodes=ancestors(sortMode);
    if(static_cast<int>(nodes.size()) > idx)
	{
        return nodes[idx];
	}

	return nullptr;
}

const std::string&  VNode::nodeType()
{
	static std::string suiteStr("suite");
	static std::string familyStr("family");
	static std::string taskStr("task");
	static std::string defaultStr("node");
	static std::string serverStr("server");

	if(isServer())
		return serverStr;

	node_ptr np=node();

	if(!np || !np.get())
		return defaultStr;

	if(np->isSuite())
		return suiteStr;
	else if(np->isFamily())
		return familyStr;
	else if(np->isTask())
        return taskStr;

	return defaultStr;
}

#if 0
bool VNode::isFamily() const
{
    node_ptr np=node();

    if(!np || !np.get())
        return false;

    return (np->isFamily())?true:false;
}

bool VNode::isAlias() const
{
    node_ptr np=node();

    if(!np || !np.get())
        return false;

    return (np->isAlias())?true:false;
}
#endif

std::string VNode::flagsAsStr() const
{
    return (node_)?node_->flag().to_string():std::string();
}

bool VNode::isFlagSet(ecf::Flag::Type f) const
{
    if(node_)
	{
		return node_->flag().is_set(f);
	}
	return false;
}

void VNode::why(std::vector<std::string>& bottomUp,
                std::vector<std::string>& topDown) const
{
    if(node_)
	{
        node_->bottom_up_why(bottomUp,true);
        if(isFamily() || isSuite())
        {
           node_->top_down_why(topDown,true);
        }
    }
}

const std::string& VNode::abortedReason() const
{
    if(node_)
	{
		return node_->abortedReason();
	}

	static std::string emptyStr;
	return emptyStr;

}

void VNode::statusChangeTime(QString& sct) const
{
    if(node_)
    {
        boost::posix_time::ptime t = node_->state_change_time();
        std::string s=boost::posix_time::to_simple_string(t);
        sct=QString::fromStdString(s);
    }
}

unsigned int VNode::statusChangeTime() const
{
    if(node_)
    {
        static  boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
        boost::posix_time::ptime t = node_->state_change_time();
        boost::posix_time::time_duration diff(t - epoch);
        return diff.ticks()/diff.ticks_per_second();
    }
    return 0;
}

QString VNode::toolTip()
{    
    QString txt="<b>Name</b>: " + name() + "<br>";
    txt+="<b>Path</b>: " + QString::fromStdString(absNodePath()) + "<br>";
    txt+="<b>Type</b>: " + QString::fromStdString(nodeType()) + "<br>";
    
    txt+="<b>Status</b>: " + stateName();
    if(isSuspended())        
        txt+=" (" + VNState::toRealStateName(this) + ")";

    txt+="<br>";
    txt+="<b>Default status</b>: " + defaultStateName() + "<br>";
    
    txt+="<b>Server:</b> " + QString::fromStdString(server()->name()) + "<br>";
    txt+="<b>Host</b>: " + QString::fromStdString(server()->host());
    txt+=" <b>Port</b>: " + QString::fromStdString(server()->port());
    
    QString rs=QString::fromStdString(abortedReason());
    if(!rs.isEmpty())
    	txt+="<br><b>Aborted reason</b>:" + rs;

    return txt;
}   

//===========================================================
// Triggers
//===========================================================

void VNode::triggerExpr(std::string& trigger, std::string& complete) const
{
    VTriggerAttr::expressions(this,trigger,complete);
}

//Collect the information about all the triggers triggering this node
void VNode::triggers(TriggerCollector* tlc)
{
    VItem* nullItem=nullptr;
    if (!node_) {
        return;
    }

    //Check the node itself
    //if(tlr.self())
    {
        //find nodes, event, meters and variables triggering this node
        if(node_ && !node_->isSuite())
        {
            std::vector<VItem*> theVec;
            AstCollateVNodesVisitor astVisitor(theVec);

            //Collect the nodes from ast
            if(node_->completeAst())
                node_->completeAst()->accept(astVisitor);

            if(node_->triggerAst())
                node_->triggerAst()->accept(astVisitor);

            //Add the found items to the collector
            for(auto & it : theVec)
            {
                tlc->add(it,nullItem, TriggerCollector::Normal);
            }
        }

        //Check other attributes

        //Limiters
        const std::vector<InLimit>& limiterVec=node_->inlimits();
        for (auto & limiter: limiterVec) {
            if (Limit* lim = node_->findLimitViaInLimit(limiter)) {
                if (Node *limParent = lim->node()) {
                    if (VNode *p = root()->toVNode(limParent)) {
                        if(VAttribute *n=p->getLimit(lim->name())) {
                            tlc->add(n,nullItem, TriggerCollector::Normal);
                        }
                    }
                }
            }
        }

        //Date
        std::vector<VAttribute*> dateVec;
        findAttributes(VAttributeType::find("date"),dateVec);
        size_t n=dateVec.size();
        for(std::size_t i=0; i < n; i++)
        {
            tlc->add(dateVec[i],nullItem,TriggerCollector::Normal);
        }

        //Time
        std::vector<VAttribute*> timeVec;
        findAttributes(VAttributeType::find("time"),timeVec);
        n=timeVec.size();
        for(std::size_t i=0; i < n; i++)
        {
            tlc->add(timeVec[i],nullItem,TriggerCollector::Normal);
        }

    }

    if(tlc->scanParents())
    {
        VNode* p=parent();
        while(p)
        {
            TriggerParentCollector tpc(p,tlc);
            p->triggers(&tpc);
            p = p->parent();
        }
    }

    if(tlc->scanKids())
    {
        triggersInChildren(this,this,tlc);
    }
}

//Collect the triggers triggering node n in the children of parent
void VNode::triggersInChildren(VNode *n,VNode* p,TriggerCollector* tlc)
{
    for(auto & i : p->children_)
    {
        TriggerChildCollector tcc(n,i,tlc);
        i->triggers(&tcc);
        triggersInChildren(n,i,tlc);
  }
}

void VNode::clearTriggerData()
{
    if(data_)
        delete data_;
    data_=nullptr;
}

//These are called during the scan for triggered nodes
void VNode::addTriggeredData(VItem* n)
{
    if(!data_)
        data_=new VNodeTriggerData;

    data_->add(n);
}

void VNode::addTriggeredData(VItem* triggered,VAttribute* trigger)
{
    if(!data_)
       data_=new VNodeTriggerData;

    assert(trigger->parent() == this);
    data_->add(triggered,trigger);
}

//Collect the information about all the nodes this node or its attributes trigger
void VNode::triggered(TriggerCollector* tlc,TriggeredScanner* scanner)
{
    if(scanner && !root()->triggeredScanned())
    {
        //unsigned int aNum=VAttribute::totalNum();
        scanner->start(root());
        root()->setTriggeredScanned(true);
        //assert(aNum == VAttribute::totalNum());
    }

    //Get the nodes directly triggered by this node
    if(data_)
        data_->get(this,tlc);

    if(tlc->scanParents())
    {
        VNode* p=parent();
        while(p)
        {
            TriggerParentCollector tpc(p,tlc);
            p->triggered(&tpc);
            p = p->parent();
        }
    }

    if(tlc->scanKids())
    {
        triggeredByChildren(this,this,tlc);
    }
}

void VNode::triggeredByChildren(VNode *n,VNode* p,TriggerCollector* tlc)
{
    for(auto & i : p->children_)
    {
        TriggerChildCollector tcc(n,i,tlc);
        i->triggered(&tcc);
        triggeredByChildren(n,i,tlc);
    }
}


void VNode::triggeredByEvent(const std::string& name,std::vector<std::string>& triggeredVec,TriggeredScanner* scanner)
{
   if(scanner && !root()->triggeredScanned())
   {
       //unsigned int aNum=VAttribute::totalNum();
       scanner->start(root());
       root()->setTriggeredScanned(true);
       //assert(aNum == VAttribute::totalNum());
   }

   //Get the nodes directly triggered by this event
   if(data_)
       data_->getEvent(this,name,triggeredVec);
}

VAttribute* VNode::getLimit(const std::string& name)
{
    VAttribute* nullItem=nullptr;
    std::vector<VAttribute*> limit;
    findAttributes(VAttributeType::find("limit"),limit);
    std::size_t limitNum=limit.size();
    for(std::size_t i=0; i < limitNum; i++)
    {
       if(limit[i]->strName() == name)
       {
           return limit[i];
       }
    }

    return nullItem;
}

QString VNode::nodeMenuMode() const
{
    ServerHandler* s=server();
    Q_ASSERT(s);
    return s->nodeMenuMode();
}

QString VNode::defStatusNodeMenuMode() const
{
    ServerHandler* s=server();
    Q_ASSERT(s);
    return s->defStatusNodeMenuMode();
}

const std::string& VSuiteNode::typeName() const
{
   static std::string t("suite");
   return t;
}

const std::string& VFamilyNode::typeName() const
{
   static std::string t("family");
   return t;
}

const std::string& VAliasNode::typeName() const
{
   static std::string t("alias");
   return t;
}

void VNode::print()
{
    UiLog().dbg() << name() << " " << children_.size();
    for(auto & i : children_)
        i->print();
}

//=================================================
//
// VNodeRoot - this represents the server
//
//=================================================

VServer::VServer(ServerHandler* server) :
	VNode(nullptr,node_ptr()),
	server_(server),
    totalNum_(0),
    triggeredScanned_(false)
{
    //Attributes are not scannedfor servers
    Q_ASSERT(attr_.empty());
}

VServer::~VServer()
{
	clear();
}

int VServer::totalNumOfTopLevel(VNode* n) const
{
	if(!n->isTopLevel())
		return -1;

	int idx=indexOfChild(n);
	if(idx != -1)
		return totalNumOfTopLevel(idx);

	return -1;
}

int VServer::totalNumOfTopLevel(int idx) const
{
	assert(totalNumInChild_.size() == children_.size());

    if(idx >=0 && idx < static_cast<int>(totalNumInChild_.size()))
	{
        return totalNumInChild_[idx];
	}

	return -1;
}

int VServer::totalNumOfTopLevel(const std::string& name) const
{
    for(size_t i=0; i < children_.size(); i++)
    {
        if(name == children_[i]->strName())
            return totalNumOfTopLevel(i);
    }
    return -1;
}

//--------------------------------
// Clear
//--------------------------------

//Clear the whole contents
void VServer::clear()
{
	if(totalNum_==0)
		return;

	cache_.clear();
	prevNodeState_.clear();

    //clear attrubutes
    Q_ASSERT(attr_.empty());
    for(auto & i : attrForSearch_)
        delete i;

    attrForSearch_.clear();

	bool hasNotifications=server_->conf()->notificationsEnabled();

	//Delete the children nodes. It will recursively delete all the nodes. It also saves the prevNodeState!!
	for(std::vector<VNode*>::const_iterator it=children_.begin(); it != children_.end(); ++it)
	{
		deleteNode(*it,hasNotifications);
	}

	//Clear the children vector
	children_.clear();

	totalNumInChild_.clear();

	//A sanity check
	assert(totalNum_ == 0);

	//Deallocate the nodes vector
	nodes_=std::vector<VNode*>();

    triggeredScanned_=false;
}

//Delete a particular node
void VServer::deleteNode(VNode* node,bool hasNotifications)
{
    for(int i=0; i < node->numOfChildren(); i++)
	{
		deleteNode(node->childAt(i),hasNotifications);
	}

	//If there are notifications we need to save previous state
	if(hasNotifications)
	{
		if(node->node_->isTask())
		{
			VNodeInternalState st;
			node->internalState(st);
			prevNodeState_[node->absNodePath()]=st;
		}
	}

	delete node;
	totalNum_--;
}

//------------------------------------------
// Variables
//------------------------------------------

int VServer::variablesNum() const
{
	return cache_.vars_.size();
}

int VServer::genVariablesNum() const
{
	return cache_.genVars_.size();
}

void VServer::variables(std::vector<Variable>& vars) const
{
	vars.clear();
	vars=cache_.vars_;
}

void VServer::genVariables(std::vector<Variable>& vars) const
{
	vars.clear();
	vars=cache_.genVars_;
}

std::string VServer::genVariable(const std::string& key) const
{
	std::string val;

	for(const auto & genVar : cache_.genVars_)
	{
	   if(genVar.name() == key)
		   val=genVar.theValue();
	}

	return val;
}

//------------------------------------------
// Find
//------------------------------------------

VNode* VServer::toVNode(const Node* nc) const
{
	return static_cast<VNode*>(nc->graphic_ptr());
}

VNode* VServer::find(const std::string& fullPath)
{
	if(fullPath.empty())
		return nullptr;

	if(fullPath == "/")
		return this;

	std::vector<std::string> pathVec;
	boost::split(pathVec,fullPath,boost::is_any_of("/"));

	if(pathVec.size() > 0 && pathVec.at(0).empty())
	{
		pathVec.erase(pathVec.begin());
	}

	return VNode::find(pathVec);
}

std::string VServer::findVariable(const std::string& key,bool substitute) const
{
	std::string val;

	//Search user variables first
	for(const auto & var : cache_.vars_)
	{
		if(var.name() == key)
		{
			val=var.theValue();
			if(substitute)
                substituteVariableValue(val);

			return val;
		}
	}

	//Then search server variables
	for(const auto & genVar : cache_.genVars_)
	{
		if(genVar.name() == key)
		{
			val=genVar.theValue();
			if(substitute)
                substituteVariableValue(val);

			return val;
		}
	}

	return val;
}

std::string VServer::findInheritedVariable(const std::string& key,bool substitute) const
{
	return findVariable(key,substitute);
}

bool VServer::substituteVariableValue(std::string& val) const
{
	if(val.empty())
        return false;

	ServerDefsAccess defsAccess(server_);  // will reliquish its resources on destruction
	defs_ptr defs = defsAccess.defs();
	if (!defs)
        return false;

    return defs->server().variableSubsitution(val);
}

//----------------------------------------------
// Scan
//----------------------------------------------

//Clear the contents and get the number of children (suites)
//the server contain. At this point we do not build the tree.
void VServer::beginScan(VServerChange& change)
{
	//Clear the contents
	clear();

    //Get the Defs.
    {
        ServerDefsAccess defsAccess(server_);  // will reliquish its resources on destruction
        defs_ptr defs = defsAccess.defs();
        if (!defs)
            return;

        //We need to update the cached server variables
        updateCache(defs);
    }
}

//Build the whole tree.
void VServer::endScan()
{
	totalNum_=0;

	//Get the Defs
	{
		ServerDefsAccess defsAccess(server_);  // will reliquish its resources on destruction
		defs_ptr defs = defsAccess.defs();
		if (!defs)
			return;

		bool hasNotifications=server_->conf()->notificationsEnabled();

		//Scan the suits.This will recursively scan all nodes in the tree.
		const std::vector<suite_ptr> &suites = defs->suiteVec();

		for(const auto & suite : suites)
		{
            VNode* vn=new VSuiteNode(this,suite);
			totalNum_++;
			scan(vn,hasNotifications);
		}
	}

	if(totalNum_ > 0)
	{
		nodes_.reserve(totalNum_);
		collect(nodes_);
		for(size_t i=0; i < nodes_.size(); i++)
			nodes_[i]->setIndex(i);
	}
}

void VServer::scan(VNode *node,bool hasNotifications)
{
	int prevTotalNum=totalNum_;

	std::vector<node_ptr> nodes;
	node->node()->immediateChildren(nodes);

	//totalNum_+=nodes.size();

    //Preallocates the children vector to the reqiuired size to save memory.
    if(nodes.size() > 0)
    {
        node->children_.reserve(nodes.size());
    }

	for(std::vector<node_ptr>::const_iterator it=nodes.begin(); it != nodes.end(); ++it)
	{
		VNode* vn=nullptr;
		if((*it)->isTask())
		{
            vn=new VTaskNode(node,*it);

			//If there are notifications we need to check them using the previous state
			if(hasNotifications)
			{
				std::string path=(*it)->absNodePath();
				std::map<std::string,VNodeInternalState>::const_iterator itP=prevNodeState_.find(path);
				if(itP != prevNodeState_.end())
					vn->check(server_->conf(),itP->second);
            }
		}
        else if((*it)->isFamily())
		{
            vn=new VFamilyNode(node,*it);
		}
        else if((*it)->isAlias())
        {
            vn=new VAliasNode(node,*it);
        }
        else
        {
            assert(0);

        }
		totalNum_++;
		scan(vn,hasNotifications);
	}

	if(node->parent() == this)
	{
		 totalNumInChild_.push_back(totalNum_-prevTotalNum);
	}
}

VNode* VServer::nodeAt(int idx) const
{
    assert(idx>=0 && idx < static_cast<int>(nodes_.size()));
	return nodes_.at(idx);
}

//----------------------------------------------
// Update
//----------------------------------------------

void VServer::beginUpdate(VNode* node,const std::vector<ecf::Aspect::Type>& aspect,VNodeChange& change)
{
#if 0
    //If the number of nodes changed we need to rescan the whole server-tree
    if(std::find(aspect.begin(),aspect.end(),ecf::Aspect::ADD_REMOVE_NODE) != aspect.end())
    {
        change.rescan_=true;
    }
#endif
    //NOTE: when this function is called the real node (Node) has already been updated. However the
	//views do not know about this change. So at this point (this is the begin step of the update)
	//all VNode functions have to return the values valid before the update happened!!!!!!!
	//The main goal of this function is to cleverly provide the views with some information about the nature of the update.

    //Update the generated variables. There is no notification about their change so we have to do it!!!
	if(node->node())
	{
		Suite *s=nullptr;
		s=node->node()->isSuite();
		if(!s)
		{
			s=node->node()->suite();
		}

		if(s && s->begun())
		{
			node->node()->update_generated_variables();
            s->update_generated_variables();
		}

		if(node->nodeType() == "task")
		{
			bool stateCh=(std::find(aspect.begin(),aspect.end(),ecf::Aspect::STATE) != aspect.end());
			node->check(server_->conf(),stateCh);
		}
	}

    //-------------------------------------------------------------------------
    // The trigger relations might be changed. We need to clear the mapped
    // trigger relations globally if:
    //    -a trigger expression changed (the aspect is EXPR_TRIGGER)
    //    -a trigger expression was added or removed (the aspect is ADD_REMOVE_ATTR)
    //--------------------------------------------------------------------------

    for(auto it : aspect)
    {
        if(it == ecf::Aspect::ADD_REMOVE_ATTR)
        {
            //we need to rescan the attributes belong to the node
            node->rescanAttr();
            clearNodeTriggerData();
            return;
        }
        else if (it == ecf::Aspect::EXPR_TRIGGER)
        {
            clearNodeTriggerData();
        }
    }

	//In any other cases it is just a simple update (value or status changed)
}

//-------------------------------------------------------------------------------------------
// Finishes the update. It has to be consistent with the changes registered in VNodeChange.
// If anything does not match we return false that will call reset!!!
//-------------------------------------------------------------------------------------------

void VServer::endUpdate(VNode* node,const std::vector<ecf::Aspect::Type>& aspect,const VNodeChange& change)
{
}

void VServer::beginUpdate(const std::vector<ecf::Aspect::Type>& aspect)
{
	//We need to update the cached server variables
	if(std::find(aspect.begin(),aspect.end(),ecf::Aspect::SERVER_VARIABLE) != aspect.end() ||
	   std::find(aspect.begin(),aspect.end(),ecf::Aspect::FLAG) != aspect.end())
	{
		//This will use the defs!!!
		updateCache();
	}
}

// NOTE: server attributes are only used for search, because the tree is not yet
// able to manage them. So we always keep the attr_ vector empty and use attrForSearch_
// when server attribute data is needed for the search.
const std::vector<VAttribute*>& VServer::attrForSearch()
{
    Q_ASSERT(attr_.empty());
    for(auto & i : attrForSearch_)
        delete i;

    attrForSearch_.clear();

    // this call will update the attr_ vector !!
    rescanAttr();

    attrForSearch_ = attr_;
    attr_.clear();
    return attrForSearch_;
}

void VServer::suites(std::vector<std::string>& sv)
{
	for(int i=0; i < numOfChildren(); i++)
	{
		sv.push_back(children_.at(i)->strName());
	}
}

std::string VServer::strName() const
{
	if(server_)
		return server_->name();

	return std::string();


	/*if(name_.empty())
	{
		if(server_)
			name_=server_->name();
	}
	return name_;*/
}

QString VServer::stateName()
{
	if(VSState::isRunningState(server_))
	{
		return VNState::toName(server_);
	}

	return VSState::toName(server_);
}

QString VServer::defaultStateName()
{
	return stateName();
}

QString VServer::serverStateName()
{
	return VSState::toName(server_);
}

bool VServer::isSuspended() const
{
	return false;
}

QColor  VServer::stateColour() const
{
	if(VSState::isRunningState(server_))
	{
		return VNState::toColour(server_);
	}

	return VSState::toColour(server_);
}

QColor  VServer::stateFontColour() const
{
	if(VSState::isRunningState(server_))
	{
		return VNState::toFontColour(server_);
	}

	return VSState::toFontColour(server_);
}

void VServer::why(std::vector<std::string>& theReasonWhy) const
{
	ServerDefsAccess defsAccess(server_);  // will reliquish its resources on destruction
	defs_ptr defs = defsAccess.defs();
	if (!defs)
		return;

    defs->why(theReasonWhy,true);
}


std::string VServer::flagsAsStr() const
{
    return cache_.flag_.to_string();
}

bool VServer::isFlagSet(ecf::Flag::Type f) const
{
	return cache_.flag_.is_set(f);
}

QString VServer::logOrCheckpointError() const
{
    std::string s;
    if (isFlagSet(ecf::Flag::LOG_ERROR)) {
        s = findVariable("ECF_LOG_ERROR", true);
    }
    if (isFlagSet(ecf::Flag::CHECKPT_ERROR)) {
        if (!s.empty())
            s += " ";
        s += findVariable("ECF_CHECKPT_ERROR", true);
    }
    return QString::fromStdString(s);
}

void VServer::updateCache()
{
	cache_.clear();

	ServerDefsAccess defsAccess(server_);  // will reliquish its resources on destruction
	defs_ptr defs = defsAccess.defs();
	if (!defs)
		return;

	updateCache(defs);
}

void VServer::updateCache(defs_ptr defs)
{
	cache_.vars_=defs->server().user_variables();
	cache_.genVars_=defs->server().server_variables();
	cache_.flag_=defs->flag();
}

const std::string& VServer::typeName() const
{
   static std::string t("server");
   return t;
}

QString VServer::toolTip()
{
	QString txt="<b>Server</b>: " + QString::fromStdString(server_->name()) + "<br>";
	txt+="<b>Host</b>: " + QString::fromStdString(server_->host());
	txt+=" <b>Port</b>: " + QString::fromStdString(server_->port()) + "<br>";

    if (server_->isSsl() ) {
        txt+="<b>SSL</b>: enabled<br>";
    }

    if (!server_->user().empty()) {
        txt+="<b>Custom user</b>: " + QString::fromStdString(server_->user()) + "<br>";
    }

	ConnectState* st=server_->connectState();
    QColor colErr(255,95,95);

	if(server_->activity() == ServerHandler::LoadActivity)
	{
		txt+="<b>Server is being loaded!</b><br>";
		//txt+="<b>Started</b>: " + VFileInfo::formatDateAgo(st->lastConnectTime()) + "<br>";
	}
	else
	{
		if(st->state() == ConnectState::Normal)
		{
			txt+="<b>Server status</b>: " + VSState::toName(server_) + "<br>";
			txt+="<b>Status</b>: " + VNState::toName(server_) + "<br>";
			txt+="<b>Total number of nodes</b>: " +  QString::number(totalNum_);
		}
		else if(st->state() == ConnectState::Lost)
		{			
			txt+="<b><font color=" + colErr.name() +">Failed to connect to server!</b><br>";
			txt+="<b>Last connection</b>: " + VFileInfo::formatDateAgo(st->lastConnectTime()) + "<br>";
			txt+="<b>Last failed attempt</b>: " + VFileInfo::formatDateAgo(st->lastLostTime()) + "<br>";
			if(!st->errorMessage().empty())
				txt+="<b>Error message</b>:<br>" + QString::fromStdString(st->shortErrorMessage());
		}
		else if(st->state() == ConnectState::Disconnected)
		{			
			txt+="<b><font color=" + colErr.name() +">Server is disconnected!</b><br>";
			txt+="<b>Disconnected</b>: " + VFileInfo::formatDateAgo(st->lastDisconnectTime()) + "<br>";
        }
        else if(st->state() == ConnectState::VersionIncompatible)
        {
            txt+="<b><font color=" + colErr.name() +">Server version is incompatible with client!</b><br>";
            if(!st->errorMessage().empty())
                txt+="<b>Error message</b>:<br>" + QString::fromStdString(st->errorMessage());
        }
        else if(st->state() == ConnectState::SslIncompatible)
        {
            txt+="<b><font color=" + colErr.name() +">Possible SSL incompatibility between server and client!</b><br>";
            if(!st->errorMessage().empty())
                txt+="<b>Error message</b>:<br>" + QString::fromStdString(st->errorMessage()).replace("\n","<br>");
        }
        else if(st->state() == ConnectState::SslCertificateError)
        {
            txt+="<b><font color=" + colErr.name() +">SSL certificate error!</b><br>";
            if(!st->errorMessage().empty())
                txt+="<b>Error message</b>:<br>" + QString::fromStdString(st->errorMessage()).replace("\n","<br>");
        }
        else if(st->state() == ConnectState::FailedClient)
        {
            txt+="<b><font color=" + colErr.name() +">Could not create client object!</b><br>";
            if(!st->errorMessage().empty())
                txt+="<b>Error message</b>:<br>" + QString::fromStdString(st->errorMessage()).replace("\n","<br>");
        }
	}

    auto userLogHost = server_->conf()->stringValue(VServerSettings::UserLogServerHost);
    auto userLogPort = server_->conf()->stringValue(VServerSettings::UserLogServerPort);
    if (!userLogHost.isEmpty() && !userLogPort.isEmpty()) {
        txt+="<br><b>Custom logserver</b>: " + userLogHost + "@" + userLogPort;
    }

	return txt;
}

void VServer::clearNodeTriggerData()
{
    triggeredScanned_=false;
    std::size_t num=nodes_.size();
    for(std::size_t i=0; i < num; i++)
        nodes_[i]->clearTriggerData();
}

void VServer::print()
{
    for(auto & i : children_)
        i->print();
}
