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

#include "stdafx.h"

#define MAXLINE 10000

using namespace std;

#ifdef _MSC_VER
#pragma hdrstop
#endif

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

#include "Blif.h"

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


Blif::Blif()
{
}

Blif::~Blif()
{
}

Blif::Blif( const Blif& orig )
{
	inames = orig.inames;
	onames = orig.onames;
}

unsigned int Blif::GetInputs(void) const
{
	return inames.size();
}

unsigned int Blif::GetOutputs(void) const
{
	return onames.size();
}

Blif::Blif( const vector<string> _inames, const vector<string> _onames )
{
	inames = _inames;
	onames = _onames;
}

void Blif::clear(void)
{
	inames.clear();
	onames.clear();
	data.clear();
}

void Blif::LoadBlif(const char *fn)
{
    unsigned int pos;
    ifstream f;
    string s, s1;
    char line[MAXLINE];
    Cube t;
    unsigned int i;
	vector<string> tmpnames;
	vector<string>::iterator tmpnames_it;

    this->clear();

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

			// read the header
    f.getline(line, MAXLINE);
	while ( !f.eof() ) {
        pos = 0;
		if ( line[0] != '#' && line[0] != 0 ) {
			if ( line[0] == '.' ) {
			   s = GetToken(line, pos);
			   if ( s == ".model" ) {
					s = GetToken(line, pos);
					model = s;
			   } else if ( s == ".inputs" ) {
					s = GetToken(line, pos);
					while ( !s.empty() ) {
						if ( s == "\\" ) {
							f.getline(line, MAXLINE);
							pos = 0;
							s = GetToken(line, pos);
						}
						inames.push_back(s);
						s = GetToken(line, pos);
					}
			   } else if ( s == ".outputs" ) {
					s = GetToken(line, pos);
					while ( !s.empty() ) {
						if ( s == "\\" ) {
							f.getline(line, MAXLINE);
							pos = 0;
							s = GetToken(line, pos);
						}
						onames.push_back(s);
						s = GetToken(line, pos);
					}
			   } else if ( s == ".default_input_arrival" ) {
					s = GetToken(line, pos);
					s = GetToken(line, pos);
			   } else if ( s == ".default_output_required" ) {
					s = GetToken(line, pos);
					s = GetToken(line, pos);
			   } else if ( s == ".default_input_drive" ) {
					s = GetToken(line, pos);
					s = GetToken(line, pos);
			   } else if ( s == ".default_output_load" ) {
					s = GetToken(line, pos);
			   } else if ( s == ".default_max_input_load" ) {
					s = GetToken(line, pos);

			   } else if ( s == ".names" ) break;		// end of the header
			   else if ( s == ".gate" ) break;		// end of the header
			   else if ( s == ".end" ) break;
			   else throw IOException( "Bad or damaged Blif file: " + string(fn) );
			}
		}
		f.getline(line, MAXLINE);
	}
			// read nodes
	while ( !f.eof() ) {
        pos = 0;
		if ( line[0] != '#' && line[0] != 0 ) {
			if ( line[0] == '.' ) {
			   s = GetToken(line, pos);
			   if ( s == ".end" ) break;
			   else if ( s == ".names" ) {
					tmpnames.clear();
					s = GetToken(line, pos);
					while ( !s.empty() ) {
						if ( s == "\\" ) {
							f.getline(line, MAXLINE);
							pos = 0;
							s = GetToken(line, pos);
						}
						tmpnames.push_back(s);
						s = GetToken(line, pos);
					}
					if ( tmpnames.size() == 0 ) throw IOException( "Bad or damaged Blif file: " + string(fn) );
					if ( tmpnames.size() == 1 ) {	// constant 0 or 1 node
						data.push_back(SoPLA(1));
					} else data.push_back(SoPLA(tmpnames.size()-1));
					SoPLA& node = data.back();
					node.SetName(tmpnames.back());		// set node names
					tmpnames.pop_back();	// remove the last name (output)
					i = 0;
					for ( tmpnames_it = tmpnames.begin(); tmpnames_it != tmpnames.end(); tmpnames_it++ )
						node.SetIName(i++, (*tmpnames_it));	

						// parse the node PLA
					do {		
						f.getline(line, MAXLINE);
						if ( line[0] != 0 && line[0] != '.' ) {
							pos = 0;				
							s = GetToken(line, pos);
							if ( s.length() != node.GetInputs() ) throw IOException( "Bad or damaged Blif file: " + string(fn) );
							if ( tmpnames.size() == 0 ) {						
								t.clear();
								t.SetIM("-");
								if ( s == "1" )	node.SetType(true);
								else if ( s == "0" ) node.SetType(false);
							} else {
								s1.clear();
								for ( i = 0; i < node.GetInputs(); i++ )
								  if ( s[i] == '0' || s[i] == '1' ) s1 += s[i];
								  else if ( s[i] == '-' || s[i] == '2' ) s1 += '-';
								  else throw IOException( "Bad or damaged Blif file: " + string(fn) );

								t.clear();
								t.SetIM(s1);

								s = GetToken(line, pos);
								if ( s.length() != 1 ) throw IOException( "Bad or damaged Blif file: " + string(fn) );
								if ( node.GetTerms() == 0 ) {
									if ( s == "1" )	node.SetType(true);
									else if ( s == "0" ) node.SetType(false);
									else throw IOException( "Bad or damaged Blif file: " + string(fn) );
								} else if ( (s != "0" && s != "1") || (s == "0" && node.GetType()) || (s == "1" && !node.GetType()) ) throw IOException( "Bad or damaged Blif file: " + string(fn) );
							}
							node.AppendTerm(t);
						} else if ( line[0] == '.' && node.GetTerms() == 0 ) {
							t.clear();
							t.SetIM("-");
							node.SetType(false);
							node.AppendTerm(t);
						}

					} while ( !f.eof() && line[0] != '.' );
					continue;
			   } else if ( s == ".gate" ) {
					data.push_back(SoPLA(1));

			   } else throw IOException( "Bad or damaged Blif file: " + string(fn) );
			}
		}
		f.getline(line, MAXLINE);
    }
	f.close();
}

void Blif::SaveBlif( const char *fn )
// Saves the network to a BLIF file
{
	ofstream f;
	unsigned int i;
	vector<string>::const_iterator name_it;
	list<SoPLA>::iterator node_it;
	CubeList::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 = data.begin(); node_it != data.end(); node_it++ ) {
		f << ".names";
		for ( i = 0; i < (*node_it).GetInputs(); i++ )
			if ( !(*node_it).GetIName(i).empty()) f << " " << (*node_it).GetIName(i);
			else throw RuntimeException( "Blif::SaveBlif: Empty node input name" );
		if ( (*node_it).GetName().empty() ) throw RuntimeException( "Blif::SaveBlif: Empty node output name" );
		f << " " << (*node_it).GetName() << endl;
		for ( ti = (*node_it).GetData()->begin(); ti != (*node_it).GetData()->end(); ti++ )
			f << (*ti).GetIM() << " " << (((*node_it).GetType())?"1":"0") << endl;
	}
	f << ".end" << endl;
}

void Blif::AppendPLA( const SoPLA *_pla )
{
	data.push_back( *_pla );
}

void Blif::Splice( list<SoPLA> &plas )
{
	data.splice(data.end(), plas);
}

void Blif::Splice( Blif &blif )
{
	data.splice(data.end(), blif.data);
}

void Blif::AppendBlif( const Blif *_blif )
// Joins data from two Blifs. Input and output names are not considered!
{
	list<SoPLA>::const_iterator pla_it;

	for ( pla_it = _blif->data.begin(); pla_it != _blif->data.end(); pla_it++ )
		this->AppendPLA(&*pla_it);
}

unsigned int Blif::GetLiterals(void)
{
	unsigned int cnt = 0;
	list<SoPLA>::iterator pla_it;

	for ( pla_it = data.begin(); pla_it != data.end(); pla_it++ )
		cnt += (*pla_it).GetLiterals();
	return cnt;
}

unsigned int Blif::GetGEs(void)
{
	unsigned int cnt = 0;
	list<SoPLA>::iterator pla_it;

	for ( pla_it = data.begin(); pla_it != data.end(); pla_it++ )
		cnt += (*pla_it).GetGEs();
	return cnt;
}

unsigned int Blif::GetLevels(void)
	// returns the number of BLIF levels (longest path)
{
	list<SoPLA>::iterator pla_it, pla_it2;
	vector<string>::iterator s_it;
	bool change;
	unsigned int max = 1;
	unsigned int i;
	
	for ( pla_it = data.begin(); pla_it != data.end(); pla_it++ )
		(*pla_it).tmp = 0;
	for ( s_it = inames.begin(); s_it != inames.end(); s_it++ )
		for ( pla_it = data.begin(); pla_it != data.end(); pla_it++ )
			for ( i = 0; i < (*pla_it).GetInputs(); i++ )
				if ( (*s_it) == (*pla_it).GetIName(i) ) (*pla_it).tmp = 1;
	do {
		change = false;
		for ( pla_it = data.begin(); pla_it != data.end(); pla_it++ )
			if ( (*pla_it).tmp != 0 )
				for ( pla_it2 = data.begin(); pla_it2 != data.end(); pla_it2++ )
					if ( pla_it != pla_it2 )
						for ( i = 0; i < (*pla_it2).GetInputs(); i++ )
							if ( (*pla_it).GetName() == (*pla_it2).GetIName(i) ) 
								if ( (*pla_it2).tmp <= (*pla_it).tmp ) {
									(*pla_it2).tmp = (*pla_it).tmp + 1;
									if ( (*pla_it2).tmp > max ) max = (*pla_it2).tmp;
									change = true;	
									break;
								}
	} while (change);
	return max;
}

unsigned int Blif::GetNonTrivNodes(void)
{
	unsigned int nd;
	list<SoPLA>::iterator pla_it;

	nd = data.size();
	for ( pla_it = data.begin(); pla_it != data.end(); pla_it++ )
		if ((*pla_it).GetInputs() < 2 ) nd--;
	return nd;
}

string Blif::GetIName( const unsigned int i ) const
{
	if ( i >= inames.size() ) throw RuntimeException("Bilf:: GetIName: wrong name index");
	return inames[i];
}

string Blif::GetOName( const unsigned int i ) const
{
	if ( i >= onames.size() ) throw RuntimeException("Bilf:: GetOName: wrong name index");
	return onames[i];
}


