#ifndef QT_SCIENCE_NODE
#define QT_SCIENCE_NODE

#include <qwidget.h>
#include <qobject.h>
#include <qpainter.h>
#include <qframe.h>
#include <qlabel.h>
#include <string>

#include "CompositeItem.h"
#include "NodeWidget.h"

/** Logical representation of a node in science tree */ 
class ScienceNode : public QObject
{
	Q_OBJECT
public:
	/** Gets free percents of this node 
	* @return Free percents of this node 
	*/
	virtual int getFreePercents() = 0;

	/** Adds more points to the node. Gets points that bubbled into it before
	* and value to add. First it removes all previously bubbled points and then
	* sets new value from given value and corrected by given multiplier
	* @param beforePoints Points that bubbled to the node before and will be removed
	* @param value Points that bubbled to the node now and will be added after correction
	* @param bubbledMultiplier Multiplier used for calculation of new value from give value
	*/
	virtual void addPoints(int beforePoints, int value, double bubbledMultiplier);

	/** Bubbles points to its children. 
	* @param beforePoints Points that bubbled to the children before
	* @param value Points that bubble to children now
	* @param Multiplier used for calculation of new value from give value
	*/
	virtual void bubblePoints(int beforePoints, int value, double bubbledMultiplier) = 0;

	/** Add more tokens to the node
	* @param value Amount of tokens to add
	*/
	void addTokens(int value);

	/** Constructor
	* @param nodeWidget Graphic widget that represents this node
	* @param label Label with node name that is contain in widget 
	* @param nodeName Logical name of the node
	*/
	ScienceNode(const std::string & nodeName, NodeWidget * nodeWidget, QLabel * label);

	/** Gets graphic widget that represents this node
	* @return Graphic widget that represents this node
	*/
	NodeWidget * getNodeWidget();

	/** Sets parent node for this node 
	* @param paren Parent node for this node 
	*/
	void setParent(ScienceNode * parent);

	/** Get parent node for this node 
	* @return Parent node for this node 
	*/
	ScienceNode * getParent();

	/** Gets depth of this node
	* @return Depth of this node
	*/
	int getDepth();

	/** Sets depth of this node
	* @param value Depth of this node
	*/
	void setDepth(int value);

	/** Appends given child to this node 
	* @param child Child node to append
	* @return True if node was added as a child, false otherwise
	*/
	virtual bool appendChild(ScienceNode * child) = 0;

	/** Checks if node is inner node
	* @return True if node is inner node, false otherwise 
	*/
	virtual bool isInner() = 0;

	/** Checks if node is leaf node
	* @return True if node is leaf node, false otherwise 
	*/
	virtual bool isLeaf();

	/** Gets amount of assigned tokens 
	* @return Amount of assigned tokens 
	*/
    int getAssignedTokens() const;

	/** Gets amount of assigned points 
	* @return Amount of assigned points 
	*/
    int getAssignedPoints() const;

	/** Sets multiplicator for calculation of bubbled points
	* @param value Multiplicator for calculation of bubbled points
	*/
	void setTokensMultiplicator(double value);

	/** Gets name of this node
	* @return Name of this node
	*/
	QString getName();

	/** Gets list of composite items that this node contains
	* @return List of composite items, that this node contains
	*/
	virtual std::vector<CompositeItem *> & getCompositeItems() = 0;

	/** Gets count of composite items that this node contains
	* @return ount of composite items, that this node contains
	*/
	virtual int getCompositeCountAct() = 0;

	/** Appends given composite to the list of childs composite items
	* @param composite Composite item to append
	*/
	virtual void appendComposite(CompositeItem * composite) = 0;

	/** Enables or disables showing points in nodes 
	* @param enabled If points will be shown
	*/
	void setShowPoints(bool enabled);

	/** Enables or disables showing tokens in nodes 
	* @param enabled If tokens will be shown
	*/
	void setShowTokens(bool enabled);

	/** Clears complete state of the node, that means all assigned tokens, percents and created scientific points. */
	virtual void clearState() = 0;

	/** Sets maximum assigned tokens to the node
	* @param value Maximum assigned tokens to the node
	*/
	void setMaximumTokens(unsigned long value);

	/** Gets maximum assigned tokens to the node
	* @return Maximum assigned tokens to the node
	*/
	unsigned long getMaximumTokens();
public slots:
	/** Assigns tokens to the node and bubbles it to children
	* @param value Assigned tokens to node 
	*/
	// TODO SLOT? Why?
    void setAssignedTokens(int value);

protected:
	int fullAssignedPoints_;
	ScienceNode * parent_;
	int assignedTokens_;
	int assignedPoints_;
	QString name_;
	int depth_;

	int showPoints_;
	int showTokens_;

	double pointsMultiplier_;

	NodeWidget * nodeWidget_;
	QLabel * label_;
	std::string logicalName_;

	unsigned long maximumTokens_;

	virtual void refreshWidget();
};

/** Logical representation of an inner node in science tree */ 
class ScienceInnerNode : public ScienceNode
{	
	Q_OBJECT
public:
	/** Gets free percents of this node. 
	* This only throws an exception, because inner node cannot have free percents 
	* @return Free percents of this node 
	*/
	virtual int getFreePercents();

	/** Constructor
	* @param node Graphic widget that represents this node
	* @param label Label with node name that is contain in widget 
	* @param nodeName Name of the node for logical purposes
	*/
	ScienceInnerNode(const std::string & nodeName, NodeWidget * node, QLabel * label);

	/** Bubbles points to its children. 
	* @param beforePoints Points that bubbled to the children before
	* @param value Points that bubble to children now
	* @param Multiplier used for calculation of new value from give value
	*/
	void bubblePoints(int beforePoints, int value, double bubbledMultiplier);	

	/** Gets list of composite items that this node contains
	* This only throws an exception, because inner node cannot have composite items
	* @return List of composite items, that this node contains
	*/
	virtual std::vector<CompositeItem *> & getCompositeItems();

	/** Gets count of composite items that this node contains
	* This only throws an exception, because inner node cannot have composite items
	* @return ount of composite items, that this node contains
	*/
	virtual int getCompositeCountAct();

	/** Appends given composite to the list of childs composite items
	/** This only throws an exception, because inner node cannot have composite items
	* @param composite Composite item to append		   
	*/
	virtual void appendComposite(CompositeItem * composite);

	/** Appends given child to this node 
	* @param child Child node to append
	* @return True if node was added as a child, false otherwise
	*/
	virtual bool appendChild(ScienceNode * child);

	/** Checks if node is inner node
	* @return True if node is inner node, false otherwise 
	*/
	virtual bool isInner();

	virtual void clearState();
private:
	typedef std::vector<ScienceNode *>::const_iterator NodesIterator;
	std::vector<ScienceNode *> children_;
};

/** Logical representation of a leaf node in science tree */ 
class ScienceLeafNode : public ScienceNode
{
public:
	typedef std::vector<CompositeItem *>::const_iterator ParamsIterator;

	/** Gets list of composite items that this node contains
	* @return List of composite items, that this node contains
	*/
	virtual std::vector<CompositeItem *> & getCompositeItems();

	/** Destructor */
	 ~ScienceLeafNode();

	/** Gets count of composite items that this node contains
	* @return ount of composite items, that this node contains
	*/
	virtual int getCompositeCountAct();
	 
	/** Gets free percents of this node 
	* @return Free percents of this node 
	*/
	virtual int getFreePercents();

	virtual void clearState();

	/** Constructor
	* @param node Graphic widget that represents this node
	* @param label Label with node name that is contain in widget 
	* @param nodeName Logical name of created node
	*/
	ScienceLeafNode(const std::string & nodeName, NodeWidget * node, QLabel * label);	

	/** Appends given composite to the list of childs composite items
	* @param composite Composite item to append
	*/
	virtual void appendComposite(CompositeItem * composite);

	/** Appends given child to this node 
	* This only throws an exception, because leaf node cannot have children
	* @param child Child node to append
	* @return True if node was added as a child, false otherwise
	*/
	virtual bool appendChild(ScienceNode *);

	/** Bubbles points to its children. 
	* This only throws an exception, because leaf node cannot have children
	* @param beforePoints Points that bubbled to the children before
	* @param value Points that bubble to children now
	* @param Multiplier used for calculation of new value from give value
	*/
	virtual void bubblePoints(int beforePoints, int value, double bubbledMultiplier);

	/** Checks if node is inner node
	* @return True if node is inner node, false otherwise 
	*/
	virtual bool isInner();

	/** Handler for change of assigned percents in given item
	* @param item Item where assigned percents changed
	* @param value Amout of assigned percents
	* @return True if operation was succesful
	*/
	bool assignedPercentsChanged(CompositeItem * item, int value);
private:	
	int freePercents_;
	std::vector<CompositeItem *> compositeItems_;
};

#endif