#include "edif_import.h"
#include "hub.h"
#include "structural.h"
#include "config.h"
#include "edif_err.h"
#include <iostream>
using namespace std;
using namespace hub;
// ----------------------------------------------------------------------------------------------
void EdifImport:: operator () (const string& fn) {
    if (!fn.empty()) fileName (fn);
    InconsReason locr= FindOrCreate (loc_);
    if (!locr.okay()) 
	throw UnspecException (locr, ToolPosition ("EDIF import"));
    if (fileName().empty()) 
	throw UnspecException (InconsReason ("file name not specified"),
	 ToolPosition ("EDIF import"));
    parser_.run (fileName());
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: operator() (hub::Library*,      const std::string&) {
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: operator() (hub::Entity*,       const std::string&) {
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: operator() (hub::Architecture*, const std::string&) {
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveLibrary (std::string libName) {
    theLib_ = theHub_->FindOrCreateLib (libName);
    libCtx_ = DefCtx;
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveExternal (std::string libName) {
    theLib_ = theHub_->FindOrCreateLib (libName);
    libCtx_ = ExtCtx;
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveDesign (std::string designName, std::string cellName, std::string libName) {
    Library* lib = theHub_->FindLib (libName);		// 
    if (lib==NULL)
	throw UndefException (UndefReason ("library", libName), 
			      LexPosition(parser_.l),
			      CircCtx (0));
    Entity* entity = lib->FindEntity (cellName);	// 
    if (entity==NULL)
	throw UndefException (UndefReason ("cell", cellName), 
			      LexPosition(parser_.l),
			      CircCtx (lib));
    theDesign_ = theHub_->CreateDesign (designName, entity, lib);
    if ( theDesign_ == NULL )
	throw RedefException (RedefReason ("design", designName), 
			      LexPosition(parser_.l),
			      CircCtx (0));
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveCell (std::string cellName) {
    theEntity_ = theLib_->FindOrCreateEntity (cellName);
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveView (std::string viewName) {
    // this is a netlist view
    switch (libCtx_) {
	case ExtCtx:
	    theArch_ = theEntity_->FindOrCreateArch (viewName);
	    theArchImpl_ = NULL;
	    break;
	case DefCtx:		// this must be NETLIST or the parser has folded down
	    theArch_ = theEntity_->CreateArch <StructArch> (viewName);
	    if (!theArch_) {
		throw RedefException (RedefReason ("view", viewName), 
				      LexPosition(parser_.l),
				      CircCtx (theLib_, theEntity_));
	    }	
	    theArchImpl_ = theArch_->Impl<StructArch>();
	    break;
    }
    portNum_=0;
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: havePort (std::string portName) {
    // ------------- make port prototype in the Entity 
    if (theEntity_-> ArchNum() == 1) {
	thePort_ = theEntity_->CreateNextPort (portName);
	if (thePort_ == NULL) {
	    throw RedefException (RedefReason ("port", portName), 
				  LexPosition(parser_.l),
				  CircCtx (theLib_, theEntity_, theArch_));
	}
    // ------------ check if the prototype is equal
    } else {
	thePort_ = theEntity_->FindPort (portName);
	if (thePort_ == NULL) {
	    throw OthException (OthReason ("view", theArch_->name(), "interface mismatch"), 
				LexPosition(parser_.l),
				CircCtx (theLib_, theEntity_));
	}
	portNum_++;
    }
    // ------------- if this definition not declaration make an instantiation in the StructArch
    if (theArchImpl_)						// bad practice
	theArchPort_ = theArchImpl_->CreatePort (thePort_);
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveDir (std::string portDir) { 
    
    PortProto :: flagsType dir = DecodeDir (portDir);
    // ------------- set dir
    if (theEntity_-> ArchNum() == 1) {	
	dir.Project (thePort_->flags());	// this si seen by possible arch's instance
    // ------------- check dir against entity's prototype	
    } else {	
	if (!dir.IsSubset (thePort_->flags())) {
	    throw OthException (OthReason ("view", theArch_->name(), "interface mismatch"), 
				LexPosition(parser_.l),
				CircCtx (theLib_, theEntity_));
	}	    
    }
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveInstance (
				std::string instName, 
				std::string viewName, 
				std::string cellName, 
				std::string libName) {
 
    // ------ the referenced entity and its architecture --------------
    Library* lib;
    if (libName.empty()) {
	lib = theLib_;		// library name not mentioned -> this library
    } else {
	lib = theHub_->FindLib (libName);		// 
	if (lib==NULL) {
	    throw UndefException (UndefReason ("library", libName), 
				  LexPosition(parser_.l),
				  CircCtx (0));
	}
    }
    Entity* entity = lib->FindEntity (cellName);	// 
    if (entity==NULL) {
	throw UndefException (UndefReason ("cell", cellName), 
			      LexPosition(parser_.l),
			      CircCtx (lib));
    }
    Architecture* arch = entity->FindArch (viewName);	// 
    if (arch==NULL) {
	throw UndefException (UndefReason ("view", viewName), 
			      LexPosition(parser_.l),
			      CircCtx (lib, entity));
    }
    if (!theArch_->predicate(StructArch :: is)) {
	throw OthException (OthReason ("view", theArch_->name(), "interface mismatch"), 
			    LexPosition(parser_.l),
			    CircCtx (lib, entity));
    }
    theInstance_ = entity->Instantiate (instName, theArchImpl_, arch);
    if (theInstance_==NULL) {
	throw RedefException (RedefReason ("instance", instName), 
			      LexPosition(parser_.l),
			      CircCtx (theLib_, theEntity_, theArch_));
    }
}
// ----------------------------------------------------------------------------------------------
bool EdifImport:: wantProperty (propCtx ctx, std::string propName) { 
    switch (ctx) {
	case instCtx:   return true; 
	case viewCtx:   return false; 
	case designCtx: return true;
	case netCtx:    return false; 
	case cellCtx:   return false;  
	case portCtx:   return false;  
    }
    return false;
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveStrProperty (propCtx ctx, std::string propName, std::string propValue) {
    switch (ctx) {
	case instCtx:   theInstance_->CreateProperty (propName, propValue); break;
	case viewCtx:   break;
	case designCtx: break;
	case netCtx:    break;
	case cellCtx:   break;
	case portCtx:   break;
    }
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveIntProperty (propCtx ctx, std::string propName, int propValue) {
    switch (ctx) {
	case instCtx:   break;
	case viewCtx:   break;
	case designCtx: break;
	case netCtx:    break;
	case cellCtx:   break;
	case portCtx:   break;
    }
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveBoolProperty (propCtx ctx, std::string propName, bool propValue) {
    switch (ctx) {
	case instCtx:   break;
	case viewCtx:   break;
	case designCtx: break;
	case netCtx:    break;
	case cellCtx:   break;
	case portCtx:   break;
    }
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveNet (std::string netName) {
    if (!theArch_->predicate(StructArch :: is)) {
	throw OthException (OthReason ("view", theArch_->name(), "not NETLIST"), 
			    LexPosition(parser_.l),
			    CircCtx (theLib_, theEntity_));
    }
    theNet_ = theArchImpl_ -> CreateNet (netName);	// 
    if (theNet_ == NULL ) {
	throw RedefException (RedefReason ("net", netName), 
			    LexPosition(parser_.l),
			    CircCtx (theLib_, theEntity_, theArch_));
    }
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: haveJoined (std::string portName, std::string instName) {
    if (!theArch_->predicate(StructArch :: is)) {
	throw OthException (OthReason ("view", theArch_->name(), "not NETLIST"), 
			    LexPosition(parser_.l),
			    CircCtx (theLib_, theEntity_));
    }
    Joinable*   port;
    
    if (instName.empty()) {				// this is outer port
	port = theArchImpl_->FindPort (portName);	
    } else {
	Instance* inst = theArchImpl_->FindInstance (instName);	// 
	if (inst==NULL) {
	    throw UndefException (UndefReason ("instance", instName), 
				  LexPosition(parser_.l),
				  CircCtx (theLib_, theEntity_, theArch_));
	}
        port = inst->FindPort (portName);
    }
    if (port == NULL) {
	throw UndefException (UndefReason ("port", portName), 
			      LexPosition(parser_.l),
			      CircCtx (theLib_, theEntity_, theArch_));
    }
    theNet_->Join (port);
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: endLibrary      (void) {
    theLib_ = NULL;
}    
// ----------------------------------------------------------------------------------------------
void EdifImport:: endExternal     (void) {
    theLib_ = NULL;
}    
// ----------------------------------------------------------------------------------------------
void EdifImport:: endDesign       (void) {
    theDesign_ = NULL;
}    
// ----------------------------------------------------------------------------------------------
void EdifImport:: endCell         (void) {
    theEntity_ = NULL;
}    
// ----------------------------------------------------------------------------------------------
void EdifImport:: endPort         (void) {
    thePort_ = NULL;
}
// ----------------------------------------------------------------------------------------------
void EdifImport:: endView         (void) {
    if (theEntity_-> ArchNum() > 1) {
        if (portNum_ != theEntity_->PortNum()) {
	    throw OthException (OthReason ("architecture", theArch_->name(), "interface mismatch"), 
				LexPosition(parser_.l),
				CircCtx (theLib_, theEntity_));
	}
    }
    theArch_ = NULL;
    theArchImpl_ = NULL;
}    
// ----------------------------------------------------------------------------------------------
void EdifImport:: endInstance     (void) {
    theInstance_ = NULL;
}    
// ----------------------------------------------------------------------------------------------
void EdifImport:: endNet          (void) {
    theNet_ = NULL;
}    
// -------------------------------------------------------------------
const hub :: PortProto :: flagsType&  EdifImport :: DecodeDir (const std::string& dir) {
    if (CompareText (dir, std::string ("input")) == 0) return PortProto :: inDir;
    if (CompareText (dir, std::string ("output")) == 0) return PortProto :: outDir;
    if (CompareText (dir, std::string ("inout")) == 0) return PortProto :: inOutDir;
    return PortProto :: noDir;
}
