/*
 * Copyright (c) 1995, 1996, 1997, 1998, 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 <stdio.h>
#include <assert.h>
#include <rpc/types.h>

#include <mom/compiler.h>
#include <mom/idl_id.h>
#include <mom/libmint.h>
#include <mom/c/libcast.h>
#include <mom/c/libpres_c.h>
#include <mom/pres_c.h>

/* #include "cpu.h" */
#include "type.h"
#include "routine.h"
#include "statement.h"
#include "mom_routines.h"
#include "global.h"
#include "boolean.h"
#include "xlate.h"
#include "xlate_util.h"

/* Control flags for the n_args routine. */

#define NONE 0x0 /* No special control */
#define COUNT_ARR 0x1 /* Count arrays as two arguments. */
#define NOIN_OUT 0x2 /* Don't count inout arguments */

/* Quickie lookup macro into the MINT part of the PRES_C file*/
#define m(n) (out_pres_c.mint.defs.defs_val[n])

/* Macro to define cast scope */
#define c_scope out_pres_c.cast

/* Macro to define cast scope */
#define c_stubscope out_pres_c.stubs_cast

/* Macro to define stub's location in the PRES_C */
#define c_stubs out_pres_c.stubs

/* Lookup to get at func def inside CAST */
#define c(n) (c_scope.cast_scope_val[n].u.cast_def_u_u.func_type)

/* Lookup to get at include def inside CAST */
#define i(n) (c_scope.cast_scope_val[n].u.cast_def_u_u.include)

/* Lookup into the stubs array */
#define s(n) (c_stubs.stubs_val[n])

/* Variable to store presentation stuff. Declared in main.c */
extern pres_c_1 out_pres_c;

/* Generate a client or server? */

extern int gen_client;
extern int gen_server;

extern char *ihead_name;

/* 
 * Walks through statement list, translating MIG routines.
 */		    
static void translate_routines()
{
    statement_t *CurState; 
    
    int c_include;

    int routine_count = 0;
    int simple_count = 0;

    int stub_count = 0;

    /* 
     * Three MINT references which form the top level of the MINT interface. 
     * A MINT interface is a union of interface types.(MIG, DCE, Sun, 
     * etc.) Each interface value in the top level union contains the
     * stubs for the interface.
     */

    mint_ref procs_union, intf_union, top_union; 
    
    pres_c_inline error_i;
    
    error_i = pres_c_new_inline_atom(-1, pres_c_new_mapping(
	    PRES_C_MAPPING_DIRECT));

    /* 
     * Inital count of the routines
     */
    for (CurState = StatementList; CurState != NULL;
				   CurState = CurState->stNext)
	{
	    if (CurState->stKind == skRoutine)
		{
		    if (CurState->stRoutine->rtKind == rkRoutine)
			{
			    routine_count++;
			}
		    else if (CurState->stRoutine->rtKind == rkSimpleRoutine)
			{
			    simple_count++;
			}
		}			    
	}

    /* 
     * Make the procedure union.
     * Its cases will be created on demand.
     */
    procs_union = get_union_def(0);

    /* 
     * Make the interface union. Only has one slot, corresponding to the
     * subsystem we are implementing.
     */
    intf_union = get_union_def(1);
    
    m(intf_union).mint_def_u.union_def.cases.cases_val[0].val = 
	mint_new_const_int(SubsystemBase);
    m(intf_union).mint_def_u.union_def.cases.cases_val[0].var = 
	procs_union;

    /* 
     * Make the top union. Only has one slot for MIG routines.
     */
    top_union = get_union_def(1);
    
    m(top_union).mint_def_u.union_def.cases.cases_val[0].val = 
	mint_new_const_int(IDL_MIG); /* MIG's Magic Number */
    m(top_union).mint_def_u.union_def.cases.cases_val[0].var = 
	intf_union;

    /* 
     * Make the array of C stubs, 1 client stub per routine, 
     * and one server skeleton.
     */
    assert(c_stubs.stubs_len == 0);
    if (gen_client)
	c_stubs.stubs_len = routine_count + simple_count;
    if (gen_server)
	c_stubs.stubs_len++;
    c_stubs.stubs_val = 
	    mustcalloc((c_stubs.stubs_len)*sizeof(pres_c_stub));

    assert(stub_count == 0);

    if (gen_server)
	{
	    int new_def;
	    char *new_name;
	    
	    /* 
	     * Make the server stub 
	     */
	    assert(stub_count < ((int) c_stubs.stubs_len));
	    
	    /*
	     * XXX --- The parser doesn't tell us which were included and which
	     * were not.
	     */
	    /* default case --
	    s(stub_count).included = PASSTHRU_DATA_CHANNEL; */
	    s(stub_count).kind = PRES_C_SERVER_SKEL;
	    
	    if (!ServerDemux)
	    {
		new_name = flick_asprintf("%s_server",
					  (SubsystemName == strNULL ?
					   "" : SubsystemName));
	    }
	    else
		new_name = strmake(ServerDemux);
	    
	    /* Add to the CAST scope. */
	    new_def = cast_add_def(&(c_scope),
				   cast_new_scoped_name(new_name, NULL),
				   CAST_SC_EXTERN,
				   CAST_VAR_DECL,
				   PASSTHRU_DATA_CHANNEL,
				   CAST_PROT_NONE);
	    
	    c_scope.cast_scope_val[new_def].u.cast_def_u_u.var_type = 
		cast_new_type_name("flick_server_t");
	    
	    /*
	     * Add to the stubs-only CAST scope.  This is the one that really
	     * counts, and what `sskel.c_def' will refer to in the back ends.
	     */
	    new_def = cast_add_def(&(c_stubscope),
				   cast_new_scoped_name(new_name, NULL),
				   CAST_SC_EXTERN,
				   CAST_VAR_DECL,
				   PASSTHRU_DATA_CHANNEL,
				   CAST_PROT_NONE);
	    
	    c_stubscope.cast_scope_val[new_def].u.cast_def_u_u.var_type = 
		cast_new_type_name("flick_server_t");
	    
	    s(stub_count).pres_c_stub_u.sskel.c_def = new_def;
	    
	    s(stub_count).pres_c_stub_u.sskel.request_itype = top_union;
	    s(stub_count).pres_c_stub_u.sskel.reply_itype = top_union;
	    
	    s(stub_count).pres_c_stub_u.sskel.funcs.funcs_len = 
		routine_count + simple_count;
	    
	    s(stub_count).pres_c_stub_u.sskel.funcs.funcs_val = 
		mustcalloc(s(stub_count).pres_c_stub_u.sskel.funcs.funcs_len
			   * sizeof(pres_c_func));
	    
	    stub_count++;
	}
    
    /* 
     * Generate MINT, CAST and client stubs(presentation structures) for each
     * routine.
     */

    /* Add in iheader include, if needed */
    if (gen_client && ihead_name != NULL)
	emit_include_stmt(ihead_name, 0);
    
    for (CurState = StatementList; CurState != NULL;
				   CurState = CurState->stNext)
	{
	    switch (CurState->stKind)
	    {
	    	case skImport:
		case skUImport:
		case skSImport:

		    if (CurState->stKind == skUImport && !gen_client)
		    	break;
		    if (CurState->stKind == skSImport && !gen_server)
		    	break;

		    /* XXX --- Should use `emit_include_stmt' here. */
		    
		    c_include = cast_add_def(&(c_scope),
					     null_scope_name,
					     CAST_SC_NONE,
					     CAST_INCLUDE,
					     PASSTHRU_DATA_CHANNEL,
					     CAST_PROT_NONE);
		    
		    if (CurState->stFileName[0] == '<' &&
			CurState->stFileName[strlen(CurState->stFileName)-1] ==
			'>')
			{
			    i(c_include).system_only = 1;
			}
		    else if (CurState->stFileName[0] == '"' &&
			     CurState->stFileName[strlen(
				     CurState->stFileName)-1] == '"')
			{
			    i(c_include).system_only = 0;
			}
		    else
			panic("Malformed import statement seen.");
		    
		    i(c_include).filename = mustmalloc(
			    strlen(CurState->stFileName));

		    strcpy(i(c_include).filename, CurState->stFileName + 1);
		    
		    i(c_include).filename[strlen(i(c_include).filename)-1] = 0;

		    break;

		case skRoutine:
		    if (gen_client)
			{
			    if ((CurState->stRoutine->rtKind == rkRoutine)
				|| (CurState->stRoutine->rtKind == 
				     rkSimpleRoutine))
				{
				    pres_c_stub *stub = &(s(stub_count));
				    
				    pres_c_client_stub *cstub =
					&(stub->pres_c_stub_u.cstub);
				    
				    assert(stub_count
					   < ((int) c_stubs.stubs_len));
				    
				    /*
				     * XXX --- The parser doesn't tell us
				     * which were included and which were not.
				     */
				    /* default case --
				    stub->included = PASSTHRU_DATA_CHANNEL; */
				    
				    stub->kind = PRES_C_CLIENT_STUB;
				    
				    cstub->op_flags = PRES_C_STUB_OP_FLAG_NONE;
				    
				    cstub->request_itype = top_union;
				    cstub->reply_itype = top_union;
				    
				    make_routine(CurState->stRoutine,
						 PRES_C_CLIENT_STUB,
						 procs_union,
						 &cstub->c_func,
						 &cstub->target_itype,
						 &cstub->target_i,
						 &cstub->client_itype,
						 &cstub->client_i,
						 &cstub->request_i,
						 &cstub->reply_i);
				    
				    cstub->error_itype = mint_ref_null;
				    cstub->error_i = error_i;
				    
				    if (CurState->stRoutine->rtKind == 
					rkSimpleRoutine) {
					    cstub->op_flags |=
						    PRES_C_STUB_OP_FLAG_ONEWAY;
				    }
				}
		            else
			        panic("Unsupported routine kind %d.", 
				      CurState->stRoutine->rtKind);
			}

		    if (gen_server)
		    {
			pres_c_server_func *sfunc;

			assert(s(0).kind == PRES_C_SERVER_SKEL);
			assert(stub_count - 1 >= 0);
			assert(stub_count - 1
			       < ((int) (s(0).pres_c_stub_u.sskel.
					 funcs.funcs_len)));

			/* Set the type of function */
			s(0).pres_c_stub_u.sskel.funcs.
				funcs_val[stub_count - 1].kind
				= PRES_C_SERVER_FUNC;

			sfunc = &s(0).pres_c_stub_u.sskel.funcs.
				funcs_val[stub_count - 1].pres_c_func_u.sfunc;

		        sfunc->op_flags = PRES_C_STUB_OP_FLAG_NONE;
			if (CurState->stRoutine->rtKind == 
			    rkSimpleRoutine) {
				sfunc->op_flags |=
					PRES_C_STUB_OP_FLAG_ONEWAY;
			}
			
			make_routine(CurState->stRoutine,
				     PRES_C_SERVER_SKEL,
				     procs_union,
				     &sfunc->c_func,
				     &sfunc->target_itype,
				     &sfunc->target_i,
				     &sfunc->client_itype,
				     &sfunc->client_i,
				     &sfunc->request_i,
				     &sfunc->reply_i);
			
			sfunc->error_itype = mint_ref_null;
			sfunc->error_i = error_i;
		    }

		    stub_count++;		    		    
		    break;
	   
	        case skRCSDecl:
		{
		    panic("RCS Declaration not supported.");
		}

	        default:
	    	{
		    panic("Unknown statement kind %d\n", CurState->stKind);
		}
	    }
	}

    if (gen_client)
	assert(stub_count == ((int) c_stubs.stubs_len));
    if (gen_server)
	assert(stub_count
	       == ((int) s(0).pres_c_stub_u.sskel.funcs.funcs_len) + 1);
}
	    
/* 
 * This adds in some useful standard types, and then calls translate_routines
 * which does most the work.
 */
void translate()
{
	mint_add_standard_defs(&out_pres_c.mint);
	translate_routines();
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1