/*************************************************************************
 * This file is part of Graphic des EDitor project
 *
 * Project created in conformity with the requirements for the Degree of
 * Master of Engineering in Software Engineering, Computing and Software
 * Department, McMaster University 2004 - 2008
 *
 * Author:	Xiao Ma
 * Supervisor: Dr. Ryan Leduc
*************************************************************************/

/*
 NAME
   GedDesTransEditor.cpp - implementation of Ged DES transition function editor.
 FUNCTION
   The class provides a dialog to modify the properties of a graphic transition
   function by adding or removing events to the transition. A graphic transition
   function may represent multiple DES transition function.
 NOTES
 MODIFIED
   xma	    04/01/08 - CREATION
*/

#pragma once

#include <QtGui>
#include <QList>
#include <QMap>
#include <set>

#include "GedDesState.h"
#include "GedDesTrans.h"
#include "DesState.h"
#include "DesEvent.h"
#include "DesTransition.h"
#include "GedDesLabel.h"
#include "ProjectEvent.h"

class QGraphicsPolygonItem;
class QGraphicsLineItem;
class QGraphicsScene;
class QRectF;
class QGraphicsSceneMouseEvent;
class QPainterPath;
class QMenu;
class QListWidget;
class QPushButton;

namespace DESpot
{

// used to specify a label position or transition angle is a default value
static const double defaultGedItemValue = -500000000.0;
static const bool DEFAULT_TRANS_IS_CURVE = true;

class Des;

class GedDesTrans : public QGraphicsPathItem
{
public:
    enum DesCompType { Type = UserType + 4 };
    enum GedSimType { RegularSimTrans, EligibleSimTrans, PrevSimTrans, EligiblePrevSimTrans, SelectedSimTrans };

    GedDesTrans(Des* des, GedDesState *startState, GedDesState *endState, QMenu *contextMenu,
      QGraphicsItem *parent = 0, QGraphicsScene *scene = 0, bool isCurve = DEFAULT_TRANS_IS_CURVE);

    void setMenu(QMenu* menu) { contextMenu = menu; }

	int type() const
        { return Type; }
    GedDesState *getStartState() const { return startState; }
    GedDesState *getEndState() const { return endState; }
	bool isSelfLoop() {return (startState == endState);}
	void setLine(const QLineF &ln);
	void setTransShape(QList<QPointF> pos);
	void restoreDefaultShape();
	void setAngle(qreal angle);

	GedSimType getSimType() const { return simType; }
	void setSimType(GedSimType type);

	//Des related
	void insertDesTrans(const DesTransition* desTrans);
	const DesState* fromState() const
		{return startState->getDesState();}
	const DesState* toState() const
		{return endState->getDesState();}
	// Get the set of points that
	const QList<QPointF> getPos() const;
	QPointF labelPos() const
		{return label->pos();}
	void setLabelPos(QPointF labelPos)
		{label->setPos(labelPos);}
	QRectF labelboundingRect() const
        	{return label->boundingRect();}
	qreal angle() const
		{return currentAngle;}
	const QList<const DesEvent* > desEvents()
		{return desEventList;}
	int eventCount()
		{return desEventList.count();}
	bool isCurve() const 
		{ return m_isCurve; }
	void setDesEvents(QList<const DesEvent* > events);
	void clearEvents();
	bool findEvent(const DesEvent *ev);
	void deleteEventOnly(const DesEvent* ev); //delete ev only, not transition

	void toggleCurvature();
	void updateLabel();
	void rotateSelfLoop(qreal angle); //to fix rotate issue when reading from file

	bool isReadOnly() const { return !((flags() & QGraphicsItem::ItemIsMovable) && (flags() & QGraphicsItem::ItemIsSelectable)); }
	void setReadOnly(bool readOnly);

	void setEligibleEvents(std::set<ProjectEvent*>* pEligEvents) { m_pEligEvents = pEligEvents; }
	void setPrevEvent(ProjectEvent* pEvent) { m_pPrevEvent = pEvent; }
	void setNextEvent(ProjectEvent* pEvent) { m_pNextEvent = pEvent; }

	void draw(QPainter* painter, QPointF& offset);

public slots:
    void updatePosition();

protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget = 0);
    void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);

	QLineF createCircle();
	QLineF createLine();
	QLineF changeLine();

	QPainterPath selectShape() const;
	QPainterPath shape() const;

	void drawTrans(QPainter* painter, bool simMode);

	QVariant itemChange(GraphicsItemChange change, const QVariant &value);

private:
	// All operations to desEventList must use insertEvent() and deleteEvent()
	// to make sync with underlying DES system
	// Excpetion: 1) New GedDesTrans from scene, trans inserted in the scene alreay
	//            2) draw second line in scene to insert a new trans. trans inserted in scene already
	void insertEvent(const DesEvent* ev);
	void deleteEvent(const DesEvent* ev);

	void initTransProps();

    GedDesState *startState;
    GedDesState *endState;
	QMenu *contextMenu;
    QPolygonF arrowHead;
	QLineF *line;
	qreal currentAngle;
	QPainterPath naPath;  //no arrow path - path that consists only of the line ends for a line or control points for a curve
	QPointF m_lastStartPos; // last position of start state
	QPointF m_lastEndPos; // last position of end state
	bool m_isCurve;
    GedDesLabel *label;
	QList<const DesEvent* > desEventList;
	Des* des;
	GedSimType simType;
	std::set<ProjectEvent*>* m_pEligEvents;
	ProjectEvent* m_pPrevEvent;
	ProjectEvent* m_pNextEvent;

	typedef struct {
		QColor color;
		QString text;
		bool controllable;
		bool prev;
		bool next;
	} SimLabel;

	QList<SimLabel> simLabels;
};

}
