//---------------------------------------------------------------------------

#include "stdafx.h"

using namespace std;

#pragma hdrstop

#include "../Kernel/kernelincludes.h"

#include "Blif.h"
#include "Node.h"
#include "Connection.h"
#include "Network.h"

//---------------------------------------------------------------------------

Network::Network()
{
	connected = false;
}

Network::Network( Blif &src )
// Construct the network from BLIF
{
	list<SoPLA>::iterator pla_it;

	for ( pla_it = src.GetData()->begin(); pla_it != src.GetData()->end(); pla_it++ ) 
		nodes.push_back( Node((PLA)*pla_it) );
	inames = src.GetINames();
	onames = src.GetONames();
	model = src.GetModel();
	connected = false;
//	Connect();
}

Network::~Network()
{
	Disconnect();
}

void Network::AddNode( const Node &_node )
{
	Disconnect();
	nodes.push_back(_node);
	if ( !indexnodes.empty() ) nodes.back().index = nodes.size()-1;
	names.clear();
}

list<Node>::iterator Network::DeleteNode( const list<Node>::iterator ni )
{
	list<Node>::iterator tmp;

	Disconnect();
	indexnodes.clear();
	tmp = nodes.erase(ni);
	names.clear();
	return tmp;
}

void Network::Disconnect(void)
{
	list<Node>::iterator nod_it;
	vector<Connection*>::iterator conn_it;

	if ( !connected ) return;
	for ( nod_it = nodes.begin(); nod_it != nodes.end(); nod_it++ ) {
		for ( conn_it = ((*nod_it).fanout).begin(); conn_it != (*nod_it).fanout.end(); conn_it++ ) {
			delete (*conn_it);
			(*conn_it) = NULL;
		}
		(*nod_it).fanin.clear();
		(*nod_it).fanout.clear();
	}
	connected = false;
}

void Network::Connect(void)
// Constructs the connections by processing BLIF names
{	
	list<Node>::iterator nod_it, nod_it2;
	unsigned int i, j;
	Connection *conn;

	Disconnect();
	for ( nod_it = nodes.begin(); nod_it != nodes.end(); nod_it++ ) {
		(*nod_it).fanout.resize( (*nod_it).data.GetOutputs(), NULL );
		for ( i = 0; i < (*nod_it).data.GetOutputs(); i++ ) {
			if ( (*nod_it).data.GetOName(i) == "" ) throw RuntimeException( "Network::Connect: Missing output name" );
			conn = new Connection();
			conn->name = (*nod_it).data.GetOName(i);
			conn->source = &*nod_it;
			conn->src_out = i;
			for ( nod_it2 = nodes.begin(); nod_it2 != nodes.end(); nod_it2++ ) {
				for ( j = 0; j < (*nod_it2).data.GetOutputs(); j++ )
					if ( nod_it2 != nod_it && conn->name == (*nod_it2).data.GetOName(j) ) throw RuntimeException( "Network::Connect: Multiple net drivers" );
				for ( j = 0; j < (*nod_it2).data.GetInputs(); j++ )
					if ( conn->name == (*nod_it2).data.GetIName(j) ) {
						conn->dest.push_back( make_pair(&*nod_it2, j) );
						if ( (*nod_it2).fanin.size() != (*nod_it2).data.GetInputs() ) (*nod_it2).fanin.resize((*nod_it2).data.GetInputs(), NULL);
						else if ( (*nod_it2).fanin[j] != NULL ) throw RuntimeException( "Network::Connect: Multiple net drivers" );
						(*nod_it2).fanin[j] = conn;
					}
			}
			(*nod_it).fanout[i] = conn;
		}
	}
	connected = true;
}

Blif *Network::ToBlif(void) const
// Converts the network to Blif
// type specifies the Blif type (false = r, true = f)
{
	list<Node>::const_iterator nod_it;
	unsigned int i;
	Blif *dest;
	SoPLA *pla;

	dest = new Blif( inames, onames );
	dest->SetModel( model );
	for ( nod_it = nodes.begin(); nod_it != nodes.end(); nod_it++ )
		for ( i = 0; i < (*nod_it).data.GetOutputs(); i++ ) {
			if ( (*nod_it).data.GetType().f ) pla = (*nod_it).data.ToSoPLA( i, true );
			else pla = (*nod_it).data.ToSoPLA( i, false );
			pla->RemoveAllDCCols();
			dest->AppendPLA( pla );
			delete pla;
		}
	return dest;
}

Blif *Network::ToBlif( const bool type ) const
// Converts the network to Blif
// type specifies the Blif type (false = r, true = f)
{
	list<Node>::const_iterator nod_it;
	unsigned int i;
	Blif *dest;
	SoPLA *pla;

	dest = new Blif( inames, onames );
	for ( nod_it = nodes.begin(); nod_it != nodes.end(); nod_it++ )
		for ( i = 0; i < (*nod_it).data.GetOutputs(); i++ ) {
			pla = (*nod_it).data.ToSoPLA( i, type );
			pla->RemoveAllDCCols();
			dest->AppendPLA( pla );
		}
	return dest;
}

void Network::ToSingleOutput(void)
// Converts the network into a network of single-output nodes, like Blif
{
	list<Node>::iterator nod_it, endnode;
	unsigned int i;
	SoPLA *pla;
	bool first = true;

	Disconnect();
	names.clear();
	indexnodes.clear();
	nod_it = nodes.begin();
	if ( nod_it == nodes.end() ) return;
	endnode = nodes.end();
	endnode--;
	do {
		if ( (*nod_it).data.GetOutputs() == 1 ) nod_it++;
		else {
			for ( i = 0; i < (*nod_it).data.GetOutputs(); i++ ) {
				if ( (*nod_it).data.GetType().f ) pla = (*nod_it).data.ToSoPLA( i, true );
				else pla = (*nod_it).data.ToSoPLA( i, false );
				pla->RemoveAllDCCols();
				this->AddNode( Node(*pla) );
				delete pla;
				if (first) {
					endnode = nodes.end();
					endnode--;
					first = false;
				}
			}
			nod_it = nodes.erase(nod_it);
		}
	} while ( nod_it != endnode );
}

list<Node>::iterator Network::MergeNodes( list<Node>::iterator node1, list<Node>::iterator node2 )
// merges two nodes into one
{
	unsigned int i, j;
	PLA res_pla;
	vector<string> inames;
	vector<string> onames;
	vector<string>::iterator name_it;
	string im, om;
	Term t;
	TermList::iterator ti;
	list<Node>::iterator res;

	if ( (*node1).data.GetType() != (*node1).data.GetType() ) throw RuntimeException( "Network::MergeNodes: Merging incompatible nodes" );
	res_pla.SetType( (*node1).data.GetType() );
	Disconnect();
	names.clear();
	indexnodes.clear();
	// Determine inputs
	for ( i = 0; i < (*node1).data.GetInputs(); i++ ) 
		inames.push_back( (*node1).data.GetIName(i) );
	for ( i = 0; i < (*node2).data.GetInputs(); i++ ) {
		for ( name_it = inames.begin(); name_it != inames.end(); name_it++ )
			if ( (*node2).data.GetIName(i) == (*name_it) ) break;
		if ( name_it == inames.end() ) inames.push_back( (*node2).data.GetIName(i) );
	}
	res_pla.SetInputs(inames.size());
	for ( i = 0; i < inames.size(); i++ ) res_pla.SetIName( i, inames[i] );
	// Determine outputs
	for ( i = 0; i < (*node1).data.GetOutputs(); i++ )
		onames.push_back( (*node1).data.GetOName(i) );
	for ( i = 0; i < (*node2).data.GetOutputs(); i++ ) {
		for ( name_it = onames.begin(); name_it != onames.end(); name_it++ )
			if ( (*node2).data.GetOName(i) == (*name_it) ) break;
		if ( name_it == onames.end() ) onames.push_back( (*node2).data.GetOName(i) );
	}
	res_pla.SetOutputs(onames.size());
	for ( i = 0; i < onames.size(); i++ ) res_pla.SetOName( i, onames[i] );
	// Construct PLA input matrix
	for ( ti = (*node1).data.GetData()->begin(); ti != (*node1).data.GetData()->end(); ti++ ) {
		t.init( inames.size(), onames.size() );
		for ( i = 0; i < (*ti).GetInputs(); i++ ) {
			for ( j = 0; j < t.GetInputs(); j++ )
				if ( inames[j] == (*node1).data.GetIName(i) ) break;
			t.SetIM( j, (*ti).GetIM(i) );
		}
		for ( i = 0; i < (*ti).GetOutputs(); i++ ) {
			for ( j = 0; j < t.GetOutputs(); j++ )
				if ( onames[j] == (*node1).data.GetOName(i) ) break;
			// if ( t.GetOM(j) != '~' ) throw RuntimeException( "Network::MergeNodes: Multiple output drivers" );
			t.SetOM( j, (*ti).GetOM(i) );
		}
		res_pla.AppendTerm(t);
	}
	for ( ti = (*node2).data.GetData()->begin(); ti != (*node2).data.GetData()->end(); ti++ ) {
		t.init( inames.size(), onames.size() );
		for ( i = 0; i < (*ti).GetInputs(); i++ ) {
			for ( j = 0; j < t.GetInputs(); j++ )
				if ( inames[j] == (*node2).data.GetIName(i) ) break;
			t.SetIM( j, (*ti).GetIM(i) );
		}
		for ( i = 0; i < (*ti).GetOutputs(); i++ ) {
			for ( j = 0; j < t.GetOutputs(); j++ )
				if ( onames[j] == (*node2).data.GetOName(i) ) break;
			// if ( t.GetOM(j) != '~' ) throw RuntimeException( "Network::MergeNodes: Multiple output drivers" );
			t.SetOM( j, (*ti).GetOM(i) );
		}
		res_pla.AppendTerm(t);
	}
	this->AddNode( Node(res_pla) );
	this->DeleteNode( node1 );
	this->DeleteNode( node2 );
	res = nodes.end();
	res--;
	return res;
}

void Network::Reindex(void)
{
	unsigned int i = 0;
	list<Node>::iterator ndi;

	if ( !indexnodes.empty() ) return;
	indexnodes.clear();
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) {
		indexnodes.push_back(ndi);
		(*ndi).index = i++;
	}
}

list<Node>::iterator Network::GetNodeByIndex( const unsigned int i )
{
	Reindex();
	if ( i > indexnodes.size()-1 ) throw RuntimeException( "Network::GetNodeByIndex: Wrong index" );
	return indexnodes[i];
}

void Network::SaveMBlif( const char *fn )
// Saves the network to a MBLIF file
{
	ofstream f;
	unsigned int i;
	vector<string>::iterator name_it;
	list<Node>::iterator node_it;
	TermList::iterator ti;

    f.open(fn);
    if (!f.is_open()) throw IOException( "Cannot open file: " + string(fn) );

		// write header
	if ( !model.empty() ) f << ".model " << model << endl;
	f << ".inputs";
	for ( name_it = inames.begin(); name_it != inames.end(); name_it++ )
		f << " " << *name_it;
	f << endl;
	f << ".outputs";
	for ( name_it = onames.begin(); name_it != onames.end(); name_it++ )
		f << " " << *name_it;
	f << endl;

		// write nodes
	for ( node_it = nodes.begin(); node_it != nodes.end(); node_it++ ) {
		f << ".inames";
		for ( i = 0; i < (*node_it).GetData()->GetInputs(); i++ )
			if ( !(*node_it).GetData()->GetIName(i).empty()) f << " " << (*node_it).GetData()->GetIName(i);
			else if ( (*node_it).GetData()->GetData()->front().GetIM() == "-" ) f << " " << "[void]";
			else throw RuntimeException( "Network::SaveMBlif: Empty node input name" );
		f << endl;
		f << ".onames";
		for ( i = 0; i < (*node_it).GetData()->GetOutputs(); i++ )
			if ( !(*node_it).GetData()->GetOName(i).empty()) f << " " << (*node_it).GetData()->GetOName(i);
			else throw RuntimeException( "Network::SaveMBlif: Empty node output name" );
		f << endl;

		for ( ti = (*node_it).GetData()->GetData()->begin(); ti != (*node_it).GetData()->GetData()->end(); ti++ )
			f << (*ti).GetIM() << " " << (*ti).GetOM() << endl;
		// f << endl;
	}
	f << ".end" << endl;
}

void Network::GenerateNames(void)
// Generates the "names" structure
{
	NameStruct n;
	list<Node>::iterator ndi;
	vector<string>::iterator nameit;
	unsigned int i, o;

	if ( !names.empty() ) return;
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		for ( o = 0; o < (*ndi).data.GetOutputs(); o++ ) {
			n.pos = o;
			n.ndi = ndi;
			n.name = (*ndi).data.GetOName(o);
			n.PI = false;
			names.push_back(n);
		}
	i = 0;
	for ( nameit = inames.begin(); nameit != inames.end(); nameit++ ) {
		n.pos = i++;
		n.name = *nameit;
		n.ndi = nodes.end();
		n.PI = true;
		names.push_back(n);
	}
}

NameStruct *Network::FindName( const string name )
// Finds a node with an outout of a given name or NULL if not found
{
	list<NameStruct>::iterator nameit;

	GenerateNames();
	for ( nameit = names.begin(); nameit != names.end(); nameit++ )
		if ( (*nameit).name == name ) return &*nameit;
	return NULL;
}

void Network::Rename( const string oldname, const string newname )
// Changes the given net name, in the whole network
{
	NameStruct n;
	list<Node>::iterator ndi;
	list<NameStruct>::iterator nameit;
	unsigned int i, o;

	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) {
		for ( i = 0; i < (*ndi).data.GetInputs(); i++ )
			if ( (*ndi).data.GetIName(i) == oldname ) (*ndi).data.SetIName(i, newname);
		for ( o = 0; o < (*ndi).data.GetOutputs(); o++ ) 
			if ( (*ndi).data.GetOName(o) == oldname ) {
				(*ndi).data.SetOName(o, newname);
				if ( connected ) (*ndi).fanout[o]->name = newname;
			}
	}
	if ( !names.empty() ) 
		for ( nameit = names.begin(); nameit != names.end(); nameit++ )
			if ( (*nameit).name == oldname ) {
				(*nameit).name = newname;
				break;
			}
}

void Network::DerivePorts(void)
// Generates PIs and POs by analyzing the network
// For PIs, the "names" structure is updated
{
	list<Node>::iterator ndi;
	list<NameStruct>::iterator nameit;
	unsigned int i, j;
	bool flag;
	NameStruct n;
	NameStruct *n1;
	
	inames.clear();
	onames.clear();

	names.clear();
	GenerateNames();
	for ( nameit = names.begin(); nameit != names.end(); nameit++ ) {
		flag = false;
		for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ ) {
			for ( i = 0; i < (*ndi).data.GetInputs(); i++ )
				if ( (*ndi).data.GetIName(i) == (*nameit).name ) {
					flag = true;
					break;
				}
			if ( flag ) break;
		}
		if ( !flag ) onames.push_back( (*nameit).name );
	}
	j = 0;
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		for ( i = 0; i < (*ndi).data.GetInputs(); i++ ) {
			n1 = FindName( (*ndi).data.GetIName(i) );
			if ( n1 == NULL ) {
				inames.push_back( (*ndi).data.GetIName(i) );
				n.name = (*ndi).data.GetIName(i);
				n.ndi = nodes.end();
				n.pos = j++;
				names.push_back(n);
			}
		}
	Disconnect();
}

void Network::SpliceNetworks( Network &net )
// splices two networks. Data from net is deleted
{
	indexnodes.clear();
	connected = false;
	names.clear();
	nodes.splice(nodes.end(), net.nodes);
	Disconnect();
}

list<string> Network::FindVoidSignals(void)
// Returns a list of void (unfed) signals
{
	list<string> voidnodes;
	list<Node>::iterator ndi;
	unsigned int i;
	vector<string>::iterator nameit;
	
	names.clear();
	GenerateNames();
	// for nodes
	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		for ( i = 0; i < (*ndi).data.GetInputs(); i++ )
			if ( FindName( (*ndi).data.GetIName(i) ) == NULL ) voidnodes.push_back( (*ndi).data.GetIName(i) );
	// for POs
	for ( nameit = onames.begin(); nameit != onames.end(); nameit++ )
		if ( FindName(*nameit) == NULL )
			voidnodes.push_back( *nameit );
	return voidnodes;
}

void Network::AddInput( const string name )
// Adds an input, if not present
{
	NameStruct n;
	vector<string>::iterator nameit;

	for ( nameit = inames.begin(); nameit != inames.end(); nameit++ )
		if ( *nameit == name ) return;
	n.pos = inames.size();
	n.name = name;
	n.ndi = nodes.end();
	n.PI = true;
	names.push_back(n);
	inames.push_back(name);
	Disconnect();
}

void Network::AddOutput( const string name )
// Adds an output, if not present
{
	vector<string>::iterator nameit;

	for ( nameit = onames.begin(); nameit != onames.end(); nameit++ )
		if ( *nameit == name ) return;
	onames.push_back(name);
	Disconnect();
}

bool Network::isPI( const string name ) const
// is the signal PI?
{
	vector<string>::const_iterator nameit;

	for ( nameit = inames.begin(); nameit != inames.end(); nameit++ )
		if ( name == *nameit ) return true;
	return false;
}

bool Network::isPO( const string name ) const
// is the signal PI?
{
	vector<string>::const_iterator nameit;

	for ( nameit = onames.begin(); nameit != onames.end(); nameit++ )
		if ( name == *nameit ) return true;
	return false;
}

list<NameStruct> *Network::GetNameStruct(void)
{
	GenerateNames();
	return &names;
}

unsigned int Network::GetIndex(const list<Node>::iterator ndi)
// returns index of a given node. If not indexed, performs reindexation
{
	if ( indexnodes.empty() ) Reindex();
	return (*ndi).index;
}

unsigned int Network::GetLiterals(void)
// returns the number of literals. Only input matrices are considered
{
	list<Node>::iterator ndi;
	unsigned int cnt = 0;

	for ( ndi = nodes.begin(); ndi != nodes.end(); ndi++ )
		cnt += (*ndi).GetData()->GetLiterals();
	return cnt;
}
