/*************************************************************************
 * This file is part of DES simulation 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
   SimConfig.cpp - Simulation algorithm 
 FUNCTION
   Implement simulation configuration
 NOTES
   
 MODIFIED
   xma		04/01/08 - Integrate to DESpot
   xma	    09/01/07 - CREATION. 
*/
#include <QtGui>

#include "CommonDefinitions.h"
#include "SimConfig.h"
#include "MainForm.h"
#include "EventPoolForm.h"
#include "SimWorkspace.h"
#include "DesEditor.h"
#include "NameValidator.h"
#include "MeetAlgo.h"
#include "SyncAlgo.h"
#include "CtrlStdAlgo.h"
#include "OutputUiPart.h"
#include "SimConfigPrinter.h"
#include "ProjIntegrityAlgo.h"
#include "WaitCursor.h"
#include "NonBlockingAlgo.h"
#include "MultiCtrlAlgo.h"
#include "ProjectEditor.h"
#include "Des.h"

#include "GedDesEditor.h"
#include "SimConfigWizard.h"
#include "DesProject.h"
#include "DesFlatProject.h"
#include "DesHierProject.h"

namespace DESpot
{

SimStopCondition::SimStopCondition(StopOptions op)
{
	option = op;
}

/*
SimConfig::SimConfig(MainForm* pMainForm, FormType formType) : 
			DespotForm(pMainForm, this, pMainForm, formType), 
			workSpace(null), currentDes(null)
{
	//set form attributes
	setAttribute(Qt::WA_DeleteOnClose);

	eventPoolForm = null;
	desProject = null;
}
*/

SimConfig::SimConfig(DesProject * project, MainForm* pMainForm, ProjectEditor* projEditorOwner, FormType formType) : 
		DespotForm(pMainForm, this, projEditorOwner, formType), 
		desProject(project), workSpace(null)
{
	//set form attributes
	setAttribute(Qt::WA_DeleteOnClose);

	eventPoolForm = null;

	switch(project->getType()) {
        case eFlatProject:
			flatProject = static_cast<DesFlatProject*>(project);
			hierProject = 0;
			break;
	
        case eHierProject:
			hierProject = static_cast<DesHierProject*>(project);
			flatProject = 0;			
			break;

		default:
			assert(false);
			return;
		}

	//createDefaultConfig();
	setModified(false);
	workSpace = new SimWorkspace(this, this->project());
	setAttribute(Qt::WA_DeleteOnClose);
	setTraceFileName();
}

void SimConfig::setTraceFileName()
{
	
	QDate date = QDate::currentDate();
	
	QFileInfo finfo = QFileInfo(QString::fromStdWString(desProject->getFileName()));
	
	QString fileName=finfo.absolutePath();
	fileName.append("/trace/");
	QDir dir(fileName);
	if (!dir.exists())
		dir.mkpath(fileName);

	fileName.append(QString::fromStdWString(desProject->getName()));
	fileName.append("_");
	fileName.append(date.toString("dd-MM-yyyy"));
	fileName.append(".trc");
	
	traceFileName = fileName;

}

SimConfig::~SimConfig(void)
{
	if (workSpace)
	{
		delete workSpace;
		workSpace = null;
	}
}

void SimConfig::createDefaultConfig()
{
	// Default setting
    if 	(desProject->getType() == eFlatProject)
		simMode = FLAT;
	else
		if (isProjInerfaceConsistent())
			simMode = HIER;
		else
			simMode = FLAT;

	
	eventSetMode = SET;
	stopCondition.setOption(SimStopCondition::UNLIMITED);
	initStateTuple = SimCompState::getInitGlobalState(desProject);
	eventSet = getCompleteProjEventSet();

}

void SimConfig::initSimConfig()
{
	if (okToContinue())
	{
		SimConfigWizard mywizard(this,this);
		if(!mywizard.exec())//cancel wizard
			createDefaultConfig();
		QApplication::setOverrideCursor(Qt::WaitCursor);
		workSpace->configChanged();
		eventFile.resize(0);
		QApplication::restoreOverrideCursor();
	}

}


DesProject* SimConfig::project()
{
	return desProject;
}

bool SimConfig::eventFilter(QObject* target, QEvent* event)
{
	if (event->type() == QEvent::Destroy)
	{
		if (target == (QObject*)eventPoolForm)
		{
			//the event pool is being destroyed; release the pointer
			eventPoolForm = null;
		}
	}

	return QMainWindow::eventFilter(target, event);
}

/*
UpdateProgressInterface* SimConfig::progressInterface()
{
	return static_cast<UpdateProgressInterface*>(workSpace->progressWidget());
}
*/


void SimConfig::updateWindowTitle()
{
	QString projName = QString::fromStdWString(desProject->getName());
	QString projFileName = QString::fromStdWString(desProject->getFileName());
	QString windowTitle;
	
	if (projFileName.isEmpty())
	{
		windowTitle = projName + "*" + STR_DESPOT;
	}
	else
	{
		QFileInfo fileInfo(projFileName);

		windowTitle = projName + " - " + fileInfo.fileName() + " - " + fileInfo.path();
	}
	setWindowTitle(windowTitle);
}

void SimConfig::onOpenFile()
{
	if (okToContinue()) {

		QFileInfo finfo = QFileInfo(QString::fromStdWString(desProject->getFileName()));
		QString defaultFileName=finfo.absolutePath();

		defaultFileName += "/";
		defaultFileName += finfo.baseName();
		defaultFileName += ".sim";

        QString fileName = QFileDialog::getOpenFileName(this,
                                   tr("Open simulation event file"), defaultFileName,
                                   tr("Simulation event files (*.sim)"));
        if (!fileName.isEmpty())
            loadFile(fileName);
    }
}

bool SimConfig::okToContinue()
{
    if (isModified()) {
        int r = QMessageBox::warning(this, tr("Simulation"),
                        tr("The simulated events have been changed.\n"
                           "Do you want to save your changes?"),
                        QMessageBox::Yes | QMessageBox::Default,
                        QMessageBox::No,
                        QMessageBox::Cancel | QMessageBox::Escape);
        if (r == QMessageBox::Yes) {
            bool saved = onSaveFile();
			workSpace->cleanCurrentSim();
			return saved;
        } else if (r == QMessageBox::Cancel) {
            return false;
        }
    }
	
    return true;
}

bool SimConfig::loadFile(const QString &fileName)
{
    if (!readFile(fileName)) {
//        workSpace->statusBar()->showMessage(tr("Loading canceled"), 2000);
        return false;
    }

    setCurrentFile(fileName);
//    statusBar()->showMessage(tr("File loaded"), 2000);
    return true;
}

bool SimConfig::onSaveFile()
{
    if (eventFile.isEmpty()) {
        return onSaveAsFile();
    } else {
        return saveFile(eventFile);
    }
}

bool SimConfig::onSaveAsFile()
{
	QFileInfo finfo = QFileInfo(QString::fromStdWString(desProject->getFileName()));
	QString defaultFileName=finfo.absolutePath();

	defaultFileName += "/";
	defaultFileName += finfo.baseName();
	defaultFileName += ".sim";

	QString fileName = QFileDialog::getSaveFileName(this,
                               tr("Save simulation events"),
							   defaultFileName,
                               tr("Simulation event files (*.sim)"));
    if (fileName.isEmpty())
        return false;

	 //Check suffix. On linux, suffixed is not attached automatically
	 QStringList fields = fileName.split('.');
     if (fields.size() < 2)
		 fileName.append(".sim");

    return saveFile(fileName);
}

bool SimConfig::saveFile(const QString &fileName)
{
    if (!writeFile(fileName)) {
//        statusBar()->showMessage(tr("Saving canceled"), 2000);
        return false;
    }

    setCurrentFile(fileName);
//    statusBar()->showMessage(tr("File saved"), 2000);
    return true;
}

void SimConfig::setCurrentFile(const QString &fileName)
{
    eventFile = fileName;
    setModified(false);
/*
    QString shownName = "Untitled";
    if (!eventFile.isEmpty()) {
        shownName = strippedName(curFile);
    }
*/
//    setWindowTitle(tr("%1[*] - %2").arg(shownName)
//                                   .arg(tr("Spreadsheet")));
}

/* bin version
bool SimConfig::readFile(const QString &fileName)
{

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly)) {
        QMessageBox::warning(this, tr("Simulation Events"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        return false;
    }

    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_4_1);

    quint32 magic;
    in >> magic;
    if (magic != MagicNumber) {
        QMessageBox::warning(this, tr("Simulatoin Events"),
                             tr("The file is not a simulation event file."));
        return false;
    }

    quint16 step;
    QString eventName;

	ProjectEventSet eventSet;
	QMap<QString,ProjectEvent*> lookupTab = createEvLookupTab();

	QApplication::setOverrideCursor(Qt::WaitCursor);
    while (!in.atEnd()) {
        in >> step >> eventName;
		ProjectEvent *ev=0;
		if (lookupTab.contains(eventName))
		    ev = lookupTab.value(eventName);
		else{
	        QMessageBox::warning(this, tr("Simulatoin Events File"),
		                         tr("Event %1 is invalid. File open failed."));
			return false;
		}
			
		eventSet.insert(step,ev);    
	}
    QApplication::restoreOverrideCursor();

	this->eventSet = eventSet;
	eventSetMode = SEQ;

	workSpace->configChanged();

    return true;

}
*/

bool SimConfig::readFile(const QString &fileName)
{

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly)) {
        QMessageBox::warning(this, tr("Simulation Events"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        return false;
    }

    QTextStream in(&file);

    quint16 step;
    QString eventName;
	QString desName;
	QString stateName;

	ProjectEventSet eventSet;
	SimCompState::StateTuple startingState;
	QMap<QString,ProjectEvent*> lookupTab = createEvLookupTab();
	QMap<QString,Des*> desLookupTab = createDesLookupTab();

	QApplication::setOverrideCursor(Qt::WaitCursor);
	bool readState=true;
    while (!in.atEnd()) 
	{
		QString line = in.readLine();

		if (readState) //once we have switched to read event, we do not need to compare line by line any more
		{
			if (line == "[Starting state]")
			{
				readState = true;
				continue; //skip this line
			}

			if (line == "[Event Sequence]") //event seq start
			{
				readState = false;
				continue; //skip this line
			}
		}

		if (readState) //read des state
		{
			QStringList fields = line.split(' ');
			if (fields.size() >= 2)
			{
				desName = fields.takeFirst();
				stateName = fields.takeFirst();
				Des *des=0;
				if (desLookupTab.contains(desName))
					des = desLookupTab.value(desName);
				else
				{
					QMessageBox::warning(this, tr("Simulatoin Events File"),
										 tr("Event %1 is invalid. File may be corrupt.").arg(eventName));
					QApplication::restoreOverrideCursor();
					return false;
				}
				const DesState* foundState; 
				if (!des->findState(stateName.toStdWString(),foundState))
				{
					QMessageBox::warning(this, tr("Simulatoin Events File"),
										 tr("State %1 is invalid. File may be corrupt.").arg(stateName));
					QApplication::restoreOverrideCursor();
					return false;
				}
				
				startingState[des]=const_cast<DesState*>(foundState);
			}
		}
		else
		{
			QStringList fields = line.split(' ');
			if (fields.size() >= 2)
			{
				step = fields.takeFirst().toInt();
				eventName = fields.takeFirst();
				ProjectEvent *ev=0;
				if (lookupTab.contains(eventName))
					ev = lookupTab.value(eventName);
				else
				{
					QMessageBox::warning(this, tr("Simulatoin Events File"),
										 tr("Event %1 is invalid. File open failed.").arg(eventName));
					QApplication::restoreOverrideCursor();
					//this->eventSet.clear();
					//workSpace->configChanged();
					return false;
				}
					
				eventSet.insert(step,ev);
			}
		}
	}

	this->eventSet = eventSet;
	this->initStateTuple = startingState;
	eventSetMode = SEQ;

	workSpace->configChanged();

	QApplication::restoreOverrideCursor();

    return true;

}

bool SimConfig::writeFile(const QString &fileName)
{

    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly)) {
        QMessageBox::warning(this, tr("Simulatoin Events"),
                             tr("Cannot write file %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        return false;
    }

    QTextStream out(&file);

	ProjectEventSet eventSet = workSpace->getHistEventSeq();
    QApplication::setOverrideCursor(Qt::WaitCursor);

	out<<"[Starting state]"<<endl;
	SimCompState::StateTuple::const_iterator sit = initStateTuple.begin();
    for(; sit != initStateTuple.end() ; ++sit){
		Des* des = (sit->first);
		DesState* state = (sit->second);
		out<<QString::fromStdWString(des->getName());
		out<<" ";
		out<<QString::fromStdWString(state->getName());
		out<<endl;
	}

	out<<"[Event Sequence]"<<endl;
	ProjectEventSet::iterator it = eventSet.begin();
    for (;it != eventSet.end(); ++it) {
		ProjectEvent *ev = const_cast<ProjectEvent*>(it.value());
		QString str = QString::fromStdWString(ev->getName());
        out << quint16(it.key()) <<" " << str <<endl;
        }
    QApplication::restoreOverrideCursor();
    return true;
}

/* bin version
bool SimConfig::writeFile(const QString &fileName)
{

    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly)) {
        QMessageBox::warning(this, tr("Simulatoin Events"),
                             tr("Cannot write file %1:\n%2.")
                             .arg(file.fileName())
                             .arg(file.errorString()));
        return false;
    }

    QDataStream out(&file);
    out.setVersion(QDataStream::Qt_4_1);

    out << quint32(MagicNumber);

	ProjectEventSet eventSet = workSpace->getHistEventSeq();
    QApplication::setOverrideCursor(Qt::WaitCursor);

	ProjectEventSet::iterator it = eventSet.begin();
    for (;it != eventSet.end(); ++it) {
		ProjectEvent *ev = const_cast<ProjectEvent*>(it.value());
		QString str = QString::fromStdWString(ev->getName());
        out << quint16(it.key()) << str;
        }
    QApplication::restoreOverrideCursor();
    return true;
}
*/

void SimConfig::setConfig(	SimMode simMode,
						EventSetMode eventSetMode,
						SimStopCondition stopCondition,
						SimCompState::StateTuple initStateTuple,	//Init state
						ProjectEventSet eventSet)						//Event set
{
		this->simMode = simMode;
		this->eventSetMode = eventSetMode;
		this->stopCondition = stopCondition;
		this->initStateTuple = initStateTuple;	
		this->eventSet = eventSet;
}

SimConfig::ProjectEventSet SimConfig::getCompleteProjEventSet()
{
	ProjectEventSet evMap;

	DesProject::EventIteratorPtr eventIt = desProject->createProjEventIterator();
	int i=0;
	for(eventIt->first(); eventIt->isDone() == false; eventIt->next())
	{
		const ProjectEvent& event = eventIt->currentItem();
		evMap.insert(i,&event);
		i++;
	}

	return evMap;
}

QMap<QString,ProjectEvent*> SimConfig::createEvLookupTab()
{
	QMap<QString,ProjectEvent*> lookupTab;
	DesProject::EventIteratorPtr eventIt = desProject->createProjEventIterator();
	for(eventIt->first(); eventIt->isDone() == false; eventIt->next())
	{
		ProjectEvent& ev = eventIt->currentItem();
		lookupTab.insert(QString::fromStdWString(ev.getName()),&ev);
	}

	return lookupTab;
}

QMap<QString,Des*> SimConfig::createDesLookupTab()
{
	QMap<QString,Des*> lookupTab;

	DesProject::DesIteratorPtr desIt = desProject->createDesIterator();
	//SimCompState::StateTuple gstate;

	for(desIt->first(); desIt->isDone() == false; desIt->next())
	{
		const Des& des = desIt->currentItem();	
		//const DesState* sta = &(des.getInitialState());;
		Des* pDes = const_cast<Des*>(&des);
		lookupTab.insert(QString::fromStdWString(pDes->getName()),pDes);
		//gstate[pDes] = const_cast<DesState*>(sta);
	}
	return lookupTab;
}

ProjectType SimConfig::getProjectType() const
{
	return desProject->getType();
}


void SimConfig::closeEvent(QCloseEvent *event)
{
	if (workSpace->simRunning())
	{
		int r = QMessageBox::warning(this, tr("Exit Simulation"),
                        tr("A simulation task is currently running.\n"
                           "Do you want to stop it anyway and exit?"),
                        QMessageBox::Yes | QMessageBox::Default,
                        QMessageBox::Cancel);
        if (r == QMessageBox::Yes) 
			workSpace->onExitSimulation();
		else
		{
			event->ignore();
			return;
		}
	}

	workSpace->onFlushTraceFile(true);


/*
	if (m_pProject->isModified()) 
	{
		QString msg = STR_SAVE_MODIFIED_PROJECT(QString::fromStdWString(m_pProject->getName()));
		switch (QMessageBox::question(this, STR_DESPOT, msg, QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel))
		{		
			case QMessageBox::Yes:
				onSaveProject();
				event->accept();
				break;

			case QMessageBox::Cancel:
				event->ignore();
				return;

			case QMessageBox::No:
				event->accept();
				break;
		}		
	} 
	else
	{
		event->accept();
	}
*/
	if (okToContinue())
		event->accept();
	else
	{
		event->ignore();
		return;
	}

	 QFileInfo info(traceFileName);
	 if (info.size() > 1024*1024*50)
	 {
		 int r = QMessageBox::warning(this, tr("Large Trace File"),
                        tr("The trace file size is larger than 50MB.\n"
                           "Do you want to delete it?"),
                        QMessageBox::Yes | QMessageBox::Default,
                        QMessageBox::No);
        if (r == QMessageBox::Yes) 
		{
			QFile file(traceFileName);
			file.remove();
        }
	 }

	if (onCloseForm() == false)
		{
			event->ignore();
			return;
		}
		

}

bool SimConfig::isProjInerfaceConsistent()
{
	//Note this can only be called for HIER proj
	//DesHierProject::IConsistProp iConsProp=hierProject->getIConsistProp();
	return hierProject->getIConsistProp() == eIConsYes;
}
} //end of namespace DESpot
