/*
 * Copyright (c) 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Flick is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

#include <assert.h>
#include <ctype.h>
#include <string.h>

#include <mom/compiler.h>
#include <mom/cast.h>
#include <mom/c/libcast.h>
#include <mom/c/libpres_c.h>
#include <mom/c/be/presentation_impl.hh>

presentation_impl::presentation_impl()
{
	this->idl_type = 0;
	this->pres_type = 0;
	this->scope_impl = 0;
	this->misc_impl = 0;
	this->pc = 0;
}

presentation_impl::~presentation_impl()
{
}

void presentation_impl::set_idl_type(const char *type)
{
	this->idl_type = type;
}

const char *presentation_impl::get_idl_type()
{
	return( this->idl_type );
}

void presentation_impl::set_pres_type(const char *type)
{
	this->pres_type = type;
}

const char *presentation_impl::get_pres_type()
{
	return( this->pres_type );
}

void presentation_impl::set_scope_impl(void (*impl)(cast_scope *scope,
						    tag_list *tl))
{
	this->scope_impl = impl;
}

void (*presentation_impl::get_scope_impl())(cast_scope *scope, tag_list *tl)
{
	return( this->scope_impl );
}

void presentation_impl::set_misc_impl(void (*impl)(pres_c_1 *pres, tag_list *))
{
	this->misc_impl = impl;
}

void (*presentation_impl::get_misc_impl())(pres_c_1 *pres, tag_list *)
{
	return( this->misc_impl );
}

void presentation_impl::set_collection(struct presentation_collection *the_pc)
{
	this->pc = the_pc;
}

struct presentation_collection *presentation_impl::get_collection()
{
	return( this->pc );
}

void presentation_impl::write_pres_funcs(pres_c_1 *pres,
					 struct scml_scope *pres_scope,
					 tag_list *tl)
{
	struct scml_cmd_definition *cmd_def;
	struct scml_scope *cmd_scope;
	struct scml_context *sc;
	tag_list *func_tl;
	cast_def *cfunc;
	tag_item *ti;
	int lpc, len;
	
	ti = find_tag(tl, "pres_func");
	if( !ti )
		return;
	len = tag_data_length(&ti->data);
	/* Create an execution context */
	sc = new scml_context;
	sc->set_scope(pres_scope);
	sc->set_stream_pos(this->pc->get_scml_stream_pos());
	/* Walk through the array of pres_funcs */
	for( lpc = 0; lpc < len; lpc++ ) {
		func_tl = find_tag(tl, get_tag_data(&ti->data, lpc).str)->
			data.tag_data_u.tl;
		cfunc = &pres->stubs_cast.cast_scope_val[find_tag(func_tl,
								  "c_func")->
							data.tag_data_u.i];
		/* output only if they have a C decl and a definition */
		cmd_def = pres_scope->find_cmd_definition(
			&cmd_scope, get_tag_data(&ti->data, lpc).str);
		if( !(pres->meta_data.channels.channels_val[cfunc->channel].
		      flags & DATA_CHANNEL_SQUELCHED) && cmd_def ) {
			/* Execute the definition */
			sc->exec_cmd(get_tag_data(&ti->data, lpc).str,
				     func_tl,
				     NULL);
		} else if( !(pres->meta_data.channels.
			     channels_val[cfunc->channel].flags &
			     DATA_CHANNEL_SQUELCHED) &&
			   (cfunc->protection != CAST_PROT_PRIVATE) ) {
			fprintf(stderr,
				"Warning '%s' function in %s::%s "
				"not handled\n",
				get_tag_data(&ti->data, lpc).str,
				pres_scope->get_parent()->get_name(),
				pres_scope->get_name());
		}
	}
	delete sc;
}

void presentation_impl::write_model_funcs(pres_c_1 *pres,
					  struct scml_scope *pres_scope,
					  tag_list *tl)
{
	struct scml_scope *cmd_scope;
	struct scml_context *sc;
	tag_list *func_tl;
	cast_def *cfunc;
	char *kind_str;
	tag_item *ti;
	int lpc, len;
	
	ti = find_tag(tl, "model_func");
	if( !ti )
		return;
	len = tag_data_length(&ti->data);
	sc = new scml_context;
	sc->set_scope(pres_scope);
	sc->set_stream_pos(this->pc->get_scml_stream_pos());
	for( lpc = 0; lpc < len; lpc++ ) {
		func_tl = get_tag_data(&ti->data, lpc).tl;
		cfunc = &pres->stubs_cast.cast_scope_val[find_tag(func_tl,
								  "c_func")->
							data.tag_data_u.i];
		kind_str = find_tag(func_tl, "kind")->data.tag_data_u.str;
		if( !(pres->meta_data.channels.channels_val[cfunc->channel].
		      flags & DATA_CHANNEL_SQUELCHED) &&
		    pres_scope->find_cmd_definition(&cmd_scope, kind_str) ) {
			sc->exec_cmd(kind_str, func_tl, NULL);
		} else if( !(pres->meta_data.channels.
			     channels_val[cfunc->channel].flags &
			     DATA_CHANNEL_SQUELCHED) ) {
			fprintf(stderr,
				"Warning '%s' function model in %s::%s "
				"not handled\n",
				get_tag_data(&ti->data, lpc).str,
				pres_scope->get_parent()->get_name(),
				pres_scope->get_name());
		}
	}
}

void presentation_impl::implement(pres_c_1 *pres, tag_list *tl)
{
	if( this->scope_impl ) {
		cast_scoped_name scname;
		cast_scope *scope;
		cast_type ctype;
		tag_item *ti;
		int cdef;
		
		/* This is an operation on a type's scope */
		if( (ti = find_tag(tl, "name")) &&
		    (ti->data.kind == TAG_CAST_SCOPED_NAME) ) {
			/* Find the scope */
			scname = ti->data.tag_data_u.scname;
			scope = &pres->cast;
			cdef = cast_find_def(&scope, scname,
					     CAST_TYPEDEF|CAST_TYPE);
			while( cdef != -1 ) {
				ctype = scope->cast_scope_val[cdef].u.
					cast_def_u_u.type;
				switch( ctype->kind ) {
				case CAST_TYPE_TEMPLATE:
					ctype = ctype->cast_type_u_u.
						template_type.def;
					break;
				default:
					break;
				}
				if( ctype->kind == CAST_TYPE_AGGREGATE ) {
					scope = &ctype->cast_type_u_u.
						agg_type.scope;
					/* Let the function add to it */
					this->scope_impl(scope, tl);
					cdef = -1;
				}
				else {
					cdef = cast_find_def_pos(&scope,
								 cdef + 1,
								 scname,
								 CAST_TYPEDEF|
								 CAST_TYPE);
				}
			}
		}
	}
	if( this->misc_impl )
		this->misc_impl(pres, tl);
	if( this->pc->get_scml_scope() && this->pres_type ) {
		struct scml_scope *idl_scope, *pres_scope;
		
		/* Find the matching IDL scope in the SCML */
		if( (idl_scope =
		     this->pc->get_scml_scope()->
		     find_child(find_tag(tl, "idl_type")->
				data.tag_data_u.str)) ) {
			/* Find the matching pres scope in the SCML */
			if( (pres_scope = idl_scope->
			     find_child(this->pres_type)) ) {
				this->write_pres_funcs(pres, pres_scope, tl);
				this->write_model_funcs(pres, pres_scope, tl);
			} else {
				fprintf(stderr,
					"Couldn't find pres_type '%s' in"
					" scope '%s'\n",
					this->pres_type,
					idl_scope->get_name());
			}
		} else {
			fprintf(stderr, "Couldn't find idl_type '%s' in"
				" scope\n", find_tag(tl, "idl_type")->
				data.tag_data_u.str);
		}
	}
}

presentation_collection::presentation_collection()
{
	new_list(&this->impls);
}

presentation_collection::~presentation_collection()
{
}

void presentation_collection::set_scml_scope(struct scml_scope *the_scope)
{
	this->scope = the_scope;
}

struct scml_scope *presentation_collection::get_scml_scope()
{
	return( this->scope );
}

void presentation_collection::set_scml_stream_pos(struct scml_stream_pos *ssp)
{
	this->stream_pos = ssp;
}

struct scml_stream_pos *presentation_collection::get_scml_stream_pos()
{
	return( this->stream_pos );
}

void presentation_collection::add_impl(struct presentation_impl *pi)
{
	add_tail(&this->impls, &pi->link);
	pi->set_collection(this);
}

struct presentation_impl *presentation_collection::find_impl(char *idl_type,
							     char *pres_type)
{
	struct presentation_impl *retval = 0, *curr;
	
	curr = (struct presentation_impl *)this->impls.head;
	while( curr->link.succ ) {
		if( (!idl_type || !curr->get_idl_type() ||
		     !strcmp(idl_type, curr->get_idl_type())) &&
		    (!pres_type || !curr->get_pres_type() ||
		     !strcmp(pres_type, curr->get_pres_type())) ) {
			retval = curr;
		}
		curr = (struct presentation_impl *)curr->link.succ;
	}
	return( retval );
}

void presentation_collection::implement(pres_c_1 *pres)
{
	struct presentation_impl *pi;
	tag_list *tl, *sub_tl;
	int lpc, len;
	char *idl_type;
	tag_item *pres_ti, *ti;
	
	tl = pres->pres_attrs;
	if( !(pres_ti = find_tag(tl, "pres_type")) )
		return;
	
	/* Fix the parent links of the tag_list's in tl */
	relink_tag_list(tl);
	/* Typedefs, detected by the typedef_ref tag, need to have their parent
	   links redirected to the pres_type tag_list specified by the numbe
	   in the typedef_ref */
	len = tag_data_length(&pres_ti->data);
	for( lpc = 0; lpc < len; lpc++ ) {
		sub_tl = get_tag_data(&pres_ti->data, lpc).tl;
		if( (ti = find_tag(sub_tl, "typedef_ref")) ) {
			sub_tl->parent =
				get_tag_data(&pres_ti->data,
					     ti->data.tag_data_u.i).tl;
		}
	}
	
	/* Walk through the array of pres_types and process them through
	   the list of pres_impls */
	for( lpc = 0; lpc < len; lpc++ ) {
		sub_tl = get_tag_data(&pres_ti->data, lpc).tl;
		ti = find_tag(sub_tl, "idl_type");
		if( find_tag(sub_tl, "main") && ti &&
		    !find_tag(sub_tl, "_pc_implemented") &&
		    (ti->data.kind == TAG_STRING) ) {
			idl_type = ti->data.tag_data_u.str;
			pi = (struct presentation_impl *)this->impls.head;
			while( pi->link.succ ) {
				if( (!pi->get_idl_type() ||
				     !strcmp(idl_type, pi->get_idl_type())) &&
				    (!pi->get_pres_type() ||
				     (ti = find_tag(sub_tl,
						    pi->get_pres_type()))) ) {
					/* If we have a pres type than pass
					   its tag_list down, otherwise
					   use the type's root tag_list */
					if( pi->get_pres_type() )
						pi->implement(pres,
							      ti->data.
							      tag_data_u.tl);
					else
						pi->implement(pres, sub_tl);
				}
				pi = (struct presentation_impl *)pi->link.succ;
			}
			add_tag(sub_tl, "_pc_implemented", TAG_NONE);
		}
	}
}



syntax highlighted by Code2HTML, v. 0.9.1