#ifndef WIDGETS_GRAPHPLOTTER_PLOTTERITEMGROUP_H
#define WIDGETS_GRAPHPLOTTER_PLOTTERITEMGROUP_H

#include "Common.h"
#include "Vector2D.h"

#include "DataSet.h"

#include <QGraphicsView>

#include <deque>

// -- TDE NAMESPACE ----------------------------------------------------

namespace TDE {

// -- Widgets NAMESPACE ------------------------------------------------

namespace Widgets {

namespace Plot {


class GraphPlotter;
class PlotterItemGroup;

typedef SHARED_PTR<PlotterItemGroup> PlotterItemGroupPtr;

class PlotterItemGroup : public QObject
{
	Q_OBJECT

protected:
	PlotterItemGroup(GraphPlotter* plotterPtr, double zValue = 0.0);
	PlotterItemGroup(GraphPlotter* plotterPtr, DataSetPtr datasetPtr, double zValue = 0.0);
	PlotterItemGroup(GraphPlotter* plotterPtr, const std::string& layerName, double zValue = 0.0);
	PlotterItemGroup(GraphPlotter* plotterPtr, const std::string& layerKind, const std::string& layerName, double zValue = 0.0);
	PlotterItemGroup(GraphPlotter* plotterPtr, const std::string& layerName, DataSetPtr datasetPtr, double zValue = 0.0);
	PlotterItemGroup(GraphPlotter* plotterPtr, const std::string& layerKind, const std::string& layerName, DataSetPtr datasetPtr, double zValue = 0.0);

	virtual void _connect_dataset_updated();
	virtual void _disconnect_dataset_updated();

	friend class GraphPlotter;

public:
	virtual ~PlotterItemGroup() = 0;

	GraphPlotter* plotter() const 						{ return m_plotterPtr; }
	DataSetPtr dataSet() const 							{ return m_datasetPtr; }
	QGraphicsScene* scene() const;

	std::string kind() const 							{ return (m_kind.empty() ? "--" : m_kind); }
	std::string kind(const std::string& value)			{ std::string old = m_kind; m_kind = value; return old; }
	std::string name() const;
	std::string name(const std::string& value)			{ std::string old = m_name; m_name = value; return old; }
	std::string tag() const;

	virtual bool valid() const 							{ return m_valid; }
	virtual void setValid(bool value = true)			{ m_valid = value; }
	virtual void invalidate()							{ setValid(false); }

	virtual bool dirty() const 							{ return m_dirty; }
	virtual void setDirty(bool value = true)			{ m_dirty = value; }

	virtual void draw(bool invalidate = true, bool lazy = false);
	virtual void ll_draw();
	virtual void erase();
	virtual bool drawable();

	virtual void setColor(const QPen& pen);
	virtual void setColor(QColor& penColor);

	virtual QGraphicsLineItem* _addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen& pen = QPen());
	virtual QGraphicsLineItem* _addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QColor& penColor = QColor());
	virtual QGraphicsRectItem* _addRect(qreal x, qreal y, qreal w, qreal h, const QPen& pen = QPen(), const QBrush& brush = QBrush());
	virtual QGraphicsRectItem* _addRect(qreal x, qreal y, qreal w, qreal h, const QColor& penColor = QColor(), const QColor& brushColor = QColor());
	virtual QGraphicsEllipseItem* _addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen& pen = QPen(), const QBrush& brush = QBrush());
	virtual QGraphicsEllipseItem* _addEllipse(qreal x, qreal y, qreal w, qreal h, const QColor& penColor = QColor(), const QColor& brushColor = QColor());

	virtual QGraphicsLineItem* addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen& pen = QPen());
	virtual QGraphicsLineItem* addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QColor& penColor = QColor());
	virtual QGraphicsLineItem* addLine(const Point& p1, const Point& p2, const QPen& pen = QPen());
	virtual QGraphicsLineItem* addLine(const Point& p1, const Point& p2, const QColor& penColor = QColor());
	virtual QGraphicsRectItem* addRect(qreal x, qreal y, qreal w, qreal h, const QPen& pen = QPen(), const QBrush& brush = QBrush());
	virtual QGraphicsRectItem* addRect(qreal x, qreal y, qreal w, qreal h, const QColor& penColor = QColor(), const QColor& brushColor = QColor());
	virtual QGraphicsRectItem* addRect(const Point& p1, const Point& p2, const QPen& pen = QPen(), const QBrush& brush = QBrush());
	virtual QGraphicsRectItem* addRect(const Point& p1, const Point& p2, const QColor& penColor = QColor(), const QColor& brushColor = QColor());
	virtual QGraphicsEllipseItem* addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen& pen = QPen(), const QBrush& brush = QBrush());
	virtual QGraphicsEllipseItem* addEllipse(qreal x, qreal y, qreal w, qreal h, const QColor& penColor = QColor(), const QColor& brushColor = QColor());
	virtual QGraphicsEllipseItem* addEllipse(const Point& p1, qreal w, qreal h, const QPen& pen = QPen(), const QBrush& brush = QBrush());
	virtual QGraphicsEllipseItem* addEllipse(const Point& p1, qreal w, qreal h, const QColor& penColor = QColor(), const QColor& brushColor = QColor());
	virtual QGraphicsTextItem* addText(const QString& text, const QFont& font = QFont());
	virtual QGraphicsItem* addItem(QGraphicsItem* item);
	virtual bool hasItem(QGraphicsItem* item);

	virtual Point sampleToScreen(const Point& point);
	virtual Point screenToSample(const Point& point);

	virtual bool applyTransforms() const 				{ return m_apply_transforms; }
	virtual bool applyTransforms(bool value)
	{ bool old = m_apply_transforms; m_apply_transforms = value; return old; }

	virtual bool applyWorldToScene() const 				{ return m_apply_world_to_scene; }
	virtual bool applyWorldToScene(bool value)
	{ bool old = m_apply_world_to_scene; m_apply_world_to_scene = value; return old; }

	virtual Matrix3D world2SceneTransform();

	virtual void clearTransform();
	virtual void pushTransform(const Matrix3D& transform);
	virtual void popTransform();
	virtual bool currentTransformUnset() const 					{ return (m_tcache.isInvalid() || m_tcache.isZero()); }
	virtual const Matrix3D& currentTransform() 					{ if (currentTransformUnset()) _setupTransforms(); return m_tcache; }
	virtual const Matrix3D& currentInverseTransform() 			{ if (currentTransformUnset()) _setupTransforms(); return m_tcache_inv; }
	virtual bool currentTransformInvalid()		 				{ if (currentTransformUnset()) _setupTransforms(); return (currentTransform().isInvalid() || currentTransform().isZero()); }
	virtual bool currentTransformValid()	 					{ return (! currentTransformInvalid()); }

public slots:
	virtual void on_dataset_updated(QString name, long updated_id);

protected:
	virtual void createItemGroup(const QList<QGraphicsItem*>& items);
	virtual void createItemGroup();
	virtual void addToGroup(QGraphicsItem* item);
	virtual void destroyItemGroup();

	virtual void _erase();
	virtual void _drawSetup();
	virtual void _draw() = 0;

	virtual DataSetPtr _dataSetForTransform() const					{ return dataSet(); }
	virtual void _setupTransforms();
	virtual Matrix3D _computeWorld2SceneTransform();
	virtual void _updateTransform();

protected:
	GraphPlotter* m_plotterPtr;
	DataSetPtr m_datasetPtr;
	QMetaObject::Connection m_ds_updated_connection;
	std::string m_kind;
	std::string m_name;
	double m_zValue;
	bool m_valid;
	bool m_dirty;
	bool m_apply_transforms;
	bool m_apply_world_to_scene;
	Matrix3D m_w2s;
	Matrix3D m_tcache;
	Matrix3D m_tcache_inv;
	std::deque<Matrix3D> m_tstack;

	QGraphicsItem* m_topitem;
	QList<QGraphicsItem*> m_items;
};


} /* end of namespace Plot */

} /* end of namespace Widgets */

} /* end of namespace TDE */

#endif /* WIDGETS_GRAPHPLOTTER_PLOTTERITEMGROUP_H */
