#include <QtCore>
#include <QString>
#include <QtAlgorithms>
#include "SimEventModel.h"
#include "DesSubsystem.h"
#include "DesInterface.h"
#include "DesProject.h"

namespace DESpot
{

const QString SimEventNode::cDefaultTypeLabel		=QObject::tr("d");
const QString SimEventNode::cHighLevelTypeLabel		=QObject::tr("h");
const QString SimEventNode::cLowLevelTypeLabel		=QObject::tr("l");
const QString SimEventNode::cRequestTypeLabel		=QObject::tr("r");
const QString SimEventNode::cAnswerTypeLabel		=QObject::tr("a");
const QString SimEventNode::cLDataTypeLabel			=QObject::tr("ld");


SimEventNode::SimEventNode(unsigned long step, const ProjectEvent* ev)
{
	this->step = step;
	this->ev = ev;

	const DesEvent* srcev=0;
	QString name;
	if (!const_cast<ProjectEvent*>(ev)->findSourceEvent(srcev)){
		name=QString::fromStdWString(ev->getName());
		//name.append("[No Src!]");
	}
	else 
		name=QString::fromStdWString(ev->getName());
	QString alias=QString::fromStdWString(ev->getAlias());
	QString controlable=ev->isControllable() ? "x" : "";
	QString type=getTypeLabel(ev->getType());
	props<<name<<alias<<controlable<<type;
}

QString SimEventNode::getTypeLabel(EventType eventType)
{
	switch(eventType)
	{
        case eDefaultEvent:
			return cDefaultTypeLabel;

        case eHighLevelEvent:
			return cHighLevelTypeLabel;

        case eLowLevelEvent:
			return cLowLevelTypeLabel;

        case eRequestEvent:
			return cRequestTypeLabel;

        case eAnswerEvent:
			return cAnswerTypeLabel;

        case eLDataEvent:
			return cLDataTypeLabel;

		default:
			assert(false);
			throw EX("Unknown event type. Cannot return label.")
	}
}

SimEventModel::SimEventModel(QObject *parent)
    : QAbstractTableModel(parent)
{
}

void SimEventModel::setEventMap(const QMultiMap<unsigned long, const ProjectEvent*> map)
{
	eventDataMap.clear();
	eventMap.clear();
    eventMap = map;

	//qSort(map.begin(),map.end(),lessThan);

	foreach (int key, eventMap.keys()){
		foreach (const ProjectEvent* ev, map.values(key)){
			eventDataMap.append(new SimEventNode(key,ev));
		}
	}
    reset();
}

void SimEventModel::setEventMap(std::set<const ProjectEvent*> eventSet)
{
	//Fix. This is called by updateViewer to setup eligible event
	//So the order is not always false. Setting to faluse will seg fault
	//Testcase:   simple manufacture example, accept all in wizard, step forward
	//setOrder(false);

	eventMap.clear();
	std::set<const ProjectEvent*>::iterator it=eventSet.begin();
	for (unsigned long i=1; it != eventSet.end(); ++it, ++i)
		eventMap.insert(i, *it);

	popuplateEventDataMap();
}

void SimEventModel::setEventMap(std::set<ProjectEvent*> eventSet)
{
	//Fix. This is called by updateViewer to setup eligible event
	//So the order is not always false. Setting to faluse will seg fault
	//Testcase:   simple manufacture example, accept all in wizard, step forward
	//setOrder(false);

	eventMap.clear();
	std::set<ProjectEvent*>::iterator it=eventSet.begin();
	for (unsigned long i=1; it != eventSet.end(); ++it, ++i)
		eventMap.insert(i, *it);

	popuplateEventDataMap();
}

void SimEventModel::popuplateEventDataMap()
{
	eventDataMap.clear();
	foreach (int key, eventMap.keys()){
		foreach (const ProjectEvent* ev, eventMap.values(key)){
			eventDataMap.append(new SimEventNode(key,ev));
		}
	}
	reset();
}


int SimEventModel::rowCount(const QModelIndex & /* parent */) const
{
    return eventMap.count();
}

int SimEventModel::columnCount(const QModelIndex & /* parent */) const
{
	if (ordered) 
		return 5;
	else 
		return 4;
}

QVariant SimEventModel::data(const QModelIndex &index, int role) const
{
	/*  QMultiMap version
    if (!index.isValid())
        return QVariant();

    if (role == Qt::TextAlignmentRole) {
        return int(Qt::AlignRight | Qt::AlignVCenter);
    } else if (role == Qt::DisplayRole) {		
		int seq = (eventMap.begin()+index.row()).key();
		const ProjectEvent* ev = (eventMap.begin()+index.row()).value();
		if (ordered)
			if (index.column()==0) 
				return QString("%1").arg(seq);
			else
				return eventDataMap[ev][index.column()-1];
		else
			return eventDataMap[ev][index.column()];
	}
    return QVariant();
	*/
    if (!index.isValid())
        return QVariant();

    if (role == Qt::TextAlignmentRole) {
        return int(Qt::AlignRight | Qt::AlignVCenter);
    } else if (role == Qt::DisplayRole) {		
		int row = index.row();
		unsigned long seq = eventDataMap[row]->step;
		/*const ProjectEvent* ev = */eventDataMap[row]->ev;

		// If we need a event sequence, we display column 0 as seq
		if (ordered)   
			if (index.column()==0) 
				return QString("%1").arg(seq);
			else
				return eventDataMap[row]->props[index.column()-1]; //index into props
		else
			return eventDataMap[row]->props[index.column()];
	}
    return QVariant();

}

QVariant SimEventModel::headerData(int section,
                                   Qt::Orientation orientation,
                                   int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

	if (orientation == Qt::Vertical)
		return QString("%1").arg(section+1);

	QStringList header;
	header<<tr("Seq")<<tr("Event")<<tr("Alias")<<tr("Ctrl")<<tr("Type");
	if(ordered)
		if(section==0) 
			return "Seq";
		else
			return header[section];
	else
		return header[section+1];

}


// This function support multiple events in each step. i.e
// Step       Event
// 1          e1
// 2          e2
// 3          e3
// 3          e2
// 4          e1
// The selected rows are inserted into below the current row
// If the step of current row = nex row, inserted rows will have same steps
// If not, insert as sequence
void SimEventModel::addEvents(QList<const ProjectEvent*> selEvents, int row)
{
	if (selEvents.count()<=0) return;

	QMultiMap<unsigned long, const ProjectEvent*> newEvMap;

	newEvMap.clear();
	int eventCnt = eventDataMap.count();
	int existingMaxStep = (eventCnt==0)?0:eventDataMap[eventCnt-1]->step;
	int selCnt = selEvents.count();
	
	// We keep the sequence increasing for now
	// no selection or select last line, add to the bottom of the list
	if (row == -1 || row == (eventCnt - 1)) 
	{
		for (int i=0; i<eventCnt; i++)
			newEvMap.insert(eventDataMap[i]->step,eventDataMap[i]->ev);
		for (int i=0; i<selEvents.count(); i++)
			newEvMap.insert(existingMaxStep+i+1,selEvents.at(i));
	}

	if (row >= 0 && row < (eventDataMap.count() - 1)) // in the middle of the list
	{
		for(int i=0; i<=row; i++)
			newEvMap.insert(eventDataMap[i]->step,eventDataMap[i]->ev);

		if (eventDataMap[row]->step == eventDataMap[row+1]->step) // Keep steps the same
		{
			for (int i=0; i<selEvents.count(); i++)
				newEvMap.insert(eventDataMap[row]->step,selEvents.at(i));
			for (int i=row+1; i< eventDataMap.count() ; i++)
				newEvMap.insert(eventDataMap[i]->step, eventDataMap[i]->ev);
		}
		else{
			for (int i=0; i<selEvents.count(); i++){
				int step = eventDataMap[row]->step + 1 + i;
				newEvMap.insert(step,selEvents.at(i));
			}
			for (int i=row+1; i< eventDataMap.count() ; i++){
				int step = eventDataMap[i]->step + selCnt;
				newEvMap.insert(step, eventDataMap[i]->ev);
			}
		}
	}

	setEventMap(newEvMap);

}

void SimEventModel::removeAllEvents()
{

	eventDataMap.clear();
	eventMap.clear();
	reset();
}

void SimEventModel::removeEvents(QModelIndex index)
{

	int row = index.row();

	for (int r = row+1; r <= (eventDataMap.count() - 1); r++) // not the first row
	{
		eventMap.remove(eventDataMap[r]->step, eventDataMap[r]->ev);
		eventMap.insert(eventDataMap[r]->step - 1, eventDataMap[r]->ev);
	}

	int cnt = eventMap.remove(eventDataMap[row]->step,eventDataMap[row]->ev);
	//Remove unused warning in release
	cnt = cnt;
	assert(cnt==1 || cnt==0);

	QMultiMap<unsigned long, const ProjectEvent*> newEvMap;
	newEvMap.clear();
	newEvMap = eventMap;
	setEventMap(newEvMap);
}

bool SimEventModel::moveUp(QModelIndex index)
{
	int row=index.row();
	if (row >= 1 && row <= (eventDataMap.count() - 1)) // not the first row
	{
		eventMap.remove(eventDataMap[row]->step, eventDataMap[row]->ev);
		eventMap.remove(eventDataMap[row-1]->step, eventDataMap[row-1]->ev);
		eventMap.insert(eventDataMap[row]->step - 1, eventDataMap[row]->ev);
		eventMap.insert(eventDataMap[row-1]->step + 1, eventDataMap[row-1]->ev);
		QMultiMap<unsigned long, const ProjectEvent*> newEvMap;
		newEvMap.clear();
		newEvMap = eventMap;
		setEventMap(newEvMap);
		return true;
	}
	return false;

}

bool SimEventModel::moveDown(QModelIndex index)
{
	int row=index.row();
	if (row >= 0 && row < (eventDataMap.count() - 1)) // not the last row
	{
		eventMap.remove(eventDataMap[row]->step, eventDataMap[row]->ev);
		eventMap.remove(eventDataMap[row+1]->step, eventDataMap[row+1]->ev);
		eventMap.insert(eventDataMap[row]->step + 1, eventDataMap[row]->ev);
		eventMap.insert(eventDataMap[row+1]->step - 1, eventDataMap[row+1]->ev);
		QMultiMap<unsigned long, const ProjectEvent*> newEvMap;
		newEvMap.clear();
		newEvMap = eventMap;
		setEventMap(newEvMap);
		return true;
	}
	return false;

}

const ProjectEvent* SimEventModel::getProjectEvent(int row)
{
	return eventDataMap[row-1]->ev;
}
}
