// ---------------------------------------------------------------------------
// - Serial.cpp                                                              -
// - standard object library - serializable object implementation            -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  is  distributed in  the hope  that it will be useful, but -
// - without  any  warranty;  without  even   the   implied    warranty   of -
// - merchantability or fitness for a particular purpose.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2008 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Set.hpp"
#include "Byte.hpp"
#include "Cons.hpp"
#include "Real.hpp"
#include "Input.hpp"
#include "Regex.hpp"
#include "Stdsid.hxx"
#include "Vector.hpp"
#include "Strvec.hpp"
#include "Relatif.hpp"
#include "Boolean.hpp"
#include "Property.hpp"
#include "Character.hpp"
#include "NameTable.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - private section                                                       -
  // -------------------------------------------------------------------------

  // the maximum number of serial codes
  static const long SERIAL_CBKMAX = 256;
  // the array of serial callback
  static Serial::t_genser* p_sercbk = nilp;

  // this procedure initialize the serial array callbacks
  static void init_serial_cbk (void) {
    if (p_sercbk != nilp) return;
    p_sercbk = new Serial::t_genser[SERIAL_CBKMAX];
    for (long i = 0; i < SERIAL_CBKMAX; i++) p_sercbk[i] = nilp;
  }

  // add a new serial callback
  static void add_serial_cbk (const t_byte sid, Serial::t_genser cbk) {
    init_serial_cbk ();
    if ((sid == 0x00) || (p_sercbk[sid] != nilp))
      throw Exception ("serial-errror", "cannot add callback");
    p_sercbk[sid] = cbk;
  }

  // get a serial object by sid
  static Serial* get_serial_object (const t_byte sid) {
    if ((p_sercbk == nilp) || (p_sercbk[sid] == nilp)) 
      throw Exception ("serial-error", "cannot find object to deserialize");
    Serial::t_genser cbk = p_sercbk[sid];
    return (cbk ());
  }

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // return the object serial code

  t_byte Serial::serialid (void) const {
    throw Exception ("serial-error", "cannot get serial id for", repr ());
  }

  // serialize an object to an output stream

  void Serial::wrstream (class Output& os) const {
    throw Exception ("serial-error", "cannot serialize object", repr ());
  }

  // deserialize an object from an input stream

  void Serial::rdstream (class Input& is) {
    throw Exception ("serial-error", "cannot deserialize object", repr ());
  }

  // serialize an object with it serial id

  void Serial::serialize (Output& os) const {
    // write the serial id
    os.write ((char) serialid ());
    // serialize the object
    wrstream (os);
  }

  // write a nil id to an output strea

  void Serial::wrnilid (class Output& os) {
    os.write ((char) SERIAL_NILP_ID);
  }
  
  // register a deserialize callback

  t_byte Serial::setsid (const t_byte sid, t_genser cbk) {
    add_serial_cbk (sid, cbk);
    return sid;
  }

  // return the a standard object by serial id

  Serial* Serial::getserial (const t_byte sid) {
    switch (sid) {
    case SERIAL_NILP_ID:
      return nilp;
      break;
    case SERIAL_BOOL_ID:
      return new Boolean;
      break;
    case SERIAL_BYTE_ID:
      return new Byte;
      break;
    case SERIAL_INTG_ID:
      return new Integer;
      break;
    case SERIAL_RELT_ID:
      return new Relatif;
      break;
    case SERIAL_REAL_ID:
      return new Real;
      break;
    case SERIAL_CHAR_ID:
      return new Character;
      break;
    case SERIAL_STRG_ID:
      return new String;
      break;
    case SERIAL_REGX_ID:
      return new Regex;
      break;
    case SERIAL_CONS_ID:
      return new Cons;
      break;
    case SERIAL_VECT_ID:
      return new Vector;
      break;
    case SERIAL_OSET_ID:
      return new Set;
      break;
    case SERIAL_NTBL_ID:
      return new NameTable;
      break;
    case SERIAL_STRV_ID:
      return new Strvec;
      break;
    case SERIAL_PROP_ID:
      return new Property;
      break;
    default:
      break;
    }
    // check if we can get a callback
    return get_serial_object (sid);
  }

  // return an object by deserialization

  Object* Serial::deserialize (Input& is) {
    // get a new object by serial id
    t_byte   sid = is.read ();
    Serial* sobj = Serial::getserial (sid);
    if (sobj == nilp) return nilp;
    // read in the object
    sobj->rdstream (is);
    return sobj;
  } 
}
