// ---------------------------------------------------------------------------
// - Nameset.cpp                                                            -
// - afnix engine - nameset class 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 "Symbol.hpp"
#include "Nameset.hpp"
#include "Exception.hpp"

namespace afnix {

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

  // the parent nameset as a quark
  static const long QUARK_PARENT = String::intern ("..");

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

  // create a default nameset

  Nameset::Nameset (void) {
    p_parent = nilp;
  }

  // destroy this nameset

  Nameset::~Nameset (void) {
    Object::dref (p_parent);
  }

  // return the parent nameset

  Nameset* Nameset::getparent (void) const {
    rdlock ();
    Nameset* result = p_parent;
    unlock ();
    return result;
  }

  // set the parent nameset 

  void Nameset::setparent (Nameset* nset) {
    wrlock ();
    if (p_parent != nilp) {
      remove (QUARK_PARENT);
      Object::dref (p_parent);
    }
    Object::iref (nset);
    p_parent = nset;
    if (nset != nilp) symcst (QUARK_PARENT, nset);
    unlock ();
  }

  // add a new object by name

  void Nameset::bind (const String& name, Object* object) {
    wrlock ();
    try {
      bind (name.toquark (), object);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the name exists in this nameset
  bool Nameset::exists (const String& name) const {
    rdlock ();
    try {
      bool result = exists (name.toquark ());
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the name exists in this nameset
  
  bool Nameset::isvalid (const long quark) const {
    rdlock ();
    try {
      // check in the local nameset
      bool result = exists (quark);
      if (result == true) {
	unlock ();
	return result;
      }
      // check in the parent nameset
      if (p_parent == nilp) {
	unlock ();
	return false;
      }
      result = p_parent->isvalid (quark);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the name exists in this nameset
  
  bool Nameset::isvalid (const String& name) const {
    rdlock ();
    try {
      bool result = isvalid (name.toquark ());
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return an object by name but do not evaluate

  Object* Nameset::find (const String& name) const {
    return find (name.toquark ());
  }

  // remove an object by name

  void Nameset::remove (const String& name) {
    remove (name.toquark ());
  }

  // create a nameset set by quark

  Nameset* Nameset::mknset (const long quark) {
    wrlock ();
    try {
      // look for an existing one
      if (exists (quark) == true) {
	Nameset* nset = getnset (quark);
	unlock ();
	return nset;
      }
      // create the result nameset and bind it
      Nameset* result = dup ();
      symcst (quark, result);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // create a nameset set by name

  Nameset* Nameset::mknset (const String& name) {
    return mknset (name.toquark ());
  }

  // get a nameset by quark

  Nameset* Nameset::getnset (const long quark) const {
    rdlock ();
    if (exists (quark) == false) {
      unlock ();
      throw Exception ("nameset-error", "cannot find nameset",
		       String::qmap (quark));
    }
    // get the object and map it - most likelly the object is a symbol
    Object*  obj = find (quark);
    Symbol*  sym = dynamic_cast <Symbol*> (obj);
    // remap the object and check
    if (sym != nilp) obj = sym->getobj ();
    Nameset* nset = dynamic_cast <Nameset*> (obj);
    if (nset == nilp) {
      unlock ();
      throw Exception ("type-error", "object is not a nameset", 
		       String::qmap (quark));
    }
    unlock ();
    return nset;
  }

  // get a nameset by name

  Nameset* Nameset::getnset (const String& name) const {
    return getnset (name.toquark ());
  }

  // create a new const symbol by quark

  void Nameset::symcst (const long quark, Object* object) {
    wrlock ();
    try {
      Symbol* sym = new Symbol (quark, object);
      sym->setconst (true);
      bind (quark, sym);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // create a new const symbol by name

  void Nameset::symcst (const String& name, Object* object) {
    wrlock ();
    try {
      Symbol* sym = new Symbol (name, object);
      sym->setconst (true);
      bind (name, sym);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // create a new symbol by quark

  void Nameset::symdef (const long quark, Object* object) {
    wrlock ();
    try {
      Symbol* sym = new Symbol (quark, object);
      bind (quark, sym);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // create a new symbol by name

  void Nameset::symdef (const String& name, Object* object) {
    wrlock ();
    try {
      Symbol* sym = new Symbol (name, object);
      bind (name, sym);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
}
