#include "Node.h"

extern oop s_nil;
extern oop s_self;

//  oop	mTemporaries;
//  oop	mStatements;

SequenceNode::SequenceNode(oop temporaries, oop statements)
{
  mTemporaries= temporaries;
  mStatements= statements;
}

oop SequenceNode::withTemporariesStatements(oop temporaries, oop statements)
{
  return new SequenceNode(temporaries, statements);
}

oop SequenceNode::withStatements(oop statements)
{
  return withTemporariesStatements(ListNode::empty(), statements);
}

oop SequenceNode::withStatement(oop statement)
{
  return withTemporariesStatements(ListNode::empty(), ListNode::with(statement));
}

oop SequenceNode::withTemporaries(oop temporaries)
{
  return withTemporariesStatements(temporaries, ListNode::empty());
}

oop SequenceNode::empty(void)
{
  return withTemporariesStatements(ListNode::empty(), ListNode::empty());
}

int SequenceNode::hasReturn(void)
{
  return mStatements->size() && mStatements->last()->isReturn();
}

oop SequenceNode::ensureReturn(void)
{
  if ((mStatements->size() == 0) || !mStatements->last()->isReturn())
    mStatements->add(BlockReturnNode::withValue(VariableNode::withName(s_self)));
  return this;
}

oop SequenceNode::ensureBlockReturn(void)
{
  if (mStatements->size() == 0)
    mStatements->add(BlockReturnNode::withValue(VariableNode::withName(s_nil)));
  else if (!mStatements->last()->isReturn())
    {
      oop last= mStatements->removeLast();
      mStatements->addLast(BlockReturnNode::withValue(last));
    }
  return this;
}

oop SequenceNode::encode(oop encoder)
{
  oop temporaries= new OrderedCollection();
  iterate(mTemporaries, iter)
    temporaries->add(encoder->declareLocal(iter.element()));	// declare temporaries
  mTemporaries= temporaries;
  iterate(mStatements, iter)
    {
      oop stmt= iter.element();
      stmt->encode(encoder);
      mLocation= stmt->location();
      encoder->popStack();
    }
  mLocation= encoder->pushStack();
  return this;
}

oop SequenceNode::genBody(oop unit, oop encoder)
{
  int stackDepth= encoder ? encoder->maxStack() : 0;
  oop stream= unit->outputStream();
  stream->format(" {\n");
  iterate(mTemporaries, temp)
    if (!(*temp)->isExported())
      stream->format("  oop %s;\n", (*temp)->mangledName()->cString());
  if (stackDepth)
    {
      stream->format("  oop _1");
      for (int i= 2;  i <= stackDepth;  ++i)
	stream->format(", _%d", i);
      stream->format(";\n");
    }
  iterate(mStatements, iter)
    iter.element()->gen(unit);
  stream->format(" }\n");
  return this;
}

oop SequenceNode::printOnIndent(oop stream, int indent)
{
  stream
    ->space(indent * 2)
    ->format("| ")
    ->print(mTemporaries)
    ->format(" |")
    ->nl();
  mStatements->printOnIndent(stream, indent + 1);
  return this;
}
