/*	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 "ProjectPrinter.h"
#include "DesPrinter.h"
#include "DesProject.h"
#include "DesHierProject.h"
#include "Des.h"
#include "DesSubsystem.h"
#include "DesInterface.h"

namespace DESpot
{

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

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

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

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

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

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

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

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

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

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

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

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

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

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

const QString ProjectPrinter::cDesContentHeader = "\n\n\n\n=======================================================================================================\n\
===                                        DES DETAILS                                             ====\n\
=======================================================================================================\n\n\n\n";


//_________________________________________________________________________________________________

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

//_________________________________________________________________________________________________

ProjectPrinter::~ProjectPrinter(void)
{
}

//_________________________________________________________________________________________________

void ProjectPrinter::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);

	//print all DES contained in the project
	printProjectDesContent(out);
}

//_________________________________________________________________________________________________

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

//_________________________________________________________________________________________________

void ProjectPrinter::printProjectDesContent(QTextStream& out)
{
	out << cDesContentHeader;

	DesProject::DesIteratorPtr desIt = m_project.createDesIterator();
	for(desIt->first(); desIt->notDone(); desIt->next())
	{
		const Des& des = desIt->currentItem();
		DesPrinter desPrinter(des);
		desPrinter.print(out);

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

//_________________________________________________________________________________________________

void ProjectPrinter::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 ProjectPrinter::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 ProjectPrinter::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 ProjectPrinter::printHighLevelSubsys(QTextStream& out)
{
	printSubsystem(out, hierProject().getRootSubsys());
}
									  

//_________________________________________________________________________________________________

void ProjectPrinter::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 ProjectPrinter::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 ProjectPrinter::printInterfaces(QTextStream& out)
{
	DesHierProject::InterfIteratorPtr interfIt = hierProject().createInterfIterator();
	for(interfIt->first(); interfIt->notDone(); interfIt->next())
	{
		const DesInterface& interf = interfIt->currentItem();
		printInterface(out, interf);
	}
}

//_________________________________________________________________________________________________

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

	out << cInterfNameLabel << toString(interf.getName()) << "\n\n";
	out << cInterfIConsistLabel << getIConsistProp(interf) << "\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 ProjectPrinter::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 ProjectPrinter::isHierProject()
{
	return (m_project.getType() == eHierProject);
}

//_________________________________________________________________________________________________

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

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

//_________________________________________________________________________________________________

QString ProjectPrinter::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	ProjectPrinter::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 ProjectPrinter::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 ProjectPrinter::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 ProjectPrinter::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 ProjectPrinter::getLwCtrlProp(const DesSubsystem& subsys)
{
	return subsys.isControllable() ? cLwCtrlPropYesVal : cLwCtrlPropNoVal;
}

//_________________________________________________________________________________________________

QString ProjectPrinter::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 nonblocking value";
	}
}

//_________________________________________________________________________________________________

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

//_________________________________________________________________________________________________

QString ProjectPrinter::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 ProjectPrinter::getIConsistProp(const DesSubsystem& subsys)
{
	return subsys.isInterfConsist() ? cIConsistPropYesVal : cIConsistPropNoVal;  
}

//_________________________________________________________________________________________________

QString ProjectPrinter::getIConsistProp(const DesInterface& interf)
{
	return interf.isInterfConsist() ? cIConsistPropYesVal : cIConsistPropNoVal;  
}

//_________________________________________________________________________________________________

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

//_________________________________________________________________________________________________

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

} //end of namespace
