#include "Node.h"

#include "idst-config.h"

#include <stdio.h>
#include <assert.h>

extern oop s__5fdebugName;
extern oop s__5fsizeof;
extern oop s_is;
extern oop s_t_;
extern oop s_SmallInteger;
extern oop s_UndefinedObject;

//  oop	mName;
//  oop	mSuper;
//  oop	mSlots;

ClassNode::ClassNode(oop name, oop super, oop slots) : GlobalVariable(name)
{
  mTypeName= 0;
  mSuper= super;
  mSlots= slots;
  mExports= 0;
}

oop ClassNode::withNameSuperSlots(oop name, oop super, oop slots)
{
  return new ClassNode(name, super, slots);
}

oop ClassNode::withNameSlots(oop name, oop slots)
{
  return withNameSuperSlots(name, 0, slots);
}

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

oop ClassNode::typeName(void)
{
  return mTypeName;
}

oop ClassNode::superclass(void)
{
  return mSuper;
}

oop ClassNode::slots(void)
{
  return mSlots;
}

oop ClassNode::predeclared(void)
{
  mName= 0;
  return this;
}

int ClassNode::operator ==(oop anObject)
{
  return anObject->isClassNode()
    &&   (*mName  == anObject->name())		// equality
    &&   ( mSuper == anObject->superclass())	// identity
    &&   (*mSlots == anObject->slots());	// equality
}

oop ClassNode::encodeIn(oop unit)
{
  mMangledName= unit->mangleVariable(mName);
  mTypeName= *s_t_ + mName;
  oop slots= new OrderedCollection();
  if (mSuper)
    {
      oop super= unit->lookupClass(mSuper);				// name -> node
      if (!super->isClassNode())
	error("%s: superclass %s is not a class", mName->cString(), mSuper->cString());
      mSuper= super;
      slots->addAll(super->slots());				// inherit slots
    }
  iterate(mSlots, iter)
    {
      oop slot= iter.element();
      if (slots->includes(slot))
	error("slot name '%s' already used", slot->cString());
      slots->add(new ReceiverVariable(slot, mangleSlot(slot), this));
    }
  mSlots= slots->asArray();
  slots->reset();
#if 0
  iterate(mSlots, slot)
    slots->add((*slot)->exported());
#endif
  mExports= slots->asArray();
  oop decl= unit->declareClass(this);
  if (decl != this)
    {
      mName= 0;
      return decl;
    }
    
  unit->declareSelector(s__5fsizeof);
  unit->declareSelector(s__5fdebugName);
  //unit->declareSelector(*s_is + mName);

  return this;
}

oop ClassNode::mangleSlot(oop slotName)
{
  char buf[1024];
  snprintf(buf, 1024, "((struct %s *)self)->%s", mTypeName->cString(), slotName->cString());
  return new String(buf);
}

oop ClassNode::encode(oop encoder)	// sent from MethodNode::encodeIn() to declare receiver variables
{
  assert(mName);
  iterate(mSlots, iter)
    {
      oop slot= iter.element();
      encoder->declare(slot->name(), slot);
    }
  return this;
}

oop ClassNode::encodeForBlock(oop encoder)	// sent from BlockNode::encode() to declare receiver variables
{
  shouldNotImplement(0);
  assert(mName);
  iterate(mExports, slot)
    encoder->declare((*slot)->name(), (*slot));
  return this;
}

oop ClassNode::genDeclarationIn(oop unit)
{ 
  if (mName)
    {
      oop stream= unit->outputStream();
      stream
	->format("struct t_%s\n", mName->cString())
	->format("{\n")
	->format("  vtbl_t _vtbl[0];\n");
      iterate(mSlots, iter)
	stream->format("  oop %s;\n", iter.element()->name()->cString());
      stream
	->format("};\n\n");
    }
  return this;
}

oop ClassNode::genImplementationIn(oop unit)
{
  if (mName)
    unit->outputStream()
      ->format("static int   %s__5fsizeof(oop self) { return sizeof(struct t_%s); }\n",   mName->cString(), mName->cString())
      ->format("static char *%s__5fdebugName(oop self) { return \"%s\"; }\n\n", mName->cString(), mName->cString())
      //->format("static oop   %s_is%s(oop self) { return v_true; }\n\n", mName->cString(), mName->cString())
      ;
  return this;
}

oop ClassNode::genInitialisationIn(oop unit)
{
  if (mName)
    {
      oop stream= unit->outputStream();
      char *cName= mName->cString();
      if (mSuper)
	stream->format("  v_%s=_proto(v_%s);\n", cName, mSuper->name()->cString());
      else
	stream->format("  v_%s=_proto(0);\n", cName);
#if 0
#    if (TAGGED_INTEGERS)
      if (*mName == s_SmallInteger)
	stream->format("  v_%s=_tagged(v_%s);\n", cName, cName);
#    endif
      if (*mName == s_UndefinedObject)
	stream->format("  v_%s=_undefined(v_%s);\n", cName, cName);
#endif
      stream
	->format("  _method(v_%s, s__5fsizeof, (imp_t)%s__5fsizeof);\n", cName, cName)
	->format("  _method(v_%s, s__5fdebugName, (imp_t)%s__5fdebugName);\n", cName, cName)
	//->format("  _method(v_%s, s_is%s, (imp_t)%s_is%s);\n", cName, cName, cName, cName)
	;
    }
  return this;
}

oop ClassNode::printOn(oop stream)
{
  stream->format("%s", mName->cString());
  if (mSuper)
    stream->format(":%s", (mSuper ? ((mSuper->isString() ? mSuper : mSuper->name())->cString()) : "-"));
  stream->format("(");
  int count= 0;
  iterate(mSlots, current)
    {
      oop slot= current.element();
      if (!slot->isString()) slot= slot->name();
      stream->format("%s%s", count++ ? " " : "", slot->cString());
    }
  stream->format(")");
  return this;
}

oop ClassNode::printOnIndent(oop stream, int indent)
{
  stream
    ->space((indent + 1) * 2)->print(this)->nl()
    ->space((indent + 1) * 2)->print(mSlots)->nl();
  return this;
}
