#include "Node.h"

#include <assert.h>

extern oop s__self_2d_3e;
extern oop s_self;
extern oop s_super;

//  oop mName;
//  oop mMangled;

Variable::Variable(oop name, oop mangled)
{
  mName= name;
  mMangledName= mangled;
}

oop Variable::name(void)
{
  return mName;
}

oop Variable::mangledName(void)
{
  return mMangledName;
}

int Variable::operator ==(oop anObject)
{
  return anObject->isVariable() && (*mName == anObject->name());
}

int Variable::isOpenBlock(void)
{
  return 0;
}

oop Variable::gen(oop unit)
{
  unit->outputStream()->format("%s", mMangledName->cString());
  return this;
}

oop Variable::printOn(oop stream)
{
  return stream->format("%s[%s]", mName->cString(), mMangledName ? mMangledName->cString() : "-");
}

GlobalVariable::GlobalVariable(oop name, oop mangled) : Variable(name, mangled)
{
}

int GlobalVariable::level(void)
{
  return 0;
}

//  int mLevel;
//  int mFree;

LocalVariable::LocalVariable(oop name, oop mangled, int level) : Variable(name, mangled)
{
  mLevel= level;
  mExported= 0;
  mOffset= 0;
}

int LocalVariable::level(void)
{
  return mLevel;
}

int LocalVariable::isExported(void)
{
  return mExported;
}

int LocalVariable::offset(void)
{
  return mOffset;
}

oop LocalVariable::exportFrom(oop encoder)
{
  assert(mLevel == encoder->level());
  if (!mExported)
    {
      mExported= 1;
      mOffset= encoder->nextExportOffset();
    }
  return this;
}

oop LocalVariable::exportedTo(oop encoder)
{
  return new RemoteVariable(encoder->level() - mLevel, mOffset);
}

oop LocalVariable::gen(oop unit)
{
  if (!mExported)
    Variable::gen(unit);
  else
    unit->outputStream()->format("_state[%d]", mOffset);
  return this;
}

ReceiverVariable::ReceiverVariable(oop name, oop mangled, oop clazz) : Variable(name, mangled)
{
  mClass= clazz;
}

oop ReceiverVariable::getClass(void)
{
  return mClass;
}

int ReceiverVariable::level(void)
{
  return 0;
}

int ReceiverVariable::isExported(void)
{
  return 0;
}

oop ReceiverVariable::exported(void)
{
  shouldNotImplement(0);
}

oop ReceiverVariable::exportedTo(oop encoder)
{
  encoder->noteExportSelf();
  return new RemoteReceiverVariable(this);
}

oop ReceiverVariable::exportFrom(oop encoder)
{
  encoder->noteExportSelf();
  return this;
}

oop ReceiverVariable::gen(oop unit)
{
  return Variable::gen(unit);
}

// oop mReceiverVariable

RemoteReceiverVariable::RemoteReceiverVariable(oop receiverVariable)
{
  mReceiverVariable= receiverVariable;
}

oop RemoteReceiverVariable::name(void)
{
  return mReceiverVariable->name();
}

int RemoteReceiverVariable::level(void)
{
  return 0;
}

int RemoteReceiverVariable::isExported(void)
{
  return 1;
}

oop RemoteReceiverVariable::gen(oop unit)
{
  unit->outputStream()
    ->format("((struct %s *)((struct t_BlockClosure *)_self)->receiver)->%s",
	     mReceiverVariable->getClass()->typeName()->cString(),
	     mReceiverVariable->name()->cString());
  return this;
}

SelfNode::SelfNode(void) : LocalVariable(s_self, s_self, 1) {}

oop SelfNode::exportFrom(oop encoder)
{
  mExported= 1;
  return this;
}

oop SelfNode::exportedTo(oop encoder)
{
  encoder->noteExportSelf();
  return new LocalVariable(s_self, s__self_2d_3e, encoder->level());
}

oop SelfNode::gen(oop unit)
{
  return Variable::gen(unit);
}

SuperNode::SuperNode(void) : LocalVariable(s_super, s_self, 0) {}

oop SuperNode::encode(oop encoder)
{
  return error("super can only appear as a message receiver");
}

// int mOffset;

StateVariable::StateVariable(int offset)
{
  mOffset= offset;
}

oop StateVariable::gen(oop unit)
{
  shouldNotImplement(0);
}

oop StateVariable::printOn(oop stream)
{
  stream->format("state[%d]", mOffset);
  return this;
}

// int mOffset;		// StateVariable
// int mLevel;		// RemoteVariable

RemoteVariable::RemoteVariable(int level, int offset) : StateVariable(offset)
{
  mLevel= level;
}

int RemoteVariable::level(void)
{
  return mLevel;
}

oop RemoteVariable::gen(oop unit)
{
  oop stream= unit->outputStream();
  stream->format("((oop *)");
  int i;
  for (i= 0;  i < mLevel;  ++i)
    stream->format("((struct t_BlockClosure *)");
  stream->format("_self)");
  for (i= 1;  i < mLevel;  ++i)
    stream->format("->outer)");
  stream->format("->_state)[%d]", mOffset);
  return this;
}

oop RemoteVariable::printOn(oop stream)
{
  stream->format("remote[%d,%d]", mLevel, mOffset);
  return this;
}
