/**
	\file "Object/inst/subinstance_manager.cc"
	Class implementation of the subinstance_manager.
	$Id: subinstance_manager.cc,v 1.24 2008/11/12 03:00:08 fang Exp $
 */

#define	ENABLE_STACKTRACE		0

#include <iostream>
#include "Object/inst/subinstance_manager.h"
#include "Object/inst/physical_instance_collection.h"
#include "Object/inst/port_alias_tracker.h"
#include "Object/ref/meta_instance_reference_base.h"
#include "Object/type/fundamental_type_reference.h"
#include "Object/port_context.h"
#include "Object/common/dump_flags.h"
#include "Object/inst/physical_instance_placeholder.h"
#include "common/ICE.h"
#include "Object/def/footprint.h"
#include "util/IO_utils.h"
#include "util/persistent_object_manager.tcc"
#include "util/memory/count_ptr.tcc"
#include "util/reserve.h"
#include "util/indent.h"
#include "util/stacktrace.h"

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

//=============================================================================
// class subinstance_manager method definitions

subinstance_manager::subinstance_manager() : subinstance_array() { }

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	TODO: not your everyday copy-constructor, needs to 
	re-establish local connections.  
 */
subinstance_manager::subinstance_manager(const this_type& s) :
	subinstance_array(s.subinstance_array) {
	// 2005-07-16: bug fixed, forgot to copy the array!
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
subinstance_manager::~subinstance_manager() {
	STACKTRACE_DTOR_VERBOSE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ostream&
subinstance_manager::dump_ports(ostream& o, const dump_flags& df) const {
if (subinstance_array.empty()) {
	return o;
} else {
	o << '(' << endl;
	{
	INDENT_SECTION(o);
	const_iterator i(subinstance_array.begin());
	const const_iterator e(subinstance_array.end());
	for ( ; i!=e; i++) {
		NEVER_NULL(*i);
		// unqualified name is sufficient
		o << auto_indent << (*i)->get_name() << " = ";
		(*i)->dump(o, df) << endl;
	}
	}
	return o << auto_indent << ')';
}
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void
subinstance_manager::push_back(const entry_value_type& v) {
	subinstance_array.push_back(v);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void
subinstance_manager::reserve(const size_t s) {
	util::reserve(subinstance_array, s);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	NOTE: This cannot be used to create an instance reference to 
		private members because this only looks up public ports.  
	TODO: write a version to make arbitrary member references.  
	\param i reference to formal instance, to be translated to an
		actual (unrolled) instance reference.  
	TODO: invariant check for matching definitions and types.  
 */
subinstance_manager::value_type
subinstance_manager::lookup_port_instance(
		const lookup_arg_type& i) const {
	STACKTRACE_VERBOSE;
	const size_t index = i.is_port_formal();
	if (index > subinstance_array.size()) {
	ICE(cerr, 
		cerr << "got port index of " << index
			<< " when limit is " << subinstance_array.size()
			<< endl;
		i.dump(cerr << "\twhile looking up: ", 
			dump_flags::verbose) << endl;
//		cerr << "Here\'s the complete dump of this subinstance set: "
//			"at " << this << endl;
//		dump(cerr) << endl;
	)
	}
	INVARIANT(index);
	INVARIANT(index <= subinstance_array.size());
	return subinstance_array[index-1];
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if RECURSE_COLLECT_ALIASES
void
subinstance_manager::collect_port_aliases(port_alias_tracker& t) const {
	STACKTRACE_VERBOSE;
	const_iterator pi(subinstance_array.begin());
	const const_iterator pe(subinstance_array.end());
	for ( ; pi!=pe; pi++) {
		(*pi)->collect_port_aliases(t);
	}
}
#endif

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	\param cr sequence of meta instance references to resolve and 
		connect to ports.  
	\pre list sizes (ports vs. references) must be equal, 
		already checked earlier.  
	TODO: future, let port instantiation be on-demand, 
		so check whether or not this is expanded first.  
 */
good_bool
subinstance_manager::__connect_ports(
		const connection_references_type& cr, 
		const unroll_context& c) {
	STACKTRACE_VERBOSE;
	typedef	connection_references_type::const_iterator
						const_ref_iterator;
	INVARIANT(subinstance_array.size() == cr.size());
	iterator pi(subinstance_array.begin());	// instance_collection_type
	const iterator pe(subinstance_array.end());
	const_ref_iterator ri(cr.begin());
	// const const_ref_iterator re(cr.end());
	for ( ; pi!=pe; pi++, ri++) {
	// references may be NULL (no-connect)
		if (*ri) {
			if ((*ri)->connect_port(**pi, c).bad) {
				// already have error message
				return good_bool(false);
			}	// else good to continue
		}
	}
	// all connections good
	return good_bool(true);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	\param cr sequence of meta instance references to resolve and 
		connect to ports.  
	\pre list sizes (ports vs. references) must be equal, 
		already checked earlier.  
	TODO: future, let port instantiation be on-demand, 
		so check whether or not this is expanded first.  
 */
good_bool
subinstance_manager::connect_port_aliases_recursive(this_type& r,
		const unroll_context& c) {
	STACKTRACE_VERBOSE;
	INVARIANT(subinstance_array.size() == r.subinstance_array.size());
	iterator pi(subinstance_array.begin());	// instance_collection_type
	iterator ri(r.subinstance_array.begin());
	const iterator pe(subinstance_array.end());
	for ( ; pi!=pe; ++pi, ++ri) {
		NEVER_NULL(*ri);
		if (!(*ri)->connect_port_aliases_recursive(**pi, c).good) {
			// already have error message?
			return good_bool(false);
		}	// else good to continue
	}
	return good_bool(true);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Re-links all sub-instances to a parent instance.  
	Needed to maintain the hierarchy.  
 */
void
subinstance_manager::relink_super_instance_alias(
		const substructure_alias& p) {
	STACKTRACE_VERBOSE;
	iterator i(subinstance_array.begin());
	const iterator e(subinstance_array.end());
	for ( ; i!=e; i++) {
		NEVER_NULL(*i);
		(*i)->relink_super_instance(p);
	}
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Allocates each port entirely.  
	TODO: give this a return value for error handling.  
 */
void
subinstance_manager::allocate_subinstances(footprint& f) {
	STACKTRACE("subinstance_manager::allocate()");
	iterator i(subinstance_array.begin());
	const iterator e(subinstance_array.end());
	for ( ; i!=e; i++) {
		NEVER_NULL(*i);
		if (!(*i)->allocate_local_instance_ids(f).good) {
			cerr << "Error allocating local instance IDs" << endl;
			THROW_EXIT;
		}
	}
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void
subinstance_manager::__construct_port_context(port_member_context& pmc, 
		const footprint_frame& ff) const {
	STACKTRACE_VERBOSE;
	pmc.resize(size());
	size_t j = 0;
	const_iterator i(subinstance_array.begin());
	const const_iterator e(subinstance_array.end());
	for ( ; i!=e; i++, j++) {
		(*i)->construct_port_context(pmc[j], ff);
	}
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void
subinstance_manager::__assign_footprint_frame(footprint_frame& ff, 
		const port_member_context& pmc) const {
	INVARIANT(pmc.size() == subinstance_array.size());
	size_t j = 0;
	const_iterator i(subinstance_array.begin());
	const const_iterator e(subinstance_array.end());
	for ( ; i!=e; i++, j++) {
		(*i)->assign_footprint_frame(ff, pmc[j]);
	}
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	NOTE: this doesn't use the visitee's accept method.  
	This forwards the calls to physical_instance_collection::accept.
 */
void
subinstance_manager::accept(alias_visitor& v) const {
	const_iterator i(subinstance_array.begin());
	const const_iterator e(subinstance_array.end());
	for ( ; i!=e; i++) {
		const never_ptr<physical_instance_collection> pi(*i);
		pi->accept(v);
	}
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Is there really a need for this when collections are managed by 
	the footprint's pool bundles?  Probably not.  
 */
void
subinstance_manager::collect_transient_info_base(
		persistent_object_manager& m) const {
	STACKTRACE_PERSISTENT_VERBOSE;
	STACKTRACE_PERSISTENT_PRINT("collected " << subinstance_array.size() <<
		" subinstances." << endl);
	const_iterator i(subinstance_array.begin());
	const const_iterator e(subinstance_array.end());
	for ( ; i!=e; ++i) {
		(*i)->collect_transient_info_base(m);
	}
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Need footprint to manage and translate pooled collections.  
 */
void
subinstance_manager::write_object_base(const footprint& f, ostream& o) const {
	STACKTRACE_PERSISTENT_VERBOSE;
	const size_t s = subinstance_array.size();
	write_value(o, s);
	const_iterator i(subinstance_array.begin());
	const const_iterator e(subinstance_array.end());
	for ( ; i!=e; ++i) {
		// doesn't raelly write a pointer, but index
		(*i)->write_local_pointer(f, o);
	}
	STACKTRACE_PERSISTENT_PRINT("wrote " << subinstance_array.size() <<
		" subinstances." << endl);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Is there a potential ordering problem with loading?
	Might loading have to be broken up into more phases?
	Load subinstances AFTER collections have been loaded, 
	or at least allocated?
	Footprint should probably pre-allocate before loading...
	\pre footprint needs to be mapped out already, may need a 
		two-pass implementation.  
 */
void
subinstance_manager::load_object_base(const footprint& f, istream& i) {
	STACKTRACE_PERSISTENT_VERBOSE;
	size_t s;
	read_value(i, s);
	subinstance_array.resize(s);
	iterator j(subinstance_array.begin());
	const iterator e(subinstance_array.end());
	for ( ; j!=e; ++j) {
		*j = f.read_pointer(i).is_a<physical_instance_collection>();
		NEVER_NULL(*j);
	}
	STACKTRACE_INDENT_PRINT("loaded " << subinstance_array.size() <<
		" subinstances." << endl);
}

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

