//---------------------------------------------------------------------------
#include "blif_export.h"
#include "hub/hub.h"
#include "hub/entity.h"
#include "hub/behavioral.h"
using namespace hub;
using namespace std;
// --------------------------------------------------------------------------------------
void BlifExpLang::enregister (void) {
    registry_.push_back((new BlifExpLang ("BLIF"))->addSuffix (".blif"));
}
// --------------------------------------------------------------------------------------
void BlifExpLang::getDefaults (hub::Hub* h, const std::string& fn, Export::Source& src) {
    src.label ("source");
    src[Location::lib].clear();
    src[Location::ent].clear();
    src[Location::arch].clear();
    src[Location::design].clear();
}
//---------------------------------------------------------------------------
// ==========================================================================
BlifExport::BlifExport(hub::Hub* h): traverser_(h, this) {}

//---------------------------------------------------------------------------
void BlifExport::operator()  (std::ostream* os, hub::Location* src) {
    traverser_ = (*src);
    out_ = os;
    nlbase_.sync (*out_);
    traverser_ ();
}
//---------------------------------------------------------------------------
std::string BlifExport :: findNet(std::string instance, std::string port, StructArch* impl)
{
	std::string res;
	StructArch :: NetIter ni;
    Net :: JoinIter ji;
    for (ni = impl->NetBeg(); ni != impl->NetEnd(); ni++) {
		for (ji = ni->second->JoinBeg(); ji != ni->second->JoinEnd(); ji++) {
			Port <Instance>* iport = dynamic_cast <Port <Instance>* > (*ji);
			if (iport) {
				if( (*ji)->name() == port ) {
					if( iport->Parent()->name() == instance ) return ni->first;
				}
			}
		}
    }

	return res;
}
//---------------------------------------------------------------------------
void BlifExport :: haveEntity (Entity* ent) {
    (*out_) << nl_ << ".model " << ent->name() ;
	inPortNames.clear();
	outPortNames.clear();
}
//---------------------------------------------------------------------------
void BlifExport :: endEntity  (Entity* ent) {
    (*out_) << nl_ << ".end" ;
}
//---------------------------------------------------------------------------
void BlifExport::havePortProto (hub::PortProto* port)
{
	if( port->predicate(PortProto::input) ) inPortNames.push_back(port->name());
	if( port->predicate(PortProto::output) ) outPortNames.push_back(port->name());
}
//---------------------------------------------------------------------------
void BlifExport::haveArch (hub::Architecture* arch)
{
    StructArch* sa = arch->Impl <StructArch> ();
    if (sa) {
		std::list<std::string>::iterator it;
		if( !inPortNames.empty() ) (*out_) << nl_ << ".inputs";
		for( it = inPortNames.begin(); it != inPortNames.end(); ++it ) {
			(*out_) << " " << *it;
		}
		if( !outPortNames.empty() ) (*out_) << nl_ << ".outputs";
		for( it = outPortNames.begin(); it != outPortNames.end(); ++it ) {
			(*out_) << " " << *it;
		}
		StructArchTraverser structTrav (sa, this);
		structTrav.travInsts ();
		return;
	} else {
		BehavArch* ba = arch->Impl <BehavArch> ();
		if( ba ) {
			(*out_) << nl_ << ".start_kiss";
			BehavDescr* descr = ba->description<BehavDescr>();
			if( descr ) {
				exportMap.Perform (*this, *descr);
			}
			(*out_) << nl_ << ".end_kiss";
		}
	}
}
//---------------------------------------------------------------------------
void BlifExport :: haveInst (hub::Instance* inst) {
//      Library* currentLib = inst->Parent()->Parent()->Parent()->Parent();
	Architecture* currentArch = inst->CurrentArch();
	Port<Instance>* entityPort;
	Entity* entity;
	int i = 0;

	if( currentArch->name() == "pla" ) {
		(*out_) << nl_ << ".named ";
		while( (entityPort = inst->FindPort(i, PortProto::input)) ) {
			(*out_) << findNet(inst->name(), entityPort->name(), inst->Parent()) << " ";
			++i;
		}
		(*out_) << inst->name();
		BehavArch* ba = currentArch->Impl <BehavArch> ();
		if( ba ) {
			BehavDescr* descr = ba->description<BehavDescr>();
			if( descr ) {
				exportMap.Perform (*this, *descr);
			}
		}
		return;
	}
	if( currentArch->name() == "kiss" ) {
		(*out_) << nl_ << ".start_kiss";
		BehavArch* ba = currentArch->Impl <BehavArch> ();
		if( ba ) {
			BehavDescr* descr = ba->description<BehavDescr>();
			if( descr ) {
				exportMap.Perform (*this, *descr);
			}
		}
		(*out_) << nl_ << ".end_kiss";
		return;
	}
	//subckt
	entity = inst->entity();
	(*out_) << nl_ << ".subckt " << entity->name() << " ";
	while( (entityPort = inst->FindPort(i, PortProto::input)) ) {
		(*out_) << entityPort->name() << "=" << findNet(inst->name(), entityPort->name(), inst->Parent()) << " ";
		++i;
	}
	i = 0;
	while( (entityPort = inst->FindPort(i, PortProto::output)) ) {
		(*out_) << entityPort->name() << "=" << findNet(inst->name(), entityPort->name(), inst->Parent()) << " ";
		++i;
	}
}
//---------------------------------------------------------------------------
bool BlifKissTable::Represent (BlifExport& exp, FsmDescr& f) {
	std::list<StateTableRow> table;
	std::list<StateTableRow>::iterator it;
	f.CreateTable(table);
	(*exp.out_) << exp.nl_ << ".i " << f.inputNo();
	(*exp.out_) << exp.nl_ << ".o " << f.outputNo();
	(*exp.out_) << exp.nl_ << ".p " << f.productNo();
	if( f.stateNo() != -1 )
		(*exp.out_) << exp.nl_ << ".s " << f.stateNo();
	(*exp.out_) << exp.nl_ << ".r " << f.resetState()->name();

	for( it = table.begin(); it != table.end(); ++it ) {
		(*exp.out_) << exp.nl_ << it->input << " " << it->currState << " " << it->nextState << " " << it->output;
	}
	return true;
}
int BlifKissTable::wantInit = Export :: exportMap.Enlist <BlifExport&, FsmDescr&, &BlifKissTable :: Represent> ();
//---------------------------------------------------------------------------
bool BlifPlaTable::Represent (BlifExport& exp, PlaDescr& f) 
{
	PlaDescr::MatrixIter inputIt, outputIt;
	for( inputIt = f.inpBeg(), outputIt = f.outpBeg(); inputIt != f.inpEnd(); ++inputIt, ++outputIt) {
		(*exp.out_) << exp.nl_ << *inputIt << " " << *outputIt;
	}
	return true;
}
int BlifPlaTable::wantInit = Export :: exportMap.Enlist <BlifExport&, PlaDescr&, &BlifPlaTable :: Represent> ();
//---------------------------------------------------------------------------
