#include "dbg_export.h"
#include <string>
#include "err.h"
#include "hub.h"
#include "entity.h"
#include "structural.h"
#include "behavioral.h"
using namespace hub;
using namespace std;
//---------------------------------------------------------------------------
DbgExport :: DbgExport (hub::Hub* h, const std::string& fn): 
Export (fn), 
traverser_(h, this)  {
    initPortAttrMap ();
    initArchAttrMap ();
    initNetAttrMap  ();
}
//---------------------------------------------------------------------------
DbgExport :: DbgExport (hub::Library* lib, const std::string& fn): 
Export (fn), 
traverser_(lib, this)  {
    initPortAttrMap ();
    initArchAttrMap ();
    initNetAttrMap  ();
}
//---------------------------------------------------------------------------
DbgExport :: DbgExport (hub::Entity* ent, const std::string& fn): 
Export (fn), 
traverser_(ent, this)  {
    initPortAttrMap ();
    initArchAttrMap ();
    initNetAttrMap  ();
}
//---------------------------------------------------------------------------
DbgExport :: DbgExport (hub::Architecture* arch, const std::string& fn): 
Export (fn), 
traverser_(arch, this)  {
    initPortAttrMap ();
    initArchAttrMap ();
    initNetAttrMap  ();
}
//---------------------------------------------------------------------------
std::map < hub::AttrIndex <hub::PortProto>, std::string > DbgExport :: portAttrMap;
std::map < hub::AttrIndex <hub::Architecture>, std::string > DbgExport :: archAttrMap;
std::map < hub::AttrIndex <hub::Net>, std::string > DbgExport :: netAttrMap;
//---------------------------------------------------------------------------
void DbgExport :: initPortAttrMap (void) {
    if (portAttrMap.size() > 0) return;
    portAttrMap [PortProto::input] = "in";
    portAttrMap [PortProto::output] = "out";
}
//---------------------------------------------------------------------------
void DbgExport :: initArchAttrMap (void) {
    archAttrMap [StructArch::is] = "struct";
    archAttrMap [BehavArch::is] = "behav";
}
//---------------------------------------------------------------------------
void DbgExport :: initNetAttrMap (void) {
}
//---------------------------------------------------------------------------
void DbgExport :: havePortAttr (PortProto* port) {
    bool attrBegins = true;
    std::map < AttrIndex <PortProto>, std::string > :: const_iterator attri;
    for (attri = portAttrMap.begin(); attri != portAttrMap.end(); attri++) {
		if (port->predicate (attri->first)) {
			if (attrBegins) {
				attrBegins = false;
				out << "attr ";
			}
			out << attri->second << nl_(" ");
		}
    }
}
//---------------------------------------------------------------------------
void DbgExport :: haveArchAttr (Architecture* arch) {
    bool attrBegins = true;
    std::map < AttrIndex <Architecture>, std::string > :: const_iterator attri;
    for (attri = archAttrMap.begin(); attri != archAttrMap.end(); attri++) {
		if (arch->predicate (attri->first)) {
			if (attrBegins) {
				attrBegins = false;
				out << "attr ";
			}
			out << attri->second << nl_(" ");
		}
    }
}
//---------------------------------------------------------------------------
void DbgExport :: haveNetAttr  (Net*) {
}
// ==========================================================================
void DbgExport :: operator() (const std::string& fn) {
    if (!fn.empty()) fileName (fn);
    InconsReason locr= traverser_.Find (loc_);	// the specified location must exist
    if (!locr.okay()) 
	throw UnspecException (locr, ToolPosition ("DBG export"));
    // --------- open output -------------------
    if (fileName_.empty()) 
		throw UnspecException 
			(InconsReason ("file name not specified"),
			 ToolPosition ("DBG export"));
    out.open (fileName_.c_str());
    if (!out.is_open()) 
		throw IOException (IOReason(errno), FilePosition (fileName_));
    nlbase_.sync (out);
    traverser_ ();
    out.close ();
}
//---------------------------------------------------------------------------
void DbgExport :: operator() (hub::Library* lib, const std::string& fn) {
    if (!fn.empty()) fileName (fn);
    traverser_.Set (lib);
    // --------- open output -------------------
    if (fileName_.empty()) 
		throw UnspecException 
			(InconsReason ("file name not specified"),
			 ToolPosition ("DBG export"));
    out.open (fileName_.c_str());
    if (!out.is_open()) 
		throw IOException (IOReason(errno), FilePosition (fileName_));
    nlbase_.sync (out);
    traverser_ ();
    out.close ();
}
//---------------------------------------------------------------------------
void DbgExport :: operator() (hub::Entity* ent, const std::string& fn) {
    if (!fn.empty()) fileName (fn);
    traverser_.Set (ent);
    // --------- open output -------------------
    if (fileName_.empty()) 
		throw UnspecException 
			(InconsReason ("file name not specified"),
			 ToolPosition ("DBG export"));
    out.open (fileName_.c_str());
    if (!out.is_open()) 
		throw IOException (IOReason(errno), FilePosition (fileName_));
    nlbase_.sync (out);
    traverser_ ();
    out.close ();
}
//---------------------------------------------------------------------------
void DbgExport :: operator() (hub::Architecture* arch, const std::string& fn) {
    if (!fn.empty()) fileName (fn);
    traverser_.Set (arch);
    // --------- open output -------------------
    if (fileName_.empty()) 
		throw UnspecException 
			(InconsReason ("file name not specified"),
			 ToolPosition ("DBG export"));
    out.open (fileName_.c_str());
    if (!out.is_open()) 
		throw IOException (IOReason(errno), FilePosition (fileName_));
    nlbase_.sync (out);
    traverser_ ();
    out.close ();
}
//---------------------------------------------------------------------------
void DbgExport :: haveUnit () {
    out << nl_ << "+unit";
    nl_+=4;
}
//---------------------------------------------------------------------------
void DbgExport :: endUnit  () {
    nl_-=4;
    out << nl_ << "-unit";
}
//---------------------------------------------------------------------------
void DbgExport :: haveLibrary (Library* lib) {
    out << nl_ << "+library " << lib->name() ;
    nl_+=4;
}
//---------------------------------------------------------------------------
void DbgExport :: endLibrary  (Library* lib) {
    nl_-=4;
    out << nl_ << "-library " << lib->name() ;
}
//---------------------------------------------------------------------------
void DbgExport :: haveDesign (Design* dsgn) {
    out << nl_ << "+Design " << dsgn->name() <<" " 
	<< dsgn->DesignEntity()->Parent()->name() << "."
	<< dsgn->DesignEntity()->name() ;
    nl_+=4;
}
//---------------------------------------------------------------------------
void DbgExport :: endDesign  (Design* dsgn) {
    nl_-=4;
    out << nl_ << "-Design " << dsgn->name() ;
}
//---------------------------------------------------------------------------
void DbgExport :: haveEntity (Entity* ent) {
    out << nl_ << "+entity " << ent->name() ;
    nl_+=4;
}
//---------------------------------------------------------------------------
void DbgExport :: endEntity  (Entity* ent) {
    nl_-=4;
    out << nl_ << "-entity " << ent->name() ;
}
//---------------------------------------------------------------------------
void DbgExport :: havePortProto (PortProto* pp) {
    out << nl_ << "=port " << pp->name() << " ";
    havePortAttr (pp);
}
//---------------------------------------------------------------------------
void DbgExport :: haveArch (Architecture* a) {
    out << nl_ << "+arch " << a->name()  << " ";
    haveArchAttr (a);
    nl_+=4;
    StructArch* sa = a->Impl <StructArch> ();
    if (sa) {
		StructArchTraverser structTrav (sa, this);
		structTrav.travInsts ();
		structTrav.travNets ();
		return;
    }
	BehavArch* ba = a->Impl <BehavArch> ();
	if (ba) {
		BehavDescr* descr = ba->description<BehavDescr>();
		if (!descr) {
			out << nl_ << "<empty description>";
		} else if (!exportMap.Perform (*this, *descr)) {	// either the dispatcher or the method failed
			out << nl_ << "<unknown architecture>";
		}
	}
}
//---------------------------------------------------------------------------
void DbgExport :: endArch  (Architecture* a) {
    nl_-=4;
    out << nl_ << "-arch " << a->name() ;
}
//---------------------------------------------------------------------------
void DbgExport :: haveNet (hub::Net* n) {
    out << nl_ << "+net " << n->name() << " ";
    nl_+=4;
    netBegins_ = true;
}
//---------------------------------------------------------------------------
void DbgExport :: endNet  (hub::Net* n) {
    nl_-=4;
    out << nl_ << "-net " << n->name() ;
}
//---------------------------------------------------------------------------
void DbgExport :: haveJoined (hub::Joinable* join) {
    if (netBegins_) {
	netBegins_ = false;
    } else {
	out << nl_(", ");
    }
    out  <<  join->name() ;
    Port <Instance>* iport = dynamic_cast <Port <Instance>* > (join);
    if (iport) {
	out << "." << iport->Parent()->name() ;
    }
}
//---------------------------------------------------------------------------
void DbgExport :: havePort (hub::Port <StructArch>*) {
}
//---------------------------------------------------------------------------
void DbgExport :: haveInst (hub::Instance* inst) {
    Library* currentLib = inst->Parent()->Parent()->Parent()->Parent();
    out << nl_ << "=instance " << inst->name() << " ";
    Entity* ent = inst->entity();
    out << ent->name();
    if (ent->Parent() != currentLib) out << "." << ent->Parent()->name();
}
// ==========================================================================
bool DbgSymFunc::Represent (DbgExport& exp, SymFunc& f) {
	exp.out << exp.nl_ << "symmetric func " << ftab [f.func()] << ", " << f.inpNo() << " inputs";
	return true;
}
const char* DbgSymFunc::ftab [SymFunc::fNull+1]={
	"GND", "VDD",								// no inputs
	"BUF", "INV",								// one input
	"AND", "XOR", "OR", "NOR", "XOR", "NAND",	// two and more inputs
	"NULL"										// unknown/invalid
};
int DbgSymFunc::wantInit = Export :: exportMap.Enlist <DbgExport&, SymFunc&, &DbgSymFunc :: Represent> ();
// ==========================================================================
bool DbgRTLReg::Represent (DbgExport& exp, RTLReg& f) {
	exp.out << exp.nl_ << "RTL register";
	return true;
}
int DbgRTLReg::wantInit = Export :: exportMap.Enlist <DbgExport&, RTLReg&, &DbgRTLReg :: Represent> ();
