#include "Node.h"

#include <assert.h>

extern char *yyFile;
extern int   yyLine;

Encoder::Encoder(oop unit, oop parent)
{
  mTranslationUnit= unit;
  mEnvironment= new Environment(unit);
  mLiterals=   	new OrderedCollection();
  mLocation=   	0;
  mMaxStack=   	0;
  mParent=     	parent;
  mBlocks=     	new OrderedCollection();
  mLevel=      	parent ? parent->level() + 1 : 1;
  mImports=    	new Dictionary();
  mExports=    	0;
  mExportSelf= 	0;
  mExportNLR=  	0;
  mExportOuter=	0;
}

oop Encoder::translationUnit(void)	{ return mTranslationUnit; }
int Encoder::location(void)		{ return mLocation; }
int Encoder::maxStack(void)		{ return mMaxStack; }
oop Encoder::parent(void)		{ return mParent; }
int Encoder::level(void)		{ return mLevel; }
int Encoder::exports(void)		{ return mExports; }
int Encoder::exportNLR(void)		{ return mExportNLR; }
int Encoder::exportSelf(void)		{ return mExportSelf; }
int Encoder::exportOuter(void)		{ return mExportOuter; }

oop Encoder::setClass(oop classNode)
{
  mClass= classNode;
  return this;
}

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

oop Encoder::fork(void)
{
  oop innerEncoder= new Encoder(mTranslationUnit, this);
  innerEncoder->setClass(mClass);
  return innerEncoder;
}

oop Encoder::declare(oop name, oop node)
{
  return mEnvironment->declare(name, node);
}

oop Encoder::declareLocal(oop name)
{
  return declare(name, new LocalVariable(name, mTranslationUnit->mangleVariable(name), mLevel));
}

oop Encoder::declareLiteral(oop literalNode)
{
  return mLiterals->add(literalNode);
}

oop Encoder::lookup(oop name)
{
  oop node= mEnvironment->lookupOrNil(name);
  if (node) return node;
  node= mImports->atOrNil(name);
  if (node) return node;
  if (mParent && (node= mParent->lookupFree(name)))
    {
      assert(node->isReceiverVariable() || node->isExported());
      assert(node->isReceiverVariable() || node->level() > 0);		// not file scope
      assert(node->isReceiverVariable() || node->level() != mLevel);	// not local scope
      oop remote= mImports->atPut(name, node->exportedTo(this));
      return remote;
    }
  return error("%s:%d: %s is undeclared", yyFile, yyLine, name->cString());
}

oop Encoder::lookupFree(oop name)
{
  oop node= mEnvironment->lookupOrNil(name);
  if (node)
    {
      assert(!node->isRemoteVariable());
      if (!node->isExported())
	node->exportFrom(this);
      return node;
    }
  if (mParent) return mParent->lookupFree(name);
  return error("%s:%d: %s is undeclared", yyFile, yyLine, name->cString());
}

int Encoder::nextExportOffset(void)
{
  return mExports++;
}

oop Encoder::noteExportNLR(void)
{
  mExportOuter= 1;
  if (mParent)
    mParent->noteExportNLR();
  else
    mExportNLR= 1;
  return this;
}

oop Encoder::noteExportSelf(void)
{
  mExportSelf= 1;
  if (mParent) mParent->noteExportSelf();
  return this;
}

oop Encoder::noteExportOuter(void)
{
  mExportOuter= 1;
  if (mParent) mParent->noteExportOuter();
  return this;
}

int Encoder::pushStack(void)
{
  if (++mLocation > mMaxStack) mMaxStack= mLocation;
  return mLocation;
}

oop Encoder::popStack(void)
{
  --mLocation;
  assert(mLocation >= 0);
  return this;
}

oop Encoder::popStack(int n)
{
  mLocation -= n;
  assert(mLocation >= 0);
  return this;
}

oop Encoder::addBlock(oop blockNode)
{
  mBlocks->add(blockNode);
  return this;
}

oop Encoder::genDefinitions(void)
{
  iterate(mLiterals, current)
    current.element()->genDefinition(mTranslationUnit);
  iterate(mBlocks, current)
    current.element()->genDefinition(mTranslationUnit);
  return this;
}

oop Encoder::genImplementations(void)
{
  iterate(mBlocks, current)
    current.element()->genImplementation(mTranslationUnit);
  return this;
}

oop Encoder::genInitialisations(void)
{
  iterate(mLiterals, current)
    current.element()->genInitialisation(mTranslationUnit);
  iterate(mBlocks, current)
    current.element()->genInitialisation(mTranslationUnit);
  return this;
}
