/*************************************************************************
 * 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
   SimConfigWizard.cpp - DES SIMulation Configuration wizard. 
 FUNCTION
 NOTES
 MODIFIED
   xma	    07/01/08 - CREATION 
*/


#include <QtGui>

#include "SimConfigWizard.h"
#include "SimConfig.h"
#include "DesHierProject.h"
#include "DesFlatProject.h"
#include "SimCompState.h"
#include "SimStateModel.h"
#include "SimStateDelegate.h"
#include "SimStateParser.h"
#include "ProjEventSetWidget.h"
#include "SimEventModel.h"
#include "SimEventChooser.h"

namespace DESpot
{

SimConfigWizard::SimConfigWizard(SimConfig *simConfig,QWidget *parent)
    : QWizard(parent), simConfig(simConfig)
{
	desProject = simConfig->project();
	switch(desProject ->getType()) {
        case eFlatProject:
			flatProject = static_cast<DesFlatProject*>(desProject);
			hierProject = 0;
			break;

        case eHierProject:
			hierProject = static_cast<DesHierProject*>(desProject);
			flatProject = 0;			
			break;

		default:
			assert(false);
			return;
	}

	addPage(new IntroductionPage);
    simTypePg = new SimTypePage(simConfig);
	initStateTuplePg = new InitStateTuplePage(simConfig);
	simEventSetPg = new SimEventSetPage(simConfig);
	optionsPg = new OptionsPage;

	addPage(simTypePg);
	addPage(initStateTuplePg);
    addPage(simEventSetPg);
	addPage(optionsPg);

    setPixmap(QWizard::BannerPixmap, QPixmap(":/resources/sim_banner.png"));
    setPixmap(QWizard::BackgroundPixmap, QPixmap(":/resources/sim_background.png"));

	setWindowTitle(tr("DES Simulation: Configuration Wizard"));
	//setAttribute(Qt::WA_DeleteOnClose);
	//setMinimumSize(640,480);

	/*
	setButtonText(QWizard::CustomButton1, tr("&Accept Defaults"));
    setOption(QWizard::HaveCustomButton1, true);
    connect(this, SIGNAL(customButtonClicked(int)),
            this, SLOT(printButtonClicked())); 
			*/
	
}

SimConfigWizard::~SimConfigWizard(void)
{
	delete simTypePg;
	delete initStateTuplePg;
	delete simEventSetPg;
	delete optionsPg;
}

void SimConfigWizard::accept()
{
	/* We need the following, the same as SimWorkspace::onRunSim*/
	/*
						stateModel->getGlobalState(),			//Init state
						selectedEventModel->getEventMap(),		//Event set
						simConfig->getSimMode(),				//Simulate Hier or Flat
						simConfig->getEventSetMode(),			//Is event ordered or not
						simConfig->getStopCondition());			//Stop condition

		SimAlg( DesProject* desProject,										//Project 
				SimWorkspace* configWorkspace,
			    SimCompState::StateTuple* globalState,							//Init state
			    QMultiMap<unsigned long, const ProjectEvent*> eventSet,		//Event set
				SimConfig::SimMode simMode,
				SimConfig::EventSetMode eventSetMode,
				SimStopCondition* stopCondition);							//Stop condition

		void setConfig(	SimMode simMode,
			EventSetMode eventSetMode,
			SimStopCondition* stopCondition,
			SimCompState::StateTuple* initStateTuple,	//Init state
			ProjectEventSet eventSet);						//Event set
	*/

	simConfig->setConfig(simTypePg->simMode(),
						 simTypePg->eventSetMode(),
						 optionsPg->stopCondition(),
						 initStateTuplePg->initStateTuple(),
						 simEventSetPg->eventSet()
						 );
						
    QDialog::accept();
}

IntroductionPage::IntroductionPage(QWidget *parent)
    : QWizardPage(parent)
{
    setTitle(tr("Introduction"));

    label = new QLabel(tr("This wizard will setup configuration for "
                          "the current simulation task, including initial state "
                          "tuple, event set to be simulated, simulation options "
                          "and stop conditions. "
						  "Clicking Cancel anytime will exit wizard, setting "
						  "all choices to default values"));
    label->setWordWrap(true);

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(label);
    setLayout(layout);
}

SimTypePage::SimTypePage(SimConfig* simConfig,QWidget *parent)
    : QWizardPage(parent)
{
    setTitle(tr("Choose Simulation Type and Event Set Type"));
	QString subtitle=QString(tr("Specify simulation type and event set type."));
				//	The simulation type can either be hierarchical" 
		        //   " or simulate hierarchical model using flat algorithm. The flat project can only choose flat option." 
				//   " Event set can be ordered or not"));

	QGroupBox* simTypeGroupBox = new QGroupBox(tr("Simulation Type"));
    hierTypeRadioButton = new QRadioButton(tr("&Hierarchical HISC model"));
    hierAsFlatTypeRadioButton = new QRadioButton(tr("&Hierarchical as Flat model"));
	flatTypeRadioButton = new QRadioButton(tr("&Flat model"));

	//Disable simulation type if flat project
    if(simConfig->getProjectType() == eFlatProject){
		    hierTypeRadioButton->setEnabled(false);
			hierAsFlatTypeRadioButton->setEnabled(false);
			flatTypeRadioButton->setEnabled(true);
			flatTypeRadioButton->setChecked(true);
	}else{
		    hierTypeRadioButton->setEnabled(true);
			hierAsFlatTypeRadioButton->setEnabled(true);
			flatTypeRadioButton->setEnabled(false);
			hierTypeRadioButton->setChecked(true);
			if (!simConfig->isProjInerfaceConsistent())
			{
				subtitle.append(tr(" NOTE: The project is either not interface "
				                   "consistent or the interface consistent property "
								   "has not been checked. Only flat mode simulation "
								   "is available."));
				hierTypeRadioButton->setEnabled(false);
				hierTypeRadioButton->setChecked(false);
				hierAsFlatTypeRadioButton->setChecked(true);
			}

	}

	setSubTitle(subtitle);

	QGroupBox* evTypeGroupBox = new QGroupBox(tr("Event Set Type"));
	seqModeRadioButton = new QRadioButton(tr("Simulate an event sequence"));
	setModeRadioButton = new QRadioButton(tr("Simulate an event set"));
	setModeRadioButton->setChecked(true);

	registerField("simHierMode", hierTypeRadioButton);
    registerField("simHierAsFlatMode", hierAsFlatTypeRadioButton);
	registerField("simFlatMode", flatTypeRadioButton);
	registerField("simSeqMode", seqModeRadioButton);
	registerField("simSetMode", setModeRadioButton);

    QVBoxLayout *typeBoxLayout = new QVBoxLayout;
    typeBoxLayout->addWidget(hierTypeRadioButton);
    typeBoxLayout->addWidget(hierAsFlatTypeRadioButton);
	typeBoxLayout->addWidget(flatTypeRadioButton);
    simTypeGroupBox->setLayout(typeBoxLayout);
	
    QVBoxLayout *evTypeBoxLayout = new QVBoxLayout;
    evTypeBoxLayout->addWidget(setModeRadioButton);
    evTypeBoxLayout->addWidget(seqModeRadioButton);
	evTypeGroupBox->setLayout(evTypeBoxLayout);

	QGridLayout *layout = new QGridLayout;
    layout->addWidget(simTypeGroupBox,0,0);
	layout->addWidget(evTypeGroupBox,1,0);

	setLayout(layout);
	
}

SimConfig::SimMode SimTypePage::simMode()
{
	if (hierTypeRadioButton->isChecked() && hierTypeRadioButton->isEnabled())
		return SimConfig::HIER;
	else
		return SimConfig::FLAT;
}

SimConfig::EventSetMode SimTypePage::eventSetMode()
{
	if (setModeRadioButton->isChecked())
		return SimConfig::SET;
	else
		return SimConfig::SEQ;
}

InitStateTuplePage::InitStateTuplePage(SimConfig* simConfig,QWidget *parent)
    : QWizardPage(parent),simConfig(simConfig)
{
    setTitle(tr("Choose Initial State Tuple"));
    setSubTitle(tr("Specify the starting state tuple to simulate. "
                   "The default starting point is the initial state tuple."));

	DesProject* project = simConfig->project();

	SimCompState::StateTuple initState = SimCompState::getInitGlobalState(project);
	//SimStateModel stateModel(initState,this); 
	stateModel= new SimStateModel(initState,this);
	startStatesView = new QTreeView(this);
	startStatesView->setModel(stateModel);
	SimStateDelegate *stateDelegate = new SimStateDelegate(1);
	startStatesView->setItemDelegate(stateDelegate);
	SimStateParser parser(project);

	Node *rootNode;
	if (field("simHierMode").toBool()) 
		rootNode = parser.parse(&initState,SimStateParser::Hier);
	else 
		rootNode = 	parser.parse(&initState,SimStateParser::Flat);
	
    stateModel->setRootNode(rootNode);
	startStatesView->expandAll();
	startStatesView->resizeColumnToContents(0);
	startStatesView->resizeColumnToContents(1);
	startStatesView->setColumnWidth(1,160);
	startStatesView->resizeColumnToContents(2);
	startStatesView->resizeColumnToContents(3);

	//connect(stateModel,SIGNAL(dataChanged()),
	//	this,SLOT(stateChanged()));

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(startStatesView);
    setLayout(layout);
}

SimCompState::StateTuple InitStateTuplePage::initStateTuple()
{
	return stateModel->getGlobalState();
}

void InitStateTuplePage::stateChanged()
{
	DesProject* project = simConfig->project();

	SimCompState::StateTuple initState = stateModel->getGlobalState();

	SimStateParser parser(project);

	Node *rootNode;
	if (field("simHierMode").toBool()) 
		rootNode = parser.parse(&initState,SimStateParser::Hier);
	else 
		rootNode = parser.parse(&initState,SimStateParser::Flat);
	
    stateModel->setRootNode(rootNode);
	startStatesView->expandAll();
	startStatesView->resizeColumnToContents(0);
	startStatesView->resizeColumnToContents(1);
	startStatesView->setColumnWidth(1,160);
	startStatesView->resizeColumnToContents(2);
	startStatesView->resizeColumnToContents(3);
}


SimEventSetPage::SimEventSetPage(SimConfig* simConfig, QWidget *parent)
    : QWizardPage(parent),simConfig(simConfig)
{

	this->project = simConfig->project();

	SimConfig::ProjectEventSet evMap = simConfig->getCompleteProjEventSet();
	selectedEventModel = new SimEventModel(this);
	selectedEventModel->setEventMap(evMap);

	// A set or a sequence
	if (field("simSeqMode").toBool()) 
		selectedEventModel->setOrder(true);	
	else 
		selectedEventModel->setOrder(false);

	selEventTab = new QTableView(this);
	selEventTab->setModel(selectedEventModel);
	selEventTab->resizeColumnsToContents();
	selEventTab->resizeRowsToContents();
	selEventTab->setAlternatingRowColors(true);
	selEventTab->setSelectionBehavior(QAbstractItemView::SelectRows);

	customizeButton = new QPushButton(tr("Edit Event Set"),this);


	connect(customizeButton, SIGNAL(clicked()),this, SLOT(onCustomizeEvents()));

	QHBoxLayout *buttonLayout = new QHBoxLayout;
	buttonLayout->addStretch();
	buttonLayout->addWidget(customizeButton);

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(selEventTab,0,0);
    layout->addLayout(buttonLayout,1,0);
    setLayout(layout);
}

void SimEventSetPage::initializePage()
{
	if (field("simSeqMode").toBool()){
		setTitle(tr("Choose Event Sequence"));
		setSubTitle(tr("Specify an event sequence to simulate. The default setting "
					"is all available events, ordered. Click 'Edit Event Sequence' button to change the set."));
		customizeButton->setText("Edit Event Sequence");
	}
	else
	{
		setTitle(tr("Choose Event Set"));
		setSubTitle(tr("Specify an event set to simulate. The default setting "
					"is all available events. Click 'Edit Event Set' button to change the set."));
		customizeButton->setText("Edit Event Set");
	}

}

SimConfig::ProjectEventSet SimEventSetPage::eventSet()
{
	return selectedEventModel->getEventMap();
}

void SimEventSetPage::onCustomizeEvents()
{
	/*
    if (simConfig->getProjectType()==eHierProject){
		DesHierProject* hierProject = static_cast<DesHierProject*>(project);
		SimEventChooser eventChooser(hierProject,eventSet(),this);
		if(eventChooser.exec())
			updateSelEventTab(eventChooser.getSelectedEvents());

	}else{
		DesFlatProject* flatProject = static_cast<DesFlatProject*>(project);
		SimEventChooser eventChooser(flatProject,eventSet(),this);
		if(eventChooser.exec())
			updateSelEventTab(eventChooser.getSelectedEvents());

	}
	*/
	SimConfig::EventSetMode eventSetMode;
	if (field("simSeqMode").toBool()) 
		eventSetMode=SimConfig::SEQ;
	else
		eventSetMode=SimConfig::SET;

	SimEventChooser eventChooser
		(simConfig->project(),eventSet(),eventSetMode,this);
	if(eventChooser.exec())
		updateSelEventTab(eventChooser.getSelectedEvents());

}

void SimEventSetPage::updateSelEventTab(SimConfig::ProjectEventSet eventSet)
{
	selectedEventModel->setEventMap(eventSet);
	//selectedEventModel->reset();	
}

void SimEventSetPage::onAddAllEvents()
{
}
void SimEventSetPage::onAddEvents()
{
//	QList<const ProjectEvent*> selEvents = availEventTree->getSelectedEvents();
/*
	int t=selEvents.count();
	const ProjectEvent *e = selEvents[t-1];

	//Get current selected row#. If multi-select, get max row#
	//Will insert below the selected rows
	QItemSelectionModel *selectionModel = selEventTab->selectionModel();
	QModelIndexList indexes = selectionModel->selectedIndexes();
	QModelIndex index;
	int row=-1;
	foreach(index, indexes) {
	 if (index.row() > row)
		 row = index.row();
	} 

	selectedEventModel->addEvents(selEvents, row);
	*/
}

void SimEventSetPage::onRemoveEvents()
{
	//Get current selected row#. 
	/*
	QItemSelectionModel *selectionModel = selEventTab->selectionModel();
	QModelIndexList indexes = selectionModel->selectedIndexes();
	int i=indexes.count();
	selectedEventModel->removeEvents(indexes);
	*/

}

OptionsPage::OptionsPage(QWidget *parent)
    : QWizardPage(parent)
{
    setTitle(tr("Choose stop condition"));
	setSubTitle(tr("Specify when the simulation should stop."));

	QGroupBox* stopGroupBox = new QGroupBox(tr("Stop condition type"));
    unlimitRadioButton = new QRadioButton(tr("No Stop condition"));
	stepRadioButton = new QRadioButton(tr("After number of steps: "));
	markedStateRadioButton = new QRadioButton(tr("When a marked state is reached"));
	initStateRadioButton = new QRadioButton(tr("When the initial state is reached"));

	unlimitRadioButton->setChecked(true);

	QValidator *validator = new QIntValidator(1, 999999, this);
	stepLineEdit = new QLineEdit(this);
	stepLineEdit->setMaxLength(10);
	stepLineEdit->setValidator(validator);
	stepLineEdit->setText("1");

	registerField("unlimitCondition", unlimitRadioButton);
    registerField("stepsCondition", stepRadioButton);	
	registerField("markedStateCondition", markedStateRadioButton);
    registerField("initStateCondition", initStateRadioButton);

	QHBoxLayout *stepLayout = new QHBoxLayout;
	stepLayout->addWidget(stepRadioButton);
	stepLayout->addWidget(stepLineEdit);
	stepLayout->addStretch(1);

    QVBoxLayout *groupBoxLayout = new QVBoxLayout;
    groupBoxLayout->addWidget(unlimitRadioButton);
    groupBoxLayout->addLayout(stepLayout);
    groupBoxLayout->addWidget(markedStateRadioButton);
    groupBoxLayout->addWidget(initStateRadioButton);

    stopGroupBox->setLayout(groupBoxLayout);

	QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(stopGroupBox);
    setLayout(layout);
}

SimStopCondition OptionsPage::stopCondition()
{
	SimStopCondition condition;
	if (unlimitRadioButton->isChecked())
		condition.setOption(SimStopCondition::UNLIMITED);
	if (stepRadioButton->isChecked()){
		condition.setOption(SimStopCondition::STEP);
		condition.setStep(stepLineEdit->text().toInt());
	}
	if (markedStateRadioButton->isChecked())
		condition.setOption(SimStopCondition::REACH_MARKED);
	if (initStateRadioButton->isChecked())
		condition.setOption(SimStopCondition::REACH_INIT);
	return condition;
}
}
