/* $Id */
/* $Log */
/*********************************************************************

This software module was originally developed by

Eric D. Scheirer (MIT Media Laboratory)

in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
as specified by the MPEG-2 NBC/MPEG-4 Audio standard.  ISO/IEC gives
users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
software module or modifications thereof for use in hardware or
software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
standards. Those intending to use this software module in hardware or
software products are advised that this use may infringe existing
patents. The original developer of this software module and his/her
company, the subsequent editors and their companies, and ISO/IEC have
no liability for use of this software module or modifications thereof
in an implementation.

This software module is hereby released into the public domain.

***********************************************************************/

#include <stdio.h>
#ifdef _WIN32
#include <string.h>
#else
#include <strings.h>
#endif
#include <stdlib.h>
#include "saol.h"

void walk_block_replace_tvar(block *, char *, terminal *);
void walk_expr_replace_tvar(expr *, char *, terminal *);
void walk_exprlist_replace_tvar(exprlist *, char *, terminal *);
void walk_statement_replace_tvar(statement *, char *, terminal *);

void deal_with_template(sa_decoder *sa, template_decl *t) {
  /* we need to make an instrument for each of the template names.

     this involves
       instantiating the instrument,
       moving the names over,
       making deep copies of
          the parameter list,
	  the local variable list,
	  and the code,
       checking each template variable map for correct type,
       replacing the template variables in the code with their maps,
          (those not actually used by this instrument) */

  namelist *name,*tvar,*vlp,*newparams;
  terminal_list *value;
  mapblock *map;
  instr_decl *i;
  char *newname;
  vardecl_list *newlocalvars,*vdl;
  block *newcode;
    

  if (!sa->all->il) sa->all->il = new_instr_list();

  for (name = t->names, map=t->mapping ;
       name;
       name=name->next, map=map->next) {
    if (!map) {
      parse_error("Not enough mapwith blocks for template.");
      return;
    }

    newname = strdup(name->n->name);
    newparams = copy_namelist(t->params);
    newlocalvars = copy_vardecl_list(t->localvars);
    newcode = copy_block(t->code);

    for (tvar = t->tvars, value = map->tl;
	 tvar;
	 tvar = tvar->next, value = value->next) {
      if (!value) {
	parse_error("Not enough mapwidth values in block.");
	return;
      }
      walk_block_replace_tvar(newcode,tvar->n->name,value->t);
    }

    i = new_instr_decl(newname,newparams,newlocalvars,newcode);
    if (sa->verbose) {
      printf("Made instrument '%s' from template:\n",name->n->name);
      print_block(newcode);
    }
  }
}

void walk_block_replace_tvar(block *b,char *orig,terminal *repl) {
  if (!b) return;
  walk_statement_replace_tvar(b->st,orig,repl);
  walk_block_replace_tvar(b->next,orig,repl);
}

void walk_statement_replace_tvar(statement *st, char *orig, terminal *repl) {
  if (!st) return;
  walk_block_replace_tvar(st->b,orig,repl);
  walk_block_replace_tvar(st->elseb,orig,repl);
  walk_expr_replace_tvar(st->expr,orig,repl);
  walk_expr_replace_tvar(st->lvalue,orig,repl);
  walk_exprlist_replace_tvar(st->params,orig,repl);
}

void walk_expr_replace_tvar(expr *e, char *orig, terminal *repl) {
  if (!e) return;
  if (e->d && e->d->iname && !strcmp(e->d->iname,orig)) {
    free(e->d);
    e->op = repl->type;  /* might have changed from an expr to a const */
    e->d = copy_terminal(repl);
  }
  walk_expr_replace_tvar(e->left,orig,repl);
  walk_expr_replace_tvar(e->right,orig,repl);
  walk_expr_replace_tvar(e->extra,orig,repl);
  walk_exprlist_replace_tvar(e->params,orig,repl);
}


void walk_exprlist_replace_tvar(exprlist *el, char *orig, terminal *repl) {
  if (!el) return;
  walk_expr_replace_tvar(el->p,orig,repl);
  walk_exprlist_replace_tvar(el->next,orig,repl);
}
