/////////////////////////////////////////////////////////////////////////////
// Name:        objstrm.cpp
// Purpose:     wxObjectStream classes
// Author:      Guilhem Lavaux
// Modified by:
// Created:     16/07/98
// RCS-ID:      $Id$
// Copyright:   (c) 1998 Guilhem Lavaux
// Licence:     wxWindows license
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "objstrm.h"
#endif

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
  #pragma hdrstop
#endif

#if wxUSE_SERIAL && wxUSE_STREAMS

#include "wx/object.h"
#include "wx/objstrm.h"
#include "wx/datstrm.h"

#define WXOBJ_BEGIN "OBEGIN"
#define WXOBJ_BEG_LEN 6

#define TAG_EMPTY_OBJECT "NULL"
#define TAG_DUPLICATE_OBJECT "DUPLIC"

// ----------------------------------------------------------------------------
// wxObjectOutputStream
// ----------------------------------------------------------------------------

wxObjectOutputStream::wxObjectOutputStream(wxOutputStream& s)
  : wxFilterOutputStream(s)
{
  m_saving = FALSE;
}

wxString wxObjectOutputStream::GetObjectName(wxObject *obj)
{
  wxString name;

  name.Printf(wxT("%x"), (unsigned long)obj);
  return name;
}

void wxObjectOutputStream::WriteObjectDef(wxObjectStreamInfo& info)
{
  wxDataOutputStream data_s(*this); 

  Write(WXOBJ_BEGIN, WXOBJ_BEG_LEN);

  if (info.duplicate) {
    data_s.WriteString(TAG_DUPLICATE_OBJECT);
    data_s.WriteString(GetObjectName(info.object));
    wxPrintf(wxT("info.object (dup %s)\n"), info.object->GetClassInfo()->GetClassName());
    return;
  }

  if (info.object) {
    data_s.WriteString(info.object->GetClassInfo()->GetClassName());
    wxPrintf(wxT("info.object (%s)\n"), info.object->GetClassInfo()->GetClassName());
  } else {
    data_s.WriteString(TAG_EMPTY_OBJECT);
    wxPrintf(wxT("info.object (NULL)\n"));
    return;
  }

  data_s.WriteString(GetObjectName(info.object));

  // I assume an object will not have millions of children
  // Hmmm ... it could have (for example wxGrid)
  data_s.Write32(info.children.Number());
}

void wxObjectOutputStream::AddChild(wxObject *obj)
{
  wxObjectStreamInfo *info;

  if (!FirstStage())
    return;

  info = new wxObjectStreamInfo;

  if (m_saved_objs.Member(obj) != NULL) {
    info->duplicate = TRUE;
  } else {
    info->duplicate = FALSE;
    m_saved_objs.Append(obj);
  }
  if (!obj)
    info->duplicate = FALSE;

  info->n_children = 0;
  info->object = obj;
  info->parent = m_current_info; // Not useful here.
  m_current_info->n_children++;
  m_current_info->children.Append(info);
}

void wxObjectOutputStream::ProcessObjectDef(wxObjectStreamInfo *info)
{
  wxNode *node;

  m_current_info = info;
  // First stage: get children of obj
  if (info->object && !info->duplicate)
    info->object->StoreObject(*this);

  // Prepare and write the sub-entry about the child obj.
  WriteObjectDef(*info);

  node = info->children.First();
  
  while (node) {
    ProcessObjectDef((wxObjectStreamInfo *)node->Data());
    node = node->Next();
  }
}

void wxObjectOutputStream::ProcessObjectData(wxObjectStreamInfo *info)
{
  wxNode *node = info->children.First();

  m_current_info = info;

  if (info->object && !info->duplicate)
    info->object->StoreObject(*this);

  while (node) {
    ProcessObjectData((wxObjectStreamInfo *)node->Data());
    node = node->Next();
  }
}

bool wxObjectOutputStream::SaveObject(wxObject& obj)
{
  wxObjectStreamInfo info;

  if (m_saving)
    return FALSE;

  m_saving = TRUE;

  // First stage
  m_stage = 0;
  info.object = &obj;
  info.n_children = 0;
  info.duplicate = FALSE;
  ProcessObjectDef(&info);

  m_stage = 1;
  ProcessObjectData(&info);

  info.children.Clear();
  m_saved_objs.Clear();

  m_saving = FALSE;

  return TRUE;
}

// ----------------------------------------------------------------------------
// wxObjectInputStream
// ----------------------------------------------------------------------------

wxObjectInputStream::wxObjectInputStream(wxInputStream& s)
  : wxFilterInputStream(s)
{
  m_secondcall = FALSE;
}

wxObject *wxObjectInputStream::SolveName(const wxString& name) const
{
  wxNode *node = m_solver.First();
  wxObjectStreamInfo *info;

  while (node) {
    info = (wxObjectStreamInfo *)node->Data();
    if (info->object_name == name)
      return info->object;

    node = node->Next();
  }
  return (wxObject *) NULL;
}

wxObject *wxObjectInputStream::GetParent() const
{
  if (!m_current_info->parent)
    return (wxObject *) NULL;

  return m_current_info->parent->object;
}

wxObject *wxObjectInputStream::GetChild()
{
  wxObject *obj = GetChild(0);

  m_current_info->children_removed++;

  return obj;
}

wxObject *wxObjectInputStream::GetChild(int no) const
{
  wxNode *node;
  wxObjectStreamInfo *info;

  if (m_current_info->children_removed >= m_current_info->n_children)
    return (wxObject *) NULL;

  node = m_current_info->children.Nth(m_current_info->children_removed+no);

  if (!node)
    return (wxObject *) NULL;

  info = (wxObjectStreamInfo *)node->Data();

  return info->object;
}

void wxObjectInputStream::RemoveChildren(int nb)
{
  m_current_info->children_removed += nb;
}

bool wxObjectInputStream::ReadObjectDef(wxObjectStreamInfo *info)
{
  wxDataInputStream data_s(*this);
  char sig[WXOBJ_BEG_LEN+1];
  wxString class_name;

  Read(sig, WXOBJ_BEG_LEN);
  sig[WXOBJ_BEG_LEN] = 0;
  if (wxString(sig) != WXOBJ_BEGIN)
    return FALSE;

  class_name = data_s.ReadString();
  info->children_removed = 0;
  info->n_children = 0;

  if (class_name == TAG_EMPTY_OBJECT)
    info->object = (wxObject *) NULL;
  else if (class_name == TAG_DUPLICATE_OBJECT) {
    info->object_name = data_s.ReadString();
    info->object = SolveName(info->object_name);
  } else {
    info->object_name = data_s.ReadString();
    info->object = wxCreateDynamicObject( WXSTRINGCAST class_name);
    info->n_children = data_s.Read32();
  }
  return TRUE;
}

wxObjectStreamInfo *wxObjectInputStream::ProcessObjectDef(wxObjectStreamInfo *parent)
{
  wxObjectStreamInfo *info, *c_info;
  int c;

  info = new wxObjectStreamInfo;
  info->parent = parent;
  info->children.DeleteContents(TRUE);

  m_solver.Append(info);

  if (!ReadObjectDef(info))
    return (wxObjectStreamInfo *) NULL;

  for (c=0;c<info->n_children;c++) {
    c_info = ProcessObjectDef(info);
    if (!c_info)
      return (wxObjectStreamInfo *) NULL;
    info->children.Append(c_info);
  }

  return info;
}

void wxObjectInputStream::ProcessObjectData(wxObjectStreamInfo *info)
{
  wxNode *node = info->children.First();

  m_current_info = info;

  if (info->object)
    info->object->LoadObject(*this);
  while (node) {
    ProcessObjectData((wxObjectStreamInfo *)node->Data());
    node = node->Next();
  }

  m_current_info = info;

  if (info->recall) {
    m_secondcall = TRUE;
    info->object->LoadObject(*this);
    m_secondcall = FALSE;
  }
}

wxObject *wxObjectInputStream::LoadObject()
{
  wxObjectStreamInfo *info;
  wxObject *object;

  info = ProcessObjectDef((wxObjectStreamInfo *) NULL);
  if (!info)
    return (wxObject *) NULL;
  ProcessObjectData(info);

  object = info->object;

  delete info; // It's magic ! The whole tree is destroyed.

  return object;
}

#endif // wxUSE_SERIAL && wxUSE_STREAMS

