/**
	\file "Object/inst/connection_policy.cc"
	$Id: connection_policy.cc,v 1.12 2009/07/02 23:22:47 fang Exp $
 */

#define	ENABLE_STACKTRACE			0

#include <iostream>
#include <iomanip>
#include <string>
#include "Object/inst/connection_policy.h"
#include "Object/devel_switches.h"
#include "util/IO_utils.tcc"
#include "util/stacktrace.h"

namespace HAC {
namespace entity {
using std::string;
using util::write_value;
using util::read_value;
#include "util/using_ostream.h"

//=============================================================================
// bool_connect_policy method definitions
// TODO: handle directions

/**
	These strings should be ordered according their corresponding
	bit fields (enum) in the attribute flags.  
 */
const char*
bool_connect_policy::attribute_names[] = {
	"iscomb",
	"!autokeeper",
	"isrvc1",
	"isrvc2",
	"isrvc3",
	"may_interfere",
	"may_weak_interfere",
};

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Attributes are simply copied up the instance hierarchy
	through ports.  
	\param t formal instance alias
 */
void
bool_connect_policy::initialize_actual_direction(const this_type& t) {
	STACKTRACE_VERBOSE;
#if BOOL_PRS_CONNECTIVITY_CHECKING
#if ENABLE_STACKTRACE
	t.dump_raw_attributes(STACKTRACE_INDENT_PRINT("formal: ")) << endl;
	dump_raw_attributes(STACKTRACE_INDENT_PRINT("init: ")) << endl;
#endif
	attributes = (t.attributes & BOOL_ATTRIBUTES_MASK)
		| (attributes & BOOL_INIT_ATTRIBUTES_MASK);
		// to preserve local port flag
	if (t.attributes & BOOL_ANY_FANOUT_PULL_DN)
		attributes |= BOOL_SUBSTRUCT_FANOUT_PULL_DN;
	if (t.attributes & BOOL_ANY_FANOUT_PULL_UP)
		attributes |= BOOL_SUBSTRUCT_FANOUT_PULL_UP;
	if (t.attributes & BOOL_ANY_FANIN_PULL_DN)
		attributes |= BOOL_SUBSTRUCT_FANIN_PULL_DN;
	if (t.attributes & BOOL_ANY_FANIN_PULL_UP)
		attributes |= BOOL_SUBSTRUCT_FANIN_PULL_UP;
#if ENABLE_STACKTRACE
	dump_raw_attributes(STACKTRACE_INDENT_PRINT("after: ")) << endl;
#endif
#else	// BOOL_PRS_CONNECTIVITY_CHECKING
	attributes = t.attributes;
#endif	// BOOL_PRS_CONNECTIVITY_CHECKING
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Check for conflicting flags when assigning.
 */
good_bool
bool_connect_policy::set_connection_flags(const connection_flags_type f) {
	STACKTRACE_VERBOSE;
	// no possible conflicts yet
	// maybe later with connectivity constraints
	attributes |= f;
	return good_bool(true);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	TODO: in future, not every field will be OR'd, use masks
	to compute result.
 */
good_bool
bool_connect_policy::synchronize_flags(this_type& l, this_type& r) {
	STACKTRACE_VERBOSE;
	l.attributes |= r.attributes;
	r.attributes = l.attributes;
	// TODO: handle direction checking!
	STACKTRACE_INDENT_PRINT("flags/connectivity = 0x" << std::hex <<
		l.attributes << ", 0x" << std::hex << r.attributes << endl);
	return good_bool(true);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	For debugging only.
 */
ostream&
bool_connect_policy::dump_raw_attributes(ostream& o) const {
	return o << "0x" << std::hex << attributes;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Print only names of non-default attribute values.  
	This is only for object dump debugging purposes, 
	format is not important.
	TODO: print direction flags separately?
 */
ostream&
bool_connect_policy::dump_attributes(ostream& o) const {
	// maintain order of these strings w.r.t. flag enums
if (has_nondefault_attributes()) {
	o << " @[";
	dump_flat_attributes(o);
	o << " ]";
}
	return o;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Only applies to hflat, called by cflat_prs_printer()
 */
ostream&
bool_connect_policy::dump_flat_attributes(ostream& o) const {
	connection_flags_type temp = attributes;	// better be unsigned!
	const char** p = attribute_names;
while (temp && p < attribute_names +7) {
	// b/c upper bits are connectivity
	if (temp & 1) {
		o << ' ' << *p;
	}
	++p;
	temp >>= 1;
}
	return o;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Only applies to hflat, called by cflat_prs_printer()
 */
ostream&
bool_connect_policy::dump_split_attributes(ostream& o, const string& n) const {
	connection_flags_type temp = attributes;	// better be unsigned!
	const char** p = attribute_names;
while (temp && p < attribute_names +7) {
	// b/c upper bits are connectivity
	if (temp & 1) {
		o << "@ " << n << ' ' << *p << endl;
	}
	++p;
	temp >>= 1;
}
	return o;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void
bool_connect_policy::write_flags(ostream& o) const {
	write_value(o, attributes);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void
bool_connect_policy::read_flags(istream& i) {
	read_value(i, attributes);
}

//=============================================================================
// channel_connect_policy method definitions
/**
	Check for conflicting flags.  
	Won't bother templating this method unless something else other
	than channels is directional.  
	Is this only called by CHP visits?
	Checks can be a lot more sophisticated, depending on desired semantics.
 */
good_bool
channel_connect_policy::set_connection_flags(
		const connection_flags_type f) {
	STACKTRACE_VERBOSE;
	if (f & CONNECTED_CHP_PRODUCER) {
		if (direction_flags & CONNECTED_TO_NONCHP_PRODUCER) {
			cerr << "Error: cannot connect to producer by both "
				"aliasing and CHP!" << endl;
			return good_bool(false);
		}
	} 
	// mutually exclusive, by caller
	else if (f & CONNECTED_CHP_CONSUMER) {
		if (direction_flags & CONNECTED_TO_NONCHP_CONSUMER) {
			cerr << "Error: cannot connect to consumer by both "
				"aliasing and CHP!" << endl;
			return good_bool(false);
		}
	}
	const connection_flags_type _or = f | direction_flags;
	if (!check_meta_nonmeta_usage(_or, "channel").good) {
		// already have error message
		return good_bool(false);
	}
	// already connected in CHP, connecting again OK
	direction_flags |= _or;
	return good_bool(true);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Checks to make sure a producer/consumer isn't referenced both
	by meta (constant) and nonmeta means.  
	\param _or the bitwise or of two flag sets to check.  
	\param n meta-type name.  
 */
good_bool
channel_connect_policy::check_meta_nonmeta_usage(
		const connection_flags_type _or, 
		const char* n) {
	STACKTRACE_VERBOSE;
	bool good = true;
	if ((_or & CONNECTED_PRODUCER_IS_META) &&
			(_or & CONNECTED_PRODUCER_IS_NONMETA)) {
		cerr << "Error: cannot mix meta- and nonmeta-referenced " <<
			n << " in producer alias." << endl;
		good = false;
	}
	if ((_or & CONNECTED_CONSUMER_IS_META) &&
			(_or & CONNECTED_CONSUMER_IS_NONMETA)) {
		cerr << "Error: cannot mix meta- and nonmeta-referenced " <<
			n << " in consumer alias." << endl;
		good = false;
	}
	return good_bool(good);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Write out flags.  
 */
void
channel_connect_policy::write_flags(ostream& o) const {
	write_value(o, direction_flags);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Write out flags.  
 */
void
channel_connect_policy::read_flags(istream& i) {
	read_value(i, direction_flags);
}

//=============================================================================
}	// end namespace entity
}	// end namespace HAC

