/*	Author:		Magdin Stoica
	Supervisor: Dr. Ryan Leduc
	
	Project created in conformity with the requirements for the Degree of Master of Engineering in Software Engineering, 
	Computing and Software Department, 
	McMaster University
	2003 - 2007
*/

#include <QVariant>
#include <QFile>
#include <QTextStream>
#include <QTextCodec>

#include "DesPrinter.h"
#include "Des.h"

namespace DESpot
{

const QString DesPrinter::cFileHeader = "\n====================================== %1 =============================================\n\n";
const QString DesPrinter::cStateHeader = "\n--------------------------------------- States ---------------------------------------------\n\n";
const QString DesPrinter::cEventHeader = "\n--------------------------------------- Events ---------------------------------------------\n\n";
const QString DesPrinter::cTransitionHeader = "\n------------------------------------- Transitions ---------------------------------------------\n\n";

const QString DesPrinter::cDesNameLabel = "DES Name:  ";

const QString DesPrinter::cDesTypeLabel = "DES Type:  ";
const QString DesPrinter::cDesTypeVal_Regular = "Regular";
const QString DesPrinter::cDesTypeVal_Subsystem = "Subsystem";
const QString DesPrinter::cDesTypeVal_Interface = "Interface";

const QString DesPrinter::cDesFileNameLabel = "DES File Name:    ";
const QString DesPrinter::cDesFileVersionLabel = "DES File Version: ";
const QString DesPrinter::cDesFileVersion = "1.0";

const QString DesPrinter::cDesIntegLabel = "DES Integrity Status:  ";
const QString DesPrinter::cDesIntegDateLabel = "Date Integrity Stamp:  ";
const QString DesPrinter::cDesIntegDatePrefix = "Integrity was verified on ";
const QString DesPrinter::cDesIntegStatusVal_Yes = "DES is valid";
const QString DesPrinter::cDesIntegStatusVal_No = "DES is invalid";
const QString DesPrinter::cDesIntegStatusVal_NotVerified = "Integrity has not been verified";

const QString DesPrinter::cDesStateMarkedVal_Yes = "Yes";
const QString DesPrinter::cDesStateMarkedVal_No = "No";
const QString DesPrinter::cDesStateReachableVal_Unknown = "Unknown";
const QString DesPrinter::cDesStateReachableVal_Yes = "Yes";
const QString DesPrinter::cDesStateReachableVal_No = "No";
const QString DesPrinter::cDesEventCtrlVal_Yes = "Yes";
const QString DesPrinter::cDesEventCtrlVal_No = "No";
const QString DesPrinter::cDesEventTypeVal_Def = "Default";
const QString DesPrinter::cDesEventTypeVal_Req = "Request";
const QString DesPrinter::cDesEventTypeVal_Ans = "Answer";
const QString DesPrinter::cDesEventTypeVal_Ld = "Low Data";

const QString DesPrinter::cStateNameLabel = "State Name:   ";
const QString DesPrinter::cInitStateLabel = "Initial State: ";
const QString DesPrinter::cStateCountLabel = "Number of states: ";
const QString DesPrinter::cMarkedCountLabel = "Number of marked states: ";
const QString DesPrinter::cStateAliasLabel = "State Alias:  ";
const QString DesPrinter::cStateMarkedLabel = "Marked:       ";
const QString DesPrinter::cNoInitialState = "Initial state not defined!";

const QString DesPrinter::cEventCountLabel = "Number of Events: ";
const QString DesPrinter::cEventCtrlCountLabel = "Controllable:   ";
const QString DesPrinter::cEventUnctrlCountLabel = "Uncontrollable:   ";
const QString DesPrinter::cEventDefCountLabel = "                           Default:        ";
const QString DesPrinter::cEventReqCountLabel = "    Request:          ";
const QString DesPrinter::cEventAnsCountLabel = "\n                           Answer:         ";
const QString DesPrinter::cEventLdataCountLabel = "    Low Data:         ";
const QString DesPrinter::cEventNameLabel = "Event Name:    ";
const QString DesPrinter::cEventAliasLabel = "Event Alias:   ";
const QString DesPrinter::cEventCtrlLabel = "Controllable:  ";
const QString DesPrinter::cEventTypeLabel = "Type:          ";

const QString DesPrinter::cTransCountLabel = "Number of Transitions: ";
const QString DesPrinter::cSelfTransCountLabel = "                 Self-Looped Events: ";
const QString DesPrinter::cSelfTransLabel = "The following events are self-looped at every state:";



//_________________________________________________________________________________________________

DesPrinter::DesPrinter(const Des& des) : m_des(des)
{
}

//_________________________________________________________________________________________________

DesPrinter::~DesPrinter(void)
{
}

//_________________________________________________________________________________________________

void DesPrinter::print(const std::wstring& fileName)
{
	QFile file(QString::fromStdWString(fileName));
	if (!file.open(QIODevice::WriteOnly|QIODevice::Text))
	{
		QString error("Cannot open file %1 for writing");
		error = error.arg(QString::fromStdWString(fileName));
		throw error.toStdWString();
	}

	QTextStream out(&file);
	out.setCodec( QTextCodec::codecForName("UTF-8") );

	//print the contents of DEs in the text stream
	printDes(out);
}

//_________________________________________________________________________________________________

void DesPrinter::print(QTextStream& out)
{
	printDes(out);
}

//_________________________________________________________________________________________________

void DesPrinter::printDes(QTextStream& out)
{
	printDesHeader(out);
	printDesStates(out);
	printDesEvents(out);
	printDesTransitions(out);
}

//_________________________________________________________________________________________________

void DesPrinter::printDesHeader(QTextStream& out)
{
	out << cFileHeader.arg(toString(m_des.getName()));

	out << cDesNameLabel << toString(m_des.getName()) << "\n";
	out << cDesTypeLabel << getDesType() << "\n";
	out << "\n";
	
	out << cDesIntegLabel << getDesIntegrity() << "\n";
	if (m_des.getIntegrity() != eIntegNotVerified)
	{
		out << cDesIntegDateLabel << cDesIntegDatePrefix << toString(m_des.getIntegrityStamp()) << "\n";
	}
	else
	{
		out << cDesIntegDateLabel << "-\n";
	}
	out << "\n";

	out << cDesFileNameLabel << toString(m_des.getFileName()) << "\n";
	out << cDesFileVersionLabel << cDesFileVersion << "\n";
	out << "\n";
	out << "\n";
}

//_________________________________________________________________________________________________

void DesPrinter::printDesStates(QTextStream& out)
{
	out << cStateHeader;

	out << cInitStateLabel << toString(m_des.getInitialState().getName()) << "      ";
	out << cStateCountLabel << toString(m_des.getStateCount()) << "      ";
	out << cMarkedCountLabel << toString(m_des.getMarkedStateCount());
	out << "\n\n";

	if (m_des.hasInitialState())
	{				
		//print the initial state first
		printState(out, m_des.getInitialState());
	}
	else
	{
		out << cInitStateLabel << cNoInitialState << "\n";
	}
	out << "\n";
			
	Des::StateIteratorPtr stateIt = m_des.createStateIterator();
	for(stateIt->first(); stateIt->notDone(); stateIt->next())
	{
		const DesState& state = stateIt->currentItem();
		
		if (state.isInit() == false)
		{
			printState(out, state);
			out << "\n";
		}
	}
}

//_________________________________________________________________________________________________

void DesPrinter::printState(QTextStream& out, const DesState& state)
{
	out << cStateNameLabel << toString(state.getName()) << "\n";
	out << cStateAliasLabel << toString(state.getAlias()) << "\n";
	out << cStateMarkedLabel << getStateMarkStatus(state) << "\n";
}

//_________________________________________________________________________________________________

void DesPrinter::printDesEvents(QTextStream& out)
{
	out << cEventHeader;
	
	out << cEventCountLabel << toString(m_des.getEventCount()) << "        ";
	out << cEventCtrlCountLabel << toString(m_des.getEventCountByCtrl(true)) << "        ";
	out << cEventUnctrlCountLabel << toString(m_des.getEventCountByCtrl(false)) << "\n";
	out << cEventDefCountLabel << toString(m_des.getEventCountByType(eDefaultEvent)) << "    ";
	out << cEventReqCountLabel << toString(m_des.getEventCountByType(eRequestEvent)) << "    ";
	out << cEventAnsCountLabel << toString(m_des.getEventCountByType(eAnswerEvent)) << "    ";
	out << cEventLdataCountLabel << toString(m_des.getEventCountByType(eLDataEvent)) << "\n\n";

	Des::EventIteratorPtr eventIt = m_des.createEventIterator();
	for(eventIt->first(); eventIt->notDone(); eventIt->next())
	{
		const DesEvent& event = eventIt->currentItem();
		printEvent(out, event);
		out << "\n";
	}
}

//_________________________________________________________________________________________________

void DesPrinter::printEvent(QTextStream& out, const DesEvent& event)
{
	out << cEventNameLabel << toString(event.getName()) << "\n";
	out << cEventAliasLabel << toString(event.getAlias()) << "\n";
	out << cEventCtrlLabel << getEventCtrlStatus(event) << "\n";
	out << cEventTypeLabel << getEventType(event) << "\n";
}

//_________________________________________________________________________________________________

void DesPrinter::printDesTransitions(QTextStream& out)
{
	out << cTransitionHeader;

	out << cTransCountLabel << toString(m_des.getTransCount(false));
	out << cSelfTransCountLabel << toString(m_des.getSelfTransCount()) << "\n\n";

	Des::TransIteratorPtr transIt = m_des.createTransIterator(true);
	for(transIt->first(); transIt->notDone(); transIt->next())
	{
		const DesTransition& trans = transIt->currentItem();
		printTransition(out, trans);
	}

	//print the self-looped events
	if (m_des.getSelfTransCount() > 0)
	{
		out << "\n\n" + cSelfTransLabel << "\n";

		Des::SelfTransIteratorPtr selfTransIt = m_des.createSelfTransIterator();
		for(selfTransIt->first(); selfTransIt->notDone(); selfTransIt->next())
		{
			const DesEvent& selfLoopEvent = selfTransIt->currentItem();
			out << "     - " << toString(selfLoopEvent.getName()) << "\n";
		}
	}
}

//_________________________________________________________________________________________________

void DesPrinter::printTransition(QTextStream& out, const DesTransition& trans)
{
	QString transPrint;
	transPrint.sprintf("%-15s ;     %-15s     ->        %s", toString(trans.fromState().getName()).toAscii().data(),
												toString(trans.event().getName()).toAscii().data(),
												toString(trans.toState().getName()).toAscii().data());

	out <<  transPrint << "\n";
}

//_________________________________________________________________________________________________

QString DesPrinter::toString(const std::wstring& stdString)
{
	return QString::fromStdWString(stdString);
}

//_________________________________________________________________________________________________

QString DesPrinter::toString(unsigned long long value)
{
	return (QVariant(value)).toString();
}

//_________________________________________________________________________________________________

QString DesPrinter::getDesType()
{
	switch(m_des.getType())
	{
		case eRegularDes:
			return cDesTypeVal_Regular;

		case eSubsystemDes:
			return cDesTypeVal_Subsystem;

		case eInterfaceDes:
			return cDesTypeVal_Interface;

		default:
			assert(false);
			return "";
	}
}


//_________________________________________________________________________________________________

QString DesPrinter::getDesIntegrity()
{
	switch(m_des.getIntegrity())
	{
		case eIntegYes:
			return cDesIntegStatusVal_Yes;

		case eIntegNotVerified:
			return cDesIntegStatusVal_NotVerified;

		case eIntegNo:
			return cDesIntegStatusVal_No;
		
		default:
			assert(false);
			return "";
	}
}

//_________________________________________________________________________________________________

QString DesPrinter::getStateMarkStatus(const DesState& state)
{
	return (state.isMarked() ? cDesStateMarkedVal_Yes : cDesStateMarkedVal_No);
}

//_________________________________________________________________________________________________

QString DesPrinter::getStateReachStatus(const DesState& state)
{
	return state.isReachKnown() ? 
				(state.isReachable() ? cDesStateReachableVal_Yes : cDesStateReachableVal_No) : 
				cDesStateReachableVal_Unknown;
}

//_________________________________________________________________________________________________

QString DesPrinter::getEventCtrlStatus(const DesEvent& event)
{
	return (event.isControllable() ? cDesEventCtrlVal_Yes : cDesEventCtrlVal_No);
}

//_________________________________________________________________________________________________

QString DesPrinter::getEventType(const DesEvent& event)
{
	switch(event.getType())
	{
		case eDefaultEvent:
			return cDesEventTypeVal_Def;

		case eRequestEvent:
			return cDesEventTypeVal_Req;

		case eAnswerEvent:
			return cDesEventTypeVal_Ans;

		case eLDataEvent:
			return cDesEventTypeVal_Ld;

		default:
			assert(false);
			return "";			
	}
}

} //end of namespace DESpot
