/**
	\file "Object/global_entry.tcc"
	$Id: global_entry.tcc,v 1.22 2008/11/23 17:53:34 fang Exp $
 */

#ifndef	__HAC_OBJECT_GLOBAL_ENTRY_TCC__
#define	__HAC_OBJECT_GLOBAL_ENTRY_TCC__

#ifndef	ENABLE_STACKTRACE
#define	ENABLE_STACKTRACE		0
#endif

#ifndef	STACKTRACE_PERSISTENTS
#define	STACKTRACE_PERSISTENTS		(0 && ENABLE_STACKTRACE)
#endif

#include <string>
#include <iostream>
#include <algorithm>
#include <functional>
#include <iterator>
#include "util/macros.h"
#include "util/sstream.h"
#include "Object/global_entry.h"
#include "Object/state_manager.h"
#include "Object/def/footprint.h"
#include "Object/traits/proc_traits.h"
#include "Object/traits/struct_traits.h"
#include "Object/traits/chan_traits.h"
#include "Object/inst/instance_alias_info.h"
#include "Object/inst/alias_empty.tcc"
#include "Object/inst/alias_actuals.tcc"	// for dump_complete_type
#include "Object/inst/instance_collection.h"
#include "Object/inst/port_alias_tracker.h"
#include "Object/traits/type_tag_enum.h"
#include "Object/common/dump_flags.h"
#include "Object/cflat_context.h"
#include "Object/global_entry_context.h"
#include "Object/lang/cflat_visitor.h"
#include "Object/lang/PRS_footprint.h"
#include "Object/lang/SPEC_footprint.h"
#include "Object/lang/CHP.h"		// for concurrent_actions
#include "Object/inst/datatype_instance_collection.h"
#include "Object/inst/general_collection_type_manager.h"
#include "Object/def/process_definition.h"
#include "Object/def/user_def_datatype.h"
#include "Object/type/canonical_generic_chan_type.h"
#include "util/persistent_object_manager.tcc"
#include "util/stacktrace.h"
#include "util/IO_utils.h"
#include "util/memory/index_pool.h"
#include "common/TODO.h"
#include "common/ICE.h"

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

//=============================================================================
// class footprint_frame_map method definitions

template <class Tag>
footprint_frame_map<Tag>::~footprint_frame_map() { }

//=============================================================================
/**
	\param sm the global state allocator.  
	\param gec the referencing entry with parent_id, and local_offset.
	\return reference to the parent super-instance global entry.  
 */
template <class Tag>
const global_entry<Tag>&
extract_parent_entry(const state_manager& sm, 
		const global_entry_common& gec) {
	const global_entry_pool<Tag>&
		gproc_pool(sm.template get_pool<Tag>());
	return gproc_pool[gec.parent_id];
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Resolves a global parent instance reference.
	\param Tag must be the tag of a meta type with substructure, 
		such as process_tag, channel_tag, datastruct_tag.  
	\param sm the global state allocator.  
	\param gec the referencing entry with parent_id, and local_offset.
		The parent_id member is the global id of the parent instance.
		The local_offset member the position of the corresponding
		formal instance alias in the parent's footprint.  
 */
template <class Tag>
const instance_alias_info<Tag>&
extract_parent_formal_instance_alias(const state_manager& sm, 
		const global_entry_common& gec) {
	const global_entry<Tag>&
		p_ent(extract_parent_entry<Tag>(sm, gec));
	const footprint& pfp(*p_ent._frame._footprint);
	const typename state_instance<Tag>::pool_type&
		local_placeholder_pool(pfp.template get_instance_pool<Tag>());
	const state_instance<Tag>&
		_inst(local_placeholder_pool[gec.local_offset]);
	return *_inst.get_back_ref();
}

//=============================================================================
// class footprint_frame method definitions

/**
	Prints the canonical type associated with this footprint_frame's
	reference footprint.  
	NOTE: enumerations are defined in "Object/traits/type_tag_enum.h"
	\param ind the globally assigned index (top-level) for state.  
 */
template <class Tag>
ostream&
footprint_frame::dump_footprint(global_entry_dumper& gec) const {
	typedef	typename state_instance<Tag>::pool_type	pool_type;
	typedef	instance_alias_info<Tag>	alias_type;
	INVARIANT(_footprint);
	ostream& o(gec.os);
	const size_t ind(gec.index);
	const footprint& topfp(*gec.topfp);
	const state_manager& sm(*gec.sm);
	const pool_type& _pool(topfp.template get_instance_pool<Tag>());
	if (ind >= _pool.size()) {
		// then this isn't top-level
		const global_entry_pool<Tag>&
			gpool(sm.template get_pool<Tag>());
		const global_entry<Tag>& ent(gpool[ind]);
		INVARIANT(ent.parent_tag_value);
		INVARIANT(ent.parent_id);
		switch (ent.parent_tag_value) {
		case PARENT_TYPE_PROCESS: {
			instance_alias_info<process_tag>::dump_complete_type(
				extract_parent_formal_instance_alias<
					process_tag>(sm, ent),
				o, _footprint);
			break;
		}
#if !BUILTIN_CHANNEL_FOOTPRINTS
		// case PARENT_TYPE_CHANNEL: ?
#endif
#if ENABLE_DATASTRUCTS
		case PARENT_TYPE_STRUCT: {
			instance_alias_info<datastruct_tag>::dump_complete_type(
				extract_parent_formal_instance_alias<
					datastruct_tag>(sm, ent),
				o, _footprint);
			break;
		}
#endif
		default:
			// for now, the only thing that can be parent
			// is process, append cases later...
			FINISH_ME_EXIT(Fang);
		}
	} else {
		// then it is top-level, can index the top-level footprint
		const state_instance<Tag>& _inst(_pool[ind]);
		alias_type::dump_complete_type(*_inst.get_back_ref(),
			o, _footprint);
	}
	return o;
}

//=============================================================================
// class global_entry_substructure_base method definitions

template <class Tag>
ostream&
global_entry_substructure_base<false>::dump(global_entry_dumper& ged) const {
	return ged.os;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Prints the type with which the footprint is associated, 
	and the brief contents of the footprint frame.  
 */
template <class Tag>
ostream&
global_entry_substructure_base<true>::dump(global_entry_dumper& ged) const {
	this->_frame.template dump_footprint<Tag>(ged);
	return this->_frame.dump_frame(ged.os);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
template <class Tag>
size_t
global_entry_substructure_base<true>::count_frame_size(const size_t s, 
		const this_type& t) {
	return s +t._frame.count_frame_size();
}

//=============================================================================
// class global_entry method definitions

template <class Tag>
global_entry<Tag>::global_entry() : 
		parent_type(), global_entry_common() {
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
template <class Tag>
global_entry<Tag>::~global_entry() { }

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if 0
template <class Tag>
footprint_frame_map_type&
footprint_frame::get_frame_map(void) {
	return footprint_frame_map<Tag>::id_map;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
template <class Tag>
const footprint_frame_map_type&
footprint_frame::get_frame_map(void) const {
	return footprint_frame_map<Tag>::id_map;
}
#endif

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Recursively prints canonical name.  
 */
template <class Tag>
ostream&
global_entry<Tag>::__dump_canonical_name(ostream& o, const dump_flags& df, 
		const footprint& topfp, const state_manager& sm) const {
	typedef	typename state_instance<Tag>::pool_type	pool_type;
	const pool_type& _pool(topfp.template get_instance_pool<Tag>());
	// dump canonical name
	const state_instance<Tag>* _inst;
	switch (parent_tag_value) {
	case PARENT_TYPE_NONE:
		_inst = &_pool[local_offset];
		break;
	case PARENT_TYPE_PROCESS: {
		const global_entry<process_tag>&
			p_ent(extract_parent_entry<process_tag>(sm, *this));
		p_ent.__dump_canonical_name(o, df, topfp, sm) << '.';
		// partial, omit formal type parent
		const pool_type&
			_lpool(p_ent._frame._footprint
				->template get_instance_pool<Tag>());
		_inst = &_lpool[local_offset];
		break;
	}
#if !BUILTIN_CHANNEL_FOOTPRINTS
	case PARENT_TYPE_CHANNEL: {
		const global_entry<channel_tag>&
			p_ent(extract_parent_entry<channel_tag>(sm, *this));
		p_ent.__dump_canonical_name(o, df, topfp, sm) << '.';
		// partial, omit formal type parent
		const pool_type&
			_lpool(p_ent._frame._footprint
				->template get_instance_pool<Tag>());
		_inst = &_lpool[local_offset];
		break;
	}
#endif
#if ENABLE_DATASTRUCTS
	case PARENT_TYPE_STRUCT: {
		const global_entry<datastruct_tag>&
			p_ent(extract_parent_entry<datastruct_tag>(sm, *this));
		p_ent.__dump_canonical_name(o, df, topfp, sm) << '.';
		// partial, omit formal type parent
		const pool_type&
			_lpool(p_ent._frame._footprint
				->template get_instance_pool<Tag>());
		_inst = &_lpool[local_offset];
		break;
	}
#endif
	default:
		ICE(cerr, 
			cerr << "Unknown parent tag enumeration: " <<
				parent_tag_value << endl;
		)
	}
	const instance_alias_info<Tag>& _alias(*_inst->get_back_ref());
	return _alias.dump_hierarchical_name(o, df);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Wrapped call that formats properly.  
	\param topfp should be the top-level footprint belonging to the module.
	This could just take a global_entry_context_base bundled argument.  
 */
template <class Tag>
ostream&
global_entry<Tag>::dump_canonical_name(ostream& o,
		const footprint& topfp, const state_manager& sm) const {
#if 1
	return __dump_canonical_name(o, dump_flags::no_definition_owner,
		topfp, sm);
#else
	return __dump_canonical_name(o, dump_flags::no_leading_scope,
		topfp, sm);
#endif
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	NOTE: currently, only processes are ever super instances.  
 */
template <class Tag>
ostream&
global_entry<Tag>::dump(global_entry_dumper& ged) const {
	ostream& o(ged.os);
	o << ged.index << '\t';
	switch (parent_tag_value) {
	case PARENT_TYPE_NONE:
		o << "(top)\t-\t";
		break;
	case PARENT_TYPE_PROCESS:
		o << "process\t" << parent_id << '\t';
		break;
	case PARENT_TYPE_CHANNEL:
		o << "channel\t" << parent_id << '\t';
		break;
	case PARENT_TYPE_STRUCT:
		o << "struct\t" << parent_id << '\t';
		break;
	default:
		THROW_EXIT;
	}
	o << local_offset << '\t';
	dump_canonical_name(o, *ged.topfp, *ged.sm) << '\t';
	dump_attributes(ged);
	parent_type::template dump<Tag>(ged);
	return o;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
template <class Tag>
ostream&
global_entry<Tag>::dump_attributes(global_entry_dumper& ged) const {
	return get_canonical_instance(ged)
		.get_back_ref()->dump_attributes(ged.os);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	This returns a unique leaf instance in the hierarchy that contains the
	instance and alias information.  
	The instance reference returned can be either top-level
	or a subinstance of a structure in the instance hierarchy.  
 */
template <class Tag>
const state_instance<Tag>&
global_entry<Tag>::get_canonical_instance(
		const global_entry_context_base& ged) const {
	typedef	typename state_instance<Tag>::pool_type	pool_type;
	const footprint& topfp(*ged.get_top_footprint_ptr());
	const state_manager& sm(*ged.get_state_manager());
	const pool_type& _pool(topfp.template get_instance_pool<Tag>());
	const state_instance<Tag>* _inst;
	switch (parent_tag_value) {
	case PARENT_TYPE_NONE:
		_inst = &_pool[local_offset];
		break;
	// for now, only processes can be parents
	case PARENT_TYPE_PROCESS: {
		const global_entry<process_tag>&
			p_ent(extract_parent_entry<process_tag>(sm, *this));
		const pool_type&
			_lpool(p_ent._frame._footprint
				->template get_instance_pool<Tag>());
		_inst = &_lpool[local_offset];
		break;
	}
	default:
		ICE(cerr, 
			cerr << "Unknown parent tag enumeration: " <<
				parent_tag_value << endl;
		)
	}
	NEVER_NULL(_inst);
	return *_inst;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
template <class Tag>
void
global_entry<Tag>::accept(PRS::cflat_visitor& v) const {
	STACKTRACE_VERBOSE;
	v.visit(*this);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	TODO: use global_entry_dumper
 */
template <class Tag>
void
global_entry<Tag>::write_object_base(const persistent_object_manager& m, 
		ostream& o, const size_t ind, const footprint& f, 
		const state_manager& sm) const {
	STACKTRACE_PERSISTENT_VERBOSE;
	write_value(o, parent_tag_value);
	STACKTRACE_PERSISTENT_PRINT("parent_tag = " << size_t(parent_tag_value) << endl);
	write_value(o, parent_id);
	STACKTRACE_PERSISTENT_PRINT("parent_id = " << parent_id << endl);
	write_value(o, local_offset);
	STACKTRACE_PERSISTENT_PRINT("local_offset = " << local_offset << endl);
	parent_type::write_object_base(m, o, ind, f, sm);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Ordering matters because parent_type::load_object_base
		depends on parent_id and parent_tag_value
		in reconstructing the footprint pointer.  
 */
template <class Tag>
void
global_entry<Tag>::load_object_base(const persistent_object_manager& m, 
		istream& i, const size_t ind, const footprint& f, 
		const state_manager& sm) {
	STACKTRACE_PERSISTENT_VERBOSE;
	read_value(i, parent_tag_value);
	STACKTRACE_PERSISTENT_PRINT("parent_tag = " << size_t(parent_tag_value) << endl);
	read_value(i, parent_id);
	STACKTRACE_PERSISTENT_PRINT("parent_id = " << parent_id << endl);
	read_value(i, local_offset);
	STACKTRACE_PERSISTENT_PRINT("local_offset = " << local_offset << endl);
	parent_type::load_object_base(m, i, ind, f, sm);
}

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

#endif	// __HAC_OBJECT_GLOBAL_ENTRY_TCC__

