////////////////////////////////////////////////////////////////////////////////
// Object.cpp -- this file is part of the Emulator Developers Kit
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm

RegisterPersistentClass(Object);


////////////////////////////////////////////////////////////////////////////////
// root for the object tree

static Object gObjectRoot;


////////////////////////////////////////////////////////////////////////////////
// linked list of classes and associated AfterInitHook functions

struct AfterInitHookListEntry {
  AfterInitHookListEntry* pNext;
  void (*pfnHook)(Object*);
};

static AfterInitHookListEntry* gpAfterInitHookListHead;

global void AddAfterInitHook(void (*pfnHook)(Object*)) {
  AfterInitHookListEntry* p;
  verify((p = (AfterInitHookListEntry*)malloc(sizeof AfterInitHookListEntry)) != NULL);
  p->pNext = gpAfterInitHookListHead;
  p->pfnHook = pfnHook;
  gpAfterInitHookListHead = p;
}

global void RemoveAfterInitHook(void (*pfnHook)(Object*)) {
  for (AfterInitHookListEntry** pp = &gpAfterInitHookListHead; *pp != NULL; pp = &(*pp)->pNext) {
    if ((*pp)->pfnHook == pfnHook) {
      AfterInitHookListEntry* p = *pp;
      *pp = (*pp)->pNext;
      free(p);
      return;
    }
  }
  error("RemoveAfterInitHook(): hook function was not in list");
}


////////////////////////////////////////////////////////////////////////////////
// link new object into the object tree

global void Object::Init(const char* pcNewName, Object* pNewParent) {

  // check parameters and conditions
  assert(pcNewName != NULL);
  assert(pcName == 0);
  assert(pParent == NULL);

  // link the object into the tree
  pcName = pcNewName;
  if (pNewParent != NULL) {
    pParent = pNewParent;
  } else {
    pParent = &gObjectRoot;
  }
  pSibling = pParent->pFirstChild;
  pParent->pFirstChild = this;

  // perform the object specific initialisation
  DoInit();

  // call the AfterInitHook functions
  AfterInitHookListEntry* p = gpAfterInitHookListHead;
  while (p != NULL) {
    AfterInitHookListEntry* pNext = p->pNext;
    p->pfnHook(this);
    p = pNext;
  }
}


////////////////////////////////////////////////////////////////////////////////
// destructor

Object::~Object() {

  // check for initialisation
  if (pcName == NULL) {
    trace("uninitialized %s at %08X", typeid(*this).name(), this);
  }

  // auto-delete children
  Object* p = pFirstChild;
  while (p != NULL) {
    Object* pNext = p->pSibling;
    if (p->fAutoDelete) {
      delete p;
    }
    p = pNext;
  }

  // link out of object tree
  if (pParent != NULL) {
    for (Object** pp = &pParent->pFirstChild; *pp != this; pp = &(*pp)->pSibling) {
      assert(*pp != NULL);
    }
    *pp = pSibling;
    pSibling = NULL;
    pParent = NULL;
  }
}


////////////////////////////////////////////////////////////////////////////////
// set clock for all children

global void Object::SetClock(Clock& NewClock) {
  for (Object* p = pFirstChild; p != NULL; p = p->pSibling) {
    p->SetClock(NewClock);
  }
}


////////////////////////////////////////////////////////////////////////////////
// get full object name

global char* Object::GetFullName(char* pcBuffer, const char* pcEnd) {
  char* pc = pcBuffer;
  if (pParent != NULL) {
    pc = pParent->GetFullName(pcBuffer, pcEnd);
  }
  if (pcName != NULL) {
    if (pc != pcBuffer) {
      assert(pc < pcEnd);
      *pc++ = '.';
    }
    int iLength = strlen(pcName);
    assert(pc + iLength < pcEnd);
    memcpy(pc, pcName, iLength);
    pc += iLength;
  }
  assert(pc < pcEnd);
  *pc = 0;
  return pc;
}


////////////////////////////////////////////////////////////////////////////////
// find a child object or return NULL if not found

global const Object* Object::FindChild(const char* pcName) {
  assert(pcName != NULL);
  assert(*pcName != NULL);
  Object* pParent = this;
  const char* pcStart = pcName;
  for (;;) {
    const char* pcEnd = strchr(pcStart, '.');
    if (pcEnd == NULL) {
      pcEnd = pcStart + strlen(pcStart);
    }
    int iLength = pcEnd - pcStart;
    verify(iLength != 0);
    Object* p = pParent->pFirstChild;
    for (;;) {
      if (p == NULL) {
        return NULL;
      }
      if ((int)strlen(p->pcName) == iLength && memcmp(p->pcName, pcStart, iLength) == 0) {
        if (*pcEnd == 0) {
          return p;
        }
        pParent = p;
        pcStart = pcEnd + 1;
        break;
      }
      p = p->pSibling;
    }
  }
}

global const Object* FindObject(const char* pcName) {
  return gObjectRoot.FindChild(pcName);
}


////////////////////////////////////////////////////////////////////////////////
// get a child object or raise an exception if not found

global const Object* Object::GetChild(const char* pcName) {
  const Object* p = FindChild(pcName);
  if (p == NULL) {
    char ac[256];
    GetFullName(ac, ac + sizeof ac);
    error("%s.GetObject(\"%s\") not found", ac, pcName);
  }
  return p;
}

global const Object* GetObject(const char* pcName) {
  const Object* p = gObjectRoot.FindChild(pcName);
  if (p == NULL) {
    error("GetObject(\"%s\") not found", pcName);
  }
  return p;
}


////////////////////////////////////////////////////////////////////////////////
// traverse the object tree with leaves first and call the function

void Object::ForAllChildrenCall(void (*pFunction)(Object*)) {
  if (pFirstChild != NULL) {
    pFirstChild->ForAllChildrenCall(pFunction);
  }
  Object* pSavedSibling = pSibling;
  pFunction(this);
  if (pSavedSibling != NULL) {
    pSavedSibling->ForAllChildrenCall(pFunction);
  }
}

global void ForAllObjectsCall(void (*pFunction)(Object*)) {
  if (gObjectRoot.pFirstChild != NULL) {
    gObjectRoot.pFirstChild->ForAllChildrenCall(pFunction);
  }
}


////////////////////////////////////////////////////////////////////////////////
// traverse the object tree with leaves first and delete objects

void Object::DeleteAllChildrenOfType(const type_info& Class) {
  if (pFirstChild != NULL) {
    pFirstChild->DeleteAllChildrenOfType(Class);
  }
  Object* pSavedSibling = pSibling;
  if (typeid(*this) == Class) {
    delete this;
  }
  if (pSavedSibling != NULL) {
    pSavedSibling->DeleteAllChildrenOfType(Class);
  }
}

global void DeleteAllObjectsOfType(const type_info& Class) {
  if (gObjectRoot.pFirstChild != NULL) {
    gObjectRoot.pFirstChild->DeleteAllChildrenOfType(Class);
  }
}


////////////////////////////////////////////////////////////////////////////////
// linked list of class names and associated object creation functions

struct ClassListEntry {
  ClassListEntry* pNext;
  const char* pcClassName;
  Object* (*pfnCreate)();
};

static ClassListEntry* gpClassListHead;

global void _RegisterPersistentClass(const type_info& Class, Object* (*pfnCreate)()) {
  ClassListEntry* p;
  verify((p = (ClassListEntry*)malloc(sizeof ClassListEntry)) != NULL);
  p->pNext = gpClassListHead;
  p->pcClassName = Class.name();
  p->pfnCreate = pfnCreate;
  gpClassListHead = p;
}

global void _UnregisterPersistentClass(const type_info& Class) {
  for (ClassListEntry** pp = &gpClassListHead; *pp != NULL; pp = &(*pp)->pNext) {
    if (strcmp((*pp)->pcClassName, Class.name()) == 0) {
      ClassListEntry* p = *pp;
      *pp = (*pp)->pNext;
      free(p);
      return;
    }
  }
  error("cannot unregister %s", Class.name());
}


////////////////////////////////////////////////////////////////////////////////
// create object from string
/*
static Object* CreateObjectFromString(const char* pcClassName) {
  ClassListEntry* p = gpClassListHead;
  while (p != NULL) {
    if (strcmp(pcClassName, p->pcClassName) == 0) {
      return p->pfnCreate();
    }
    p = p->pNext;
  }
  error("cannot create object of type %s", pcClassName);
  return NULL; // calm compiler
}
*/


////////////////////////////////////////////////////////////////////////////////
// persistence
// TODO: implement

void Object::Write(File& Out) {
  //out.WriteString(pcName);
  pParent->Write(Out);
  pFirstChild->Write(Out);
  pSibling->Write(Out);
}

void Object::Read(File& In) {
  //char ac[256];
  //in.ReadString(ac, ac + sizeof ac);
  pParent->Read(In);
}


////////////////////////////////////////////////////////////////////////////////
// read objects from disk

global void ReadObjects(File& /*In*/) {
}


////////////////////////////////////////////////////////////////////////////////
// write objects to disk

global void WriteObjects(File& /*Out*/) {
}
