/*************************************************************************
 * 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
   GedDesState.cpp - Ged DES state class. 
 FUNCTION
   The implementation of GedDesState.h. 
 NOTES
 MODIFIED
   xma	    09/01/07 - CREATION 
*/

#include <QtGui>
#include <QFont>

#include "GedDesState.h"
#include "GedDesTrans.h"
#include "GedDesScene.h"
#include "CommonDefinitions.h"


namespace DESpot
{

GedDesState::GedDesState(const DesState& state, QMenu *contextMenu,
        QGraphicsItem *parent, QGraphicsScene *scene)
    : QGraphicsEllipseItem(parent, scene)
{
	desState=&state;
    if (state.isInit())
		if(state.isMarked())
			gedStateType=InitMarkedState;
		else 
			gedStateType=InitState;
	else 
		if(state.isMarked())
			gedStateType=MarkedState;
		else 
			gedStateType=RegularState;

    this->contextMenu = contextMenu;

	rect = QRectF(-10,-10,20,20);
    setRect(rect);

	setFlag(QGraphicsItem::ItemIsMovable, true);
    setFlag(QGraphicsItem::ItemIsSelectable, true);

    // next needed for changes made to qt 4.6 and greater
#ifdef  __QT_Vers_gteq_4_6__
    setFlag(QGraphicsItem::ItemSendsGeometryChanges);
#endif

	label = new GedDesLabel(this);
	label->setPlainText(QString::fromStdWString(state.getName()));
	QFont tmpFont = QFont();
	tmpFont.setPointSizeF(10);
	label->setFont(tmpFont);

	//label->setPos(QPointF(20,20));
	gedSimType = RegularSimState;

}

// Construction for painting des component buttons
GedDesState::GedDesState(GedStateType desCompType, QMenu *contextMenu,
             QGraphicsItem *parent, QGraphicsScene *scene)
    : QGraphicsEllipseItem(parent, scene)
{

	gedStateType = desCompType;
    this->contextMenu = contextMenu;

	rect = QRectF(-10,-10,20,20);
    setRect(rect);
	setFlag(QGraphicsItem::ItemIsMovable, true);
    setFlag(QGraphicsItem::ItemIsSelectable, true);

    // next needed for changes made to qt 4.6 and greater
#ifdef  __QT_Vers_gteq_4_6__
    setFlag(QGraphicsItem::ItemSendsGeometryChanges);
#endif

    gedSimType = RegularSimState;
}

GedDesState::~GedDesState()
{
}


const GedDesTrans* GedDesState::gedTrans(const DesEvent *event) const
{
	if (transMap.contains(event))
		 return transMap.value(event);
	else 
		return 0;
}

void GedDesState::addGedDesTrans(GedDesTrans *tran)
{
    trans.append(tran);
}

void GedDesState::removeGedDesTrans(GedDesTrans *tran)
{
	//if (trans) return;

	if (trans.isEmpty()) return;

    int index = trans.indexOf(tran);

	assert(index>-1);

    if (index > -1)
        trans.removeAt(index);
}

void GedDesState::removeGedDesTrans()
{
	//We will double free selfloop 
	//selfloop trans, two entries in trans
	//foreach loop has two same selfloop *trans, delete will run twice
	//fix: use set 

	QSet<GedDesTrans*> transSet = trans.toSet();
    foreach (GedDesTrans *tran, transSet) {
		if (tran==0) continue;  //fix double delete of selfloop;
		GedDesState *start = tran->getStartState();
		GedDesState *end = tran->getEndState();
        start->removeGedDesTrans(tran);
	    end->removeGedDesTrans(tran);

		//Call GedDesTrans to remove DES transitions
		tran->clearEvents();
        scene()->removeItem(tran);
        delete tran;
    }
}

QPixmap GedDesState::image() const
{
    QPixmap pixmap(250, 250);
    pixmap.fill(Qt::transparent);
    QPainter painter(&pixmap);
    //painter.setPen(QPen(Qt::black, 8));

	QPen myPen = pen();
	myPen.setColor(Qt::black);
	myPen.setStyle(Qt::SolidLine);
	myPen.setWidth(2);

	painter.setPen(myPen);

	painter.translate(125, 125);
    painter.scale(10,10);

	painter.drawEllipse(rect);
	if (gedStateType==InitState)
		painter.drawEllipse(-6,-6,12,12);
	if (gedStateType==InitMarkedState || gedStateType==MarkedState){
		QPen markPen  = pen();
		markPen.setColor(Qt::black);
		markPen.setStyle(Qt::SolidLine);
		markPen.setWidth(2);
		painter.setPen(markPen);
		painter.setBrush(QBrush(Qt::black, Qt::SolidPattern));
		
		if(gedStateType==InitMarkedState) 
			painter.drawEllipse(QRectF(-6,-6,12,12));
		if (gedStateType==MarkedState)
			painter.drawEllipse(QRectF(-10,-10,20,20));
		painter.setBrush(QBrush());
		painter.setPen(myPen);
	}

    return pixmap;
}

void GedDesState::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
	if (!contextMenu) { return; }

    scene()->clearSelection();
    setSelected(true);
    contextMenu->exec(event->screenPos());
}

QVariant GedDesState::itemChange(GraphicsItemChange change,
                     const QVariant &value)
{
    if (change == QGraphicsItem::ItemPositionChange) {
        foreach (GedDesTrans *tran, trans) {
            tran->updatePosition();
        }
    }

    return value;
}

void GedDesState::setStateType(GedStateType type, bool toSet)
{
	if(toSet)
	{
		if(type==InitState){
			if(gedStateType==MarkedState) setType(InitMarkedState);
			if(gedStateType==RegularState) setType(InitState);
		}
		else if (type == MarkedState){
			if(gedStateType==InitState) setType(InitMarkedState);
			if(gedStateType==RegularState) setType(MarkedState);
		}
	}
	else
	{
		if(type==InitState){
			if(gedStateType==InitMarkedState) setType(MarkedState);
			if(gedStateType==InitState) setType(RegularState);
		}
		else if (type == MarkedState){
			if(gedStateType==InitMarkedState) setType(InitState);
			if(gedStateType==MarkedState) setType(RegularState);
		}
	}
	update(rect);
}

void GedDesState::setSimType(GedSimType type)
{
	gedSimType = type;
}

void GedDesState::setReadOnly(bool readOnly)
{
	setFlag(QGraphicsItem::ItemIsMovable, !readOnly);
	setFlag(QGraphicsItem::ItemIsSelectable, !readOnly);
	label->setFlag(QGraphicsItem::ItemIsMovable, !readOnly);
	label->setFlag(QGraphicsItem::ItemIsSelectable, !readOnly);
}

  // this function should only be called by simulator, never by 
  // graphical des editor

void GedDesState::draw(QPainter* painter, QPointF& offset)
{
	painter->save();
	painter->translate(pos()- offset);
	drawState(painter, true);
	if (label)
	{
		painter->setPen(Qt::black);
		painter->translate(label->pos());
		painter->drawText(label->boundingRect(), label->toPlainText(),
				QTextOption(Qt::AlignBottom | Qt::AlignHCenter));
	}
	painter->restore();
}

void GedDesState::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
          QWidget *)
{
	drawState(painter, false);
}

void GedDesState::drawState(QPainter* painter, bool simMode)
{
	painter->setRenderHint(QPainter::Antialiasing);

	QColor color =  Qt::black;
	if (simMode) {
		if (gedSimType == RegularSimState) {
			color = Qt::black;
		} else if (gedSimType == CurrentSimState) {
			color = Qt::blue;
		} else if (gedSimType == EligibleSimState) {
			color = Qt::yellow;
		} else if (gedSimType == IneligibleSimState) {
			color = Qt::red;
		}
	}

	QPen myPen = pen();
	myPen.setColor(color);
	myPen.setStyle(Qt::SolidLine);
	myPen.setWidth(2);

	painter->setPen(myPen);

	painter->drawEllipse(rect);
	if(gedStateType==InitState) 
	{
		painter->drawEllipse(QRectF(-6,-6,12,12));
	}
	if (gedStateType==InitMarkedState || gedStateType==MarkedState)
	{
		painter->setBrush(QBrush(color, Qt::SolidPattern));
		if(gedStateType==InitMarkedState) 
			painter->drawEllipse(QRectF(-6,-6,12,12));
		if (gedStateType==MarkedState)
			painter->drawEllipse(QRectF(-10,-10,20,20));
		painter->setBrush(QBrush());
	}

	if (isSelected() && !simMode) {
		painter->setPen(QPen(Qt::black, 1, Qt::DashLine));
		QRectF myRect = this->boundingRect();
		painter->drawRect(myRect);
		label->setSelected(true);
	}
}

void GedDesState::updateState()
{ 
	//Possible change of a state: 1) type 2) label
	DesState state = *desState;
    if (state.isInit())
		if(state.isMarked())
			gedStateType=InitMarkedState;
		else 
			gedStateType=InitState;
	else 
		if(state.isMarked())
			gedStateType=MarkedState;
		else 
			gedStateType=RegularState;

	//Label
	label->setPlainText(QString::fromStdWString(state.getName()));
	update(boundingRect());
	//label->update();
}

}
