#include "Node.h"

extern oop s_self;

//  oop	mEncoder;	// ExecNode
//  oop	mSequence;
//  oop	mClass;		// MethodNode
//  oop	mSelector;
//  oop	mArguments;
//  int	mVariadic;

MethodNode::MethodNode(oop selector, oop arguments) : ExecNode(0)
{
  mClass= 0;
  mSelector= selector;
  mArguments= arguments;
  mVariadic= 0;
}

oop MethodNode::withSelectorArguments(oop selector, oop arguments)
{
  return new MethodNode(selector, arguments);
}

oop MethodNode::withSelectorArgument(oop selector, oop argument)
{
  return withSelectorArguments(selector, ListNode::with(argument));
}

oop MethodNode::withSelector(oop selector)
{
  return withSelectorArguments(selector, ListNode::empty());
}

oop MethodNode::withClassSequence(oop clazz, oop sequence)
{
  mClass= clazz;
  mSequence= sequence;
  return this;
}

oop MethodNode::addKeywordArgument(oop keyword, oop argument)
{
  mSelector= *mSelector + keyword;
  mArguments->add(argument);
  return this;
}

oop MethodNode::beVariadic(void)
{
  mVariadic= 1;
  return this;
}

int MethodNode::variadic(void)
{
  return mVariadic;
}

oop MethodNode::encodeIn(oop unit)
{
  mEncoder= new Encoder(unit, 0);
  mClass= mEncoder->lookup(mClass);
  mEncoder->setClass(mClass);
  mClass->encode(mEncoder);					// declare receiver variables
  unit->declareSelector(mSelector);
  mEncoder->declare(s_self, new SelfNode());			// declare self
  oop arguments= new OrderedCollection();
  iterate(mArguments, iter)
    arguments->add(mEncoder->declareLocal(iter.element()));	// declare arguments
  mArguments= arguments->asArray();
  mSequence
    ->ensureReturn()
    ->encode(mEncoder);
  return this;
}

oop MethodNode::genDeclarationIn(oop unit)
{
  mEncoder->genDefinitions();
  return this;
}

oop MethodNode::genImplementationIn(oop unit)
{
  mEncoder->genImplementations();
  oop stream= unit->outputStream();
  stream->format("static oop %s__%s(oop self", mClass->name()->cString(), unit->lookupSelector(mSelector)->cString());
  iterate(mArguments, arg)
    stream->format(", oop %s", (*arg)->mangledName()->cString());
  if (mVariadic)
    stream->format(", ...");
  stream->format(")\n"
		 "{\n");
  if (mEncoder->exportNLR())
    stream->format("  jmp_buf _env;\n");
  if (mEncoder->exports())
    {
      stream->format("  oop *_state= (oop *)_newPointers(sizeof(oop) * %d);\n", mEncoder->exports());
      iterate(mArguments, arg)
	if ((*arg)->isExported())
	  stream->format("  _state[%d]= %s;\n", (*arg)->offset(), (*arg)->mangledName()->cString());
    }
  if (mEncoder->exportNLR())
    stream->format("  if (setjmp(_env)) { return _nlAnswer; };\n");
  mSequence->genBody(unit, mEncoder);
  stream->format("}\n\n");
  return this;
}

oop MethodNode::genInitialisationIn(oop unit)
{
  mEncoder->genInitialisations();
  unit->outputStream()
    ->format("  _method(%s, %s, (imp_t)%s__%s);\n",
	     mClass->mangledName()->cString(),
	     unit->lookupSelector(mSelector)->cString(),
	     mClass->name()->cString(), unit->lookupSelector(mSelector)->cString());
  return this;
}

oop MethodNode::printOn(oop stream)
{
  return stream
    ->format("MethodNode(")
    ->print(mClass)->format(">>")->print(mSelector)
    ->space()->print(mArguments)
    ->format(")");
}

oop MethodNode::printOnIndent(oop stream, int indent)
{
  Node::printOnIndent(stream, indent);
  return mSequence->printOnIndent(stream, indent + 1);
}
