/*
 * 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 <mom/compiler.h>

#include <mom/c/libcast.h>
#include <mom/c/scml.hh>
#include <mom/c/pbe.hh>

/*
 * This file contains most of the code for handling command line arguments.
 *
 * Each argument that requires some action to be taken has a handler function
 * followed by a be_handler structure for holding the handler and any
 * peripheral information.  The handlers are ordered based on priorities
 * so any arguments that may have dependencies on the others will have a
 * lower priority.  Note:  Priorities kind of suck, but its the best thing
 * short of explicitly declaring the dependencies...
 */

struct be_event *be_arg_scml_defs_handler(struct be_handler */*bh*/,
					  struct be_event *be)
{
	struct be_state *state = ((struct be_state_event *)be)->state;
	
	if( be->id != BESE_CLI_ARGS )
		return be;
	scml_parse_cmdline_defines(state->get_scml_root(),
				   &state->get_cli_args()->scml_defs);
	return( be );
}

struct be_handler be_arg_scml_defs("-D defs", 0, be_arg_scml_defs_handler);

struct be_event *be_arg_includes_handler(struct be_handler */*bh*/,
					 struct be_event *be)
{
	struct be_state *state = ((struct be_state_event *)be)->state;
	be_flags *res = state->get_cli_args();
	union tag_data_u data;
	unsigned int lpc;
	tag_item *ti;
	tag_list *tl;
	
	if( be->id != BESE_CLI_ARGS )
		return be;
	ti = find_tag(state->get_scml_root()->get_values(), "system_info");
	tl = find_tag(ti->data.tag_data_u.tl, "flags")->data.tag_data_u.tl;
	ti = find_tag(tl, "source_include_file");
	for( lpc = 0; lpc < res->src_includes.len; lpc++ ) {
		if( res->src_includes.values[lpc].string[0] != '<' ||
		    res->src_includes.values[lpc].string[0] != '\"' ) {
			res->src_includes.values[lpc].string =
				flick_asprintf("\"%s\"",
					       res->src_includes.
					       values[lpc].string);
			data.str = res->src_includes.values[lpc].string;
			set_tag_data(&ti->data, lpc, data);
		}
	}
	ti = find_tag(tl, "header_include_file");
	for( lpc = 0; lpc < res->hdr_includes.len; lpc++ ) {
		if( res->hdr_includes.values[lpc].string[0] != '<' ||
		    res->hdr_includes.values[lpc].string[0] != '\"' ) {
			res->hdr_includes.values[lpc].string =
				flick_asprintf("\"%s\"",
					       res->hdr_includes.
					       values[lpc].string);
			data.str = res->hdr_includes.values[lpc].string;
			set_tag_data(&ti->data, lpc, data);
		}
	}
	return( be );
}

struct be_handler be_arg_includes("includes", 0, be_arg_includes_handler);

static const char *stubs_file_suffix()
{
	const char *suffix;
	
	switch (cast_language) {
	default:
		warn("Unrecognized `cast_language'; "
		     "will use `.c' as the stub file suffix.");
		/* Fallthrough. */
	case CAST_C:
		suffix = ".c";
		break;
	case CAST_CXX:
		suffix = ".cc";
		break;
	}
	
	return suffix;
}

static const char *header_file_suffix()
{
	const char *suffix;
	
	switch (cast_language) {
	default:
		warn("Unrecognized `cast_language'; "
		     "will use `.h' as the header file suffix.");
		/* Fallthrough. */
	case CAST_C:
		suffix = ".h";
		break;
	case CAST_CXX:
		suffix = ".h";
		break;
	}
	
	return suffix;
}

struct be_event *be_arg_input_file_handler(struct be_handler */*bh*/,
					   struct be_event *be)
{
	struct be_state *state = ((struct be_state_event *)be)->state;
	be_flags *res = state->get_cli_args();
	data_channel_mask all_channels_mask;
	io_file_index builtin_file;
	io_file_mask builtin_mask;
	struct be_file *bef;
	
	if( be->id != BESE_CLI_ARGS )
		return be;
	bef = new be_file;
	bef->set_state(state);
	bef->set_name("input");
	bef->set_flags(bef->get_flags() | BEFF_INPUT);
	if( res->input ) {
		bef->set_path(res->input);
		bef->open();
	} else {
		bef->set_path("<stdin>");
		bef->set_file(stdin);
	}
	if( !bef->get_file() )
		panic("Unable to open input file `%s'.", bef->get_path());
	bef->export_to_scml();
	state->add_file(bef);
	
	/* Read our input data. */
	pres_c_1_readfh(state->get_pres(), bef->get_file());
	if( cast_language != state->get_pres()->cast_language )
		panic("Incompatible CAST languages\n");
	builtin_mask = meta_make_file_mask(FMA_SetFlags, IO_FILE_BUILTIN,
					   FMA_TAG_DONE);
	all_channels_mask = meta_make_channel_mask(CMA_TAG_DONE);
	meta_squelch_files(&state->get_pres()->meta_data,
			   &builtin_mask,
			   &all_channels_mask);
	builtin_file = meta_find_file(&state->get_pres()->meta_data, 0,
				      IO_FILE_BUILTIN, 1);
	assert(builtin_file != -1);
	state->set_implied_channel(meta_add_channel(&state->get_pres()->
						    meta_data,
						    builtin_file,
						    "(implied)"));
	return( be );
}

struct be_handler be_arg_input_file("input file",
				    0,
				    be_arg_input_file_handler);

struct be_event *be_arg_stubs_file_handler(struct be_handler */*bh*/,
					   struct be_event *be)
{
	struct be_state *state = ((struct be_state_event *)be)->state;
	be_flags *res = state->get_cli_args();
	struct be_file *bef;
	
	if( be->id != BESE_CLI_ARGS )
		return be;
	/* Determine the names of our output files. */
	if (res->input && !res->output)
		res->output = resuffix(res->input, stubs_file_suffix());
	
	bef = new be_file;
	bef->set_state(state);
	bef->set_name("stubs");
	bef->set_flags(bef->get_flags() | BEFF_OUTPUT);
	if( res->output ) {
		bef->set_path(res->output);
		bef->open();
	} else {
		bef->set_path("<stdout>");
	}
	if( !bef->get_file() )
		panic("Unable to open output file `%s'.", bef->get_path());
	bef->export_to_scml();
	bef->add_handler(new be_handler(be_file_prologue));
	bef->add_handler(new be_handler(be_file_pre_stubs));
	bef->add_handler(new be_handler(be_file_gen_stubs));
	bef->add_handler(new be_handler(be_file_epilogue));
	state->add_file(bef);
	return( be );
}

struct be_handler be_arg_stubs_file("stubs file",
				    0,
				    be_arg_stubs_file_handler);

struct be_event *be_arg_header_file_handler(struct be_handler */*bh*/,
					    struct be_event *be)
{
	struct be_state *state = ((struct be_state_event *)be)->state;
	be_flags *res = state->get_cli_args();
	struct be_file *bef;
	
	if( be->id != BESE_CLI_ARGS )
		return be;
	if (res->output && !res->header)
		res->header = resuffix(res->output, header_file_suffix());
	if (res->input && !res->header)
		res->header = resuffix(res->input, header_file_suffix());
	
	bef = new be_file;
	bef->set_state(state);
	bef->set_name("header");
	bef->set_flags(bef->get_flags() | BEFF_OUTPUT);
	if( res->header ) {
		bef->set_path(res->header);
	} else {
		bef->set_path("/dev/null");
	}
	if( !bef->open() )
		panic("Unable to open header file `%s'.", bef->get_path());
	bef->export_to_scml();
	bef->add_handler(new be_handler(be_file_prologue));
	bef->add_handler(new be_handler(be_file_header_defs));
	bef->add_handler(new be_handler(be_file_epilogue));
	state->add_file(bef);
	return( be );
}

struct be_handler be_arg_header_file("header file",
				     0,
				     be_arg_header_file_handler);

struct be_event *be_arg_inline_file_handler(struct be_handler */*bh*/,
					    struct be_event *be)
{
	struct be_state *state = ((struct be_state_event *)be)->state;
	be_flags *res = state->get_cli_args();
	struct be_file *bef;
	
	if( be->id != BESE_CLI_ARGS )
		return be;
	if( res->inline_file ) {
		bef = new be_file;
		bef->set_state(state);
		bef->set_name("inline");
		bef->set_flags(bef->get_flags() | BEFF_OUTPUT);
		bef->set_path(res->inline_file);
		if( !bef->open() )
			panic("Unable to open inline file `%s'.",
			      bef->get_path());
		bef->export_to_scml();
		bef->add_handler(new be_handler(be_file_prologue));
		bef->add_handler(new be_handler(be_file_epilogue));
		state->add_file(bef);
	}
	return( be );
}

struct be_handler be_arg_inline_file("inline file",
				     0,
				     be_arg_inline_file_handler);

struct be_event *be_arg_pres_impl_handler(struct be_handler */*bh*/,
					  struct be_event *be)
{
	struct be_state *state = ((struct be_state_event *)be)->state;
	be_flags *res = state->get_cli_args();
	struct scml_stream_pos *ssp;
	
	if( be->id != BESE_CLI_ARGS )
		return be;
	if( res->pres_impl ) {
		tag_item *ti;
		
		ssp = scml_execute_defs_file(state->get_scml_root(),
					     res->pres_impl_dirs,
					     res->pres_impl,
					     res->pres_impl);
		if( !ssp ) {
			panic("Couldn't open presentation "
			      "implementation file %s",
			      res->pres_impl);
		}
		state->set_scml_defs_stream(ssp);
		if( (ti = find_tag(state->get_scml_root()->get_values(),
				   "presentation_implementation_name")) ) {
			struct be_pres_impl *bpi;
			char *impl_name;
			
			if( !(impl_name = scml_string::tag_string(ti)) )
				panic("Couldn't get string from "
				      "presentation_implementation_name tag");
			if( !(bpi = state->find_pres_impl(impl_name)) )
				panic("Couldn't match "
				      "presentation_implementation_name '%s'"
				      " to any known implementation",
				      impl_name);
			state->set_pres_collection(bpi->handler(state));
		}
	}
	return( be );
}

struct be_handler be_arg_pres_impl("pres_impl",
				   REQUIRE_HANDLER(be_arg_scml_defs),
				   be_arg_pres_impl_handler);

struct be_event *be_arg_no_included_handler(struct be_handler */*bh*/,
					    struct be_event *be)
{
	struct be_state *state = ((struct be_state_event *)be)->state;
	io_file_mask root_mask;
	
	if( be->id != BESE_CLI_ARGS )
		return be;
	root_mask = meta_make_file_mask(FMA_SetFlags, IO_FILE_ROOT,
					FMA_TAG_DONE);
	
	if (state->get_cli_args()->no_included_declarations) {
		meta_squelch_channels(
			&state->get_pres()->meta_data,
			meta_make_channel_mask(CMA_ExcludesInput, &root_mask,
					       CMA_SetFlags, DATA_CHANNEL_DECL,
					       CMA_TAG_DONE));
	}
	if (state->get_cli_args()->no_included_implementations) {
		meta_squelch_channels(
			&state->get_pres()->meta_data,
			meta_make_channel_mask(CMA_ExcludesInput, &root_mask,
					       CMA_SetFlags, DATA_CHANNEL_IMPL,
					       CMA_TAG_DONE));
	}
	return( be );
}

struct be_handler be_arg_no_included("no_included_*",
				     REQUIRE_HANDLER(be_arg_input_file),
				     be_arg_no_included_handler);

struct be_event *be_arg_all_mu_stubs_handler(struct be_handler */*bh*/,
					     struct be_event *be)
{
	struct be_state *state = ((struct be_state_event *)be)->state;
	pres_c_1 *pres = state->get_pres();
	struct mu_stub_info_node *msi;
	unsigned int i;
	
	if( be->id != BESE_CLI_ARGS )
		return be;
	/*
	 * If we were told to generate all m/u stubs, add them all to the list
	 * of to-be-generated m/u stubs.
	 */
	if (state->get_cli_args()->all_mu_stubs) {
		for (i = 0; i < pres->stubs.stubs_len; i++) {
			switch (pres->stubs.stubs_val[i].kind) {
			case PRES_C_MARSHAL_STUB:
			case PRES_C_UNMARSHAL_STUB: {
				for (int j = 0; j < PRES_C_DIRECTIONS; j++) {
					if (j == PRES_C_DIRECTION_UNKNOWN)
						continue;
					msi = (mu_stub_info_node *) mustcalloc(
						sizeof(mu_stub_info_node));
					msi->stub_dir = (pres_c_direction) j;
					msi->stub_idx = i;
					msi->stub_kind =
						pres->stubs.stubs_val[i].kind;
					add_tail(state->
						 get_mu_stub_list(),
						 &msi->link);
				}
				break;
			}
			default:
				break;
			}
		}
	}
	return( be );
}

struct be_handler be_arg_all_mu_stubs("all_mu_stubs",
				      REQUIRE_HANDLER(be_arg_input_file),
				      be_arg_all_mu_stubs_handler);

/*
 * When we register with the state we just create a separate looper that
 * contains all of the argument handlers so that we don't interfere with
 * priorities of other state handlers.  If a backend wants to do something
 * else then they just grab the `arg looper' handler and add their own stuff.
 */

void register_arg_handlers(struct be_state *state)
{
	struct be_looper *arg_looper;
	
	arg_looper = new be_looper("arg looper", 10);
	arg_looper->add_handler(&be_arg_scml_defs);
	arg_looper->add_handler(&be_arg_pres_impl);
	arg_looper->add_handler(&be_arg_includes);
	arg_looper->add_handler(&be_arg_input_file);
	arg_looper->add_handler(&be_arg_inline_file);
	arg_looper->add_handler(&be_arg_stubs_file);
	arg_looper->add_handler(&be_arg_header_file);
	arg_looper->add_handler(&be_arg_no_included);
	arg_looper->add_handler(&be_arg_all_mu_stubs);
	
	state->add_handler(arg_looper);
}


syntax highlighted by Code2HTML, v. 0.9.1