#ifdef __GNUC__
#pragma interface
#endif

#define LOOP_MANY_ARGS

#include <loopcons.h>
#include <iostream.h>
#include "traverse.h"
#include "builtin-syms.h"
#include "mapping.h"

LoopConsExpr::LoopConsExpr(LoopConsProc *p, LoopConsClosure* cl)
	:proc(p)
{
  clear_std_fields(LoopCons_code);
  closure=cl;
  needs_index = 0;
  seq_arg_count = 0;
}

void LoopConsExpr::eval(void* dst, Type* dstType, struct DisplayEnv *env)
{
    MemoSeq *loop = (MemoSeq*)closure->Expression::eval(env);
#if 0 /* old? */
    Functional* f = GC_NEW GFunction(proc->function(), loop);
    loop->MemoSeq::MemoSeq(f, seq_arg_count);
#else
    loop->MemoSeq::MemoSeq(proc->function(), seq_arg_count);
#endif
    dstType->coerceFromRoot(dst, loop);
}

void LoopConsExpr::printon(ostream& outs) const
{
    outs << "[LOOP:" << *closure << "]";
}

Expr * LoopConsExpr::traverse(struct TraverseData *data)
{
  LoopConsExpr *saveCurLoopCons = data->curLoopCons;
  data->curLoopCons = this;
  closure->bl_traverse(data);
  // Optimize {A?} to A.
  Block* body = proc->expr;
  Statement *st = body->first;
#if 0
  // Bogus optimization.
  // Idea: optimize {EXPR?} to EXPR
  // but this must be done before various re-writes.
  if (st != NULL && st->next == NULL
      && (st->kind == ExprStatement || st->kind == DeclStatement)
      && st->src.code() == IndexExpr_code) {
    IndexExpr *ind = (IndexExpr*)st->src.E;
    if (ind->arg[0].E == NullExpr)
      return new ExprQuote(&IndexSequence);
    return ind->arg[0].E;
  }
#endif
#if 0
  if (proc.code() == ProcExpr_code) {
    ...;
  }
  if (func.code() == IndexExpr_code)
    {
      IndexExpr *ind = (IndexExpr*)func.E;
      if (ind->arg[0].E != NullExpr)
	return ind->arg[0].E;
    }
#endif
  data->curLoopCons = saveCurLoopCons;
  return this;
}

Expr * LoopConsProc::traverse(register struct TraverseData *data)
{
  ProcExpr::traverse1(data);
  InitClause(clause, this);
  ProcExpr::traverse2(data);
  return this;
}

Expr * IndexExpr::traverse(struct TraverseData *data)
{
  if (arg[0].E == NullExpr)
    {
      if (data->pass == BindPass && !(data->flags & TraverseInQuote))
	if (data->curLoopCons)
	  data->curLoopCons->needs_index = 1;
    }
  else
    {
#if 1
      if (data->pass == BindPass && !(data->flags & TraverseInQuote))
	{
	  struct Declaration *dcl;
	  static int loop_tmp_number = 0;
	  char buf[30];
	  Expr *exported_expr = new CoerceSeqExpr(arg[0]);
	  sprintf(buf, "__LTMP%d", ++loop_tmp_number);
	  Symbol *name = EnterSymbol(buf);
	  struct Block *closure;
	  if (data->curLoopCons == NULL)
	    {
	      TrError(data,
		      "? operator used; not inside { ... } or quoted.");
	      return this;
	    }
	  data->curLoopCons->seq_arg_count++;
#ifdef LOOP_MANY_ARGS
	  ProcExpr *proc = data->curLoopCons->proc;
	  struct ParamExpr *param = GC_NEW ParamExpr;

	  /* Link new param into proc->argList */
	  ParamExpr **param_tail = &proc->argList;
	  while (*param_tail) param_tail = &(*param_tail)->next;
	  param->next = NULL;
	  *param_tail = param;

	  param->flags = 0;
	  param->name = NULL;
	  param->default_expr = NULL;
	  proc->pn[1].required++;
	  param->arg_type = NULL;
	  Declaration *param_dcl = Symbol2Declaration(name);
	  param_dcl->blockLevel = PARAM_LEVEL(proc);
	  param->arg_expr.E = Decl2Ident(param_dcl);
	  param_dcl->set_pointer_field(param_dcl->u.offset, &RefRoot);
	  proc->nParams++;

	  /* Chain new param_dcl into proc->paramDecls list. */
	  Declaration **decl_ptr = &proc->paramDecls;
	  while (*decl_ptr) decl_ptr = &(*decl_ptr)->next();
	  *decl_ptr = param_dcl;
#endif
	  closure = GetClosure(data);
	  struct Identifier *id = NewIdentifier(name, NULL);
	  dcl = Symbol2Declaration(name);

	  dcl->set_pointer_field(dcl->u.offset, &RefRoot); // Should be: RefGenSeq
	  dcl->flags |=
	    NonLocalDeclaration|SetDeclaration|PrivateDeclaration;
	  AppendDeclaration(closure, dcl, exported_expr);
	  dcl->blockLevel = closure->level;
	  PushDecl(data, dcl);
#ifdef LOOP_MANY_ARGS
	  id = NewIdentifier(name, NULL);
	  id->v.decl = param_dcl;
	  return id;
#else
	  id->v.decl = dcl;
	  arg[0].E = id;
#endif
	}
      arg[0] = arg[0].traverse(data);
#else
      if (data->pass == BindPass)
	{
	  struct Declaration *decl = Symbol2Declaration(NULL);
	  AppendDeclaration(closure, decl, p->bin.arg[0].E);
	  arg[0].E = Decl2Ident(decl);
	}
      else
	arg[0] = arg[0].traverse(data);
#endif
    }
  arg[1] = arg[1].traverse(data);
  return this;
}

void IndexExpr::eval(void* dst, Type* dstType, DisplayEnv *env)
{
    Root *index;
    arg[1].eval(&index, &RefRoot, env);
    if (arg[0].E == NullExpr)
	dstType->coerceFromRoot(dst, index);
    else {
	// Guaranteed to be already coerced to a sequence.
	GenSeq *val = (GenSeq*)arg[0].eval(env);
	dstType->coerceFromRoot(dst, val->prefix(index));
    }
}

void IndexExpr::printon(ostream& outs) const
{
    if (arg[0].E == NullExpr)
	outs << " ?";
    else
	outs << arg[0] << "?";
}
