/*	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 <QDir>
#include <QFileInfo>

#include "SimConfigPrinter.h"
#include "DesProject.h"
#include "DesHierProject.h"
#include "Des.h"
#include "DesSubsystem.h"
#include "DesInterface.h"

namespace DESpot
{

const QString SimConfigPrinter::cFileHeader      = "\n-------------------------------------- %1 ---------------------------------------------\n\n";
const QString SimConfigPrinter::cSubsysHeader    = "\n\n-------------------------------------- %1 ---------------------------------------------\n\n";
const QString SimConfigPrinter::cInterfaceHeader = "\n\n-------------------------------------- %1 --------------------------------------------\n\n";
const QString SimConfigPrinter::cPlantHeader = "\n-------------------------------------- Plant ---------------------------------------------\n\n";
const QString SimConfigPrinter::cSupervisorHeader = "\n------------------------------------ Supervisor ---------------------------------------------\n\n";

const QString SimConfigPrinter::cProjNameLabel = "Project Name:  ";

const QString SimConfigPrinter::cProjTypeLabel = "Project Type:  ";

const QString SimConfigPrinter::cProjTypeFlatVal = "Flat";
const QString SimConfigPrinter::cProjTypeHierVal = "HISC";

const QString SimConfigPrinter::cProjFileNameLabel = "Project File Name:    ";
const QString SimConfigPrinter::cProjFileVersionLabel = "Project File Version: ";
const QString SimConfigPrinter::cProjFileVersion = "1.0";

const QString SimConfigPrinter::cProjIntegLabel = "DES Integrity Status:  ";
const QString SimConfigPrinter::cProjIntegDateLabel = "Date Integrity Stamp:  ";
const QString SimConfigPrinter::cProjIntegDatePrefix = "Integrity was verified on ";
const QString SimConfigPrinter::cProjIntegStatusYesVal = "DES is valid";
const QString SimConfigPrinter::cProjIntegStatusNoVal = "DES is invalid";
const QString SimConfigPrinter::cProjIntegStatusNotVerifVal = "Integrity has not been verified";

const QString SimConfigPrinter::cProjPropLabel = "Project Properties:";
const QString SimConfigPrinter::cProjCtrlLabel = "   Controllable:            ";
const QString SimConfigPrinter::cProjCtrlDateLabel = "              Verified on: ";
const QString SimConfigPrinter::cProjCtrlPropYesVal = "Yes";
const QString SimConfigPrinter::cProjCtrlPropNoVal = "No";
const QString SimConfigPrinter::cProjCtrlPropNotVerifVal = "Not verified";

const QString SimConfigPrinter::cProjNonblockLabel = "                      Nonblocking:             ";
const QString SimConfigPrinter::cProjNonBlockDateLabel = "              Verified on: ";
const QString SimConfigPrinter::cProjNonBlockPropYesVal = "Yes";
const QString SimConfigPrinter::cProjNonBlockPropNoVal = "No";
const QString SimConfigPrinter::cProjNonBlockPropNotVerifVal = "Not verified";

const QString SimConfigPrinter::cLwCtrlLabel = "                   LD Level-Wise Ctrl:         ";
const QString SimConfigPrinter::cLwCtrlDateLabel = "     Verified on: ";
const QString SimConfigPrinter::cLwCtrlPropYesVal = "Yes";
const QString SimConfigPrinter::cLwCtrlPropNoVal = "No";
const QString SimConfigPrinter::cLwCtrlPropNotVerifVal = "Not verified";

const QString SimConfigPrinter::cLwNonBlockLabel = "                   LD Level-Wise Nonblocking:  ";
const QString SimConfigPrinter::cLwNonBlockDateLabel = "     Verified on: ";
const QString SimConfigPrinter::cLwNonBlockPropYesVal = "Yes";
const QString SimConfigPrinter::cLwNonBlockPropNoVal = "No";
const QString SimConfigPrinter::cLwNonBlockPropNotVerifVal = "Not verified";

const QString SimConfigPrinter::cIConsistLabel = "                      Interface Consistent:    ";
const QString SimConfigPrinter::cIConsistDateLabel = "     Verified on: ";
const QString SimConfigPrinter::cIConsistPropYesVal = "Yes";
const QString SimConfigPrinter::cIConsistPropNoVal = "No";
const QString SimConfigPrinter::cIConsistPropNotVerifVal = "Not verified";

const QString SimConfigPrinter::cDesNameLabel = "DES Name:  ";
const QString SimConfigPrinter::cDesFileNameLabel = "Location:  ";

const QString SimConfigPrinter::cSubsysNameLabel = "Subsystem Name:  ";
const QString SimConfigPrinter::cSubsysDegreeLabel = "          Level:  ";
const QString SimConfigPrinter::cSubsysPropLabel = "Subsystem Properties:  ";
const QString SimConfigPrinter::cSubsysIConsistLabel = "    Interface Consistent     ";
const QString SimConfigPrinter::cSubsysLwNonBlockLabel = " LD Level-Wise Nonblocking   ";
const QString SimConfigPrinter::cSubsysLwCtrlLabel = " LD Level-Wise Controllable  ";
const QString SimConfigPrinter::cSubsysImplLabel = "Implements";
const QString SimConfigPrinter::cSubsysUsesLabel = "Uses";
const QString SimConfigPrinter::cSubsysSupervisorLabel = "Supervisor";
const QString SimConfigPrinter::cSubsysPlantLabel = "Plant";
const QString SimConfigPrinter::cInterfUsePrefix = "    ";
const QString SimConfigPrinter::cNoInterf = "-";
const QString SimConfigPrinter::cSubsysSpacePrefix = "    ";

const QString SimConfigPrinter::cInterfSpacePrefix = "    ";
const QString SimConfigPrinter::cInterfNameLabel = "Interface Name:  ";
const QString SimConfigPrinter::cInterfDesComp = "Interface Components: ";


//_________________________________________________________________________________________________

SimConfigPrinter::SimConfigPrinter(const DesProject& project) : m_project(project)
{
}

//_________________________________________________________________________________________________

SimConfigPrinter::~SimConfigPrinter(void)
{
}

//_________________________________________________________________________________________________

void SimConfigPrinter::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 the project in the text stream
	printProject(out);
}

//_________________________________________________________________________________________________

void SimConfigPrinter::printProject(QTextStream& out)
{
	printHeader(out);
	if (isHierProject())
	{
		printHighLevelSubsys(out);
		printInterfaces(out);
		printLowLevelSubsys(out);
	}
	else
	{
		printFlatSupervisor(out);
		out << "\n";
		printFlatPlant(out);
	}
}

//_________________________________________________________________________________________________

void SimConfigPrinter::printHeader(QTextStream& out)
{
	out << cFileHeader.arg(toString(m_project.getName()));

	out << cProjNameLabel << toString(m_project.getName()) << "\n";
	out << cProjTypeLabel << getProjectType() << "\n";
	out << "\n";

	out << cProjIntegLabel << getProjIntegrity() << "\n";
    if (m_project.getIntegrity() != eIntegNotVerified)
	{
		out << cProjIntegDateLabel << cProjIntegDatePrefix << toString(m_project.getIntegrityStamp()) << "\n";
	}
	else
	{
		out << cProjIntegDateLabel << "-\n";
	}
	out << "\n";

	//print file information
	out << cProjFileNameLabel << toString(m_project.getFileName()) << "\n";
	out << cProjFileVersionLabel << cProjFileVersion << "\n";
	out << "\n";

	//print project properties (i.e. controllable, non-blocking)
	out << cProjPropLabel;
	out << cProjCtrlLabel << getCtrlProp() << cProjCtrlDateLabel << toString(m_project.getCtrlStamp()) << "\n";
	out << cProjNonblockLabel << getNonBlockProp() << cProjNonBlockDateLabel << toString(m_project.getNonBlockingStamp()) << "\n";

	if (isHierProject())
	{
		out << cIConsistLabel << getIConsistProp() << cIConsistDateLabel << toString(hierProject().getIConsistStamp()) << "\n";
		out << cLwNonBlockLabel << getLwNonBlockProp() << cLwNonBlockDateLabel << toString(hierProject().getLwNonBlockStamp()) << "\n";
		out << cLwCtrlLabel << getLwCtrlProp() << cLwCtrlDateLabel << toString(hierProject().getLwCtrlStamp()) << "\n";
	}

	out << "\n";
}

//_________________________________________________________________________________________________

void SimConfigPrinter::printFlatSupervisor(QTextStream& out)
{
	out << cSupervisorHeader;

    DesProject::DesIteratorPtr desIt = m_project.createDesIterator(eSupervisorDes);
	for(desIt->first(); desIt->notDone(); desIt->next())
	{
		const Des& des = desIt->currentItem();
		printDes(out, des);
	}
}

//_________________________________________________________________________________________________

void SimConfigPrinter::printFlatPlant(QTextStream& out)
{
	out << cPlantHeader;

    DesProject::DesIteratorPtr desIt = m_project.createDesIterator(ePlantDes);
	for(desIt->first(); desIt->notDone(); desIt->next())
	{
		const Des& des = desIt->currentItem();
		printDes(out, des);
	}
}

//_________________________________________________________________________________________________

void SimConfigPrinter::printHighLevelSubsys(QTextStream& out)
{
	printSubsystem(out, hierProject().getRootSubsys());
}


//_________________________________________________________________________________________________

void SimConfigPrinter::printLowLevelSubsys(QTextStream& out)
{
	DesHierProject::SubsysIteratorPtr subsysIt = hierProject().createSubsysIterator();
	for(subsysIt->first(); subsysIt->notDone(); subsysIt->next())
	{
		const DesSubsystem& subsys = subsysIt->currentItem();

		if (subsys.getLevel() > 0)
		{
			printSubsystem(out, subsys);
		}
	}
}

//_________________________________________________________________________________________________

void SimConfigPrinter::printSubsystem(QTextStream& out, const DesSubsystem& subsys)
{
	out << cSubsysHeader.arg(toString(subsys.getName()));

	//print subsystem properties
	out << cSubsysNameLabel << toString(subsys.getName());
	out << cSubsysDegreeLabel << toString(subsys.getLevel()) << "\n\n";
	out << cSubsysPropLabel << "\n";
	out << cSubsysIConsistLabel << getIConsistProp(subsys) << "\n";
	out << cSubsysLwNonBlockLabel << getLwNonBlockProp(subsys) << "\n";
	out << cSubsysLwCtrlLabel << getLwCtrlProp(subsys) << "\n";
	out << "\n";

	//print the information on what interface the subsytem implements
	out << cSubsysImplLabel << "\n";
	if (subsys.implementsInterface())
	{
		out << cSubsysSpacePrefix << toString(subsys.getInterface().getName()) << "\n";
	}
	else
	{
		out << cSubsysSpacePrefix << cNoInterf << "\n";
	}
	out << "\n";

	//print the information on what interfaces the subsystem uses
	out << cSubsysUsesLabel << "\n";
	if (subsys.getDependsCount() > 0)
	{
		DesSubsystem::DependIteratorPtr dependIt = subsys.createDependsIterator();
		for(dependIt->first(); dependIt->notDone(); dependIt->next())
		{
			const DesSubsystem::Dependency& depend = dependIt->currentItem();
			out << cInterfUsePrefix << toString(depend.interface->getName()) << "\n";
		}
	}
	else
	{
		out << cSubsysSpacePrefix << cNoInterf << "\n";
	}
	out << "\n";

	//print the supervisor DES
	out<< cSubsysSupervisorLabel << "\n";
    DesProject::DesIteratorPtr plantDesIt = subsys.createDesIterator(eSupervisorDes);
	for(plantDesIt->first(); plantDesIt->notDone(); plantDesIt->next())
	{
		const Des& des = plantDesIt->currentItem();

		out << cSubsysSpacePrefix;
		printDes(out, des);
	}
	out << "\n";

	//print the plant DES
	out<< cSubsysPlantLabel << "\n";
    DesProject::DesIteratorPtr supDesIt = subsys.createDesIterator(ePlantDes);
	for(supDesIt->first(); supDesIt->notDone(); supDesIt->next())
	{
		const Des& des = supDesIt->currentItem();

		out << cSubsysSpacePrefix;
		printDes(out, des);
	}
}

//_________________________________________________________________________________________________

void SimConfigPrinter::printInterfaces(QTextStream& out)
{
	DesHierProject::InterfIteratorPtr interfIt = hierProject().createInterfIterator();
	for(interfIt->first(); interfIt->notDone(); interfIt->next())
	{
		const DesInterface& interf = interfIt->currentItem();
		printInterface(out, interf);
	}
}

//_________________________________________________________________________________________________

void SimConfigPrinter::printInterface(QTextStream& out, const DesInterface& interf)
{
	out << cInterfaceHeader.arg(toString(interf.getName()));

	out << cInterfNameLabel << toString(interf.getName()) << "\n\n";
	out << cInterfDesComp << "\n";

	DesInterface::DesIteratorPtr desIt = interf.createDesIterator();
	for(desIt->first(); desIt->notDone(); desIt->next())
	{
		const Des& des = desIt->currentItem();

		out << cInterfSpacePrefix;
		printDes(out, des);
	}
}

//_________________________________________________________________________________________________

void SimConfigPrinter::printDes(QTextStream& out, const Des& des)
{
	QFileInfo projFileInfo(QString::fromStdWString(m_project.getFileName()));
	QDir projDir = projFileInfo.dir();
	QString desRelFileName = projDir.relativeFilePath(toString(des.getFileName()));

	QString desPrint;
	desPrint.sprintf("%s%-20s  %s%s", cDesNameLabel.toAscii().data(), toString(des.getName()).toAscii().data(),
										cDesFileNameLabel.toAscii().data(), desRelFileName.toAscii().data());

	out << desPrint << "\n";
}

//_________________________________________________________________________________________________

bool SimConfigPrinter::isHierProject()
{
    return (m_project.getType() == eHierProject);
}

//_________________________________________________________________________________________________

const DesHierProject& SimConfigPrinter::hierProject()
{
	if (isHierProject() == false)
	{
		throw "The project being printed is not a HISC project";
	}

	return dynamic_cast<const DesHierProject&>(m_project);
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::getProjectType()
{
	switch(m_project.getType())
	{
        case eFlatProject:
			return cProjTypeFlatVal;

        case eHierProject:
			return cProjTypeHierVal;

		default:
			assert(true);
			qWarning("Unknown project type value.");
			return "ERROR: Unknown project type";
	}
}

//_________________________________________________________________________________________________

QString	SimConfigPrinter::getProjIntegrity()
{
	switch(m_project.getIntegrity())
	{
        case eIntegYes:
			return cProjIntegStatusYesVal;

        case eIntegNo:
			return cProjIntegStatusNoVal;

        case eIntegNotVerified:
			return cProjIntegStatusNotVerifVal;

		default:
			assert(true);
			qWarning("Unknown project integrity value.");
			return "ERROR: Unknown integrity value";
	}
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::getCtrlProp()
{
	switch(m_project.getControllableProp())
	{
        case eCtrlYes:
			return cProjCtrlPropYesVal;

        case eCtrlNo:
			return cProjCtrlPropNoVal;

        case eCtrlNotVerified:
			return cProjCtrlPropNotVerifVal;

		default:
			assert(true);
			qWarning("Unknown project controllable property value.");
			return "ERROR: Unknown controllable property value";
	}
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::getNonBlockProp()
{
	switch(m_project.getNonBlockingProp())
	{
        case eNonBlockYes:
			return cProjNonBlockPropYesVal;

        case eNonBlockNo:
			return cProjNonBlockPropNoVal;

        case eNonBlockNotVerified:
			return cProjNonBlockPropNotVerifVal;

		default:
			assert(true);
			qWarning("Unknown value nonblocking property value");
			return "ERROR: Unknown non-blocking value";
	}
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::getLwCtrlProp()
{
	switch(hierProject().getLwCtrlProp())
	{
        case eLwCtrlYes:
			return cLwCtrlPropYesVal;

        case eLwCtrlNo:
			return cLwCtrlPropNoVal;

        case eLwCtrlNotVerified:
			return cLwCtrlPropNotVerifVal;

		default:
			assert(true);
			qWarning("Unknown project LD level-wise controllable property value.");
			return "ERROR: Unknown LD level-wise controllable property value";
	}
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::getLwCtrlProp(const DesSubsystem& subsys)
{
	return subsys.isControllable() ? cLwCtrlPropYesVal : cLwCtrlPropNoVal;
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::getLwNonBlockProp()
{
	switch(hierProject().getLwNonBlockProp())
	{
        case eLwNonBlockYes:
			return cLwNonBlockPropYesVal;

        case eLwNonBlockNo:
			return cLwNonBlockPropNoVal;

        case eLwNonBlockNotVerified:
			return cLwNonBlockPropNotVerifVal;

		default:
			assert(true);
			qWarning("Unknown value LD level-wise nonblocking property value");
			return "ERROR: Unknown LD level-wise non-blocking value";
	}
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::getLwNonBlockProp(const DesSubsystem& subsys)
{
	return subsys.isNonBlocking() ? cLwNonBlockPropYesVal : cLwNonBlockPropNoVal;
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::getIConsistProp()
{
	switch(hierProject().getIConsistProp())
	{
        case eIConsYes:
			return cIConsistPropYesVal;

        case eIConsNo:
			return cIConsistPropNoVal;

        case eIConsNotVerified:
			return cIConsistPropNotVerifVal;

		default:
			assert(true);
			qWarning("Unknown value interface-consistent property value");
			return "ERROR: Unknown interface-consistent value";
	}
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::getIConsistProp(const DesSubsystem& subsys)
{
	return subsys.isInterfConsist() ? cIConsistPropYesVal : cIConsistPropNoVal;
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::toString(const std::wstring& stdString)
{
	if (stdString.empty())
	{
		return "-";
	}
	else
	{
		return QString::fromStdWString(stdString);
	}
}

//_________________________________________________________________________________________________

QString SimConfigPrinter::toString(int value)
{
	return (QVariant(value)).toString();
}

} //end of namespace
