#include "python.h"
#include <typeinfo>

namespace python {

   // void Object::set(PyObject *pyobj, bool owned = false) {{{
   
   void Object::set(PyObject *pyobj, bool owned) {
   DEBUG("void Object::set(PyObject " << pyobj << ", bool "<< owned<< ")");
      release();
      object = pyobj;
      if ( not owned ) {
         incref();
      }
      validate();
      DEBUG("Object set to " << *this);
      m_owned = owned;
   }
   
   // }}}
   // void Object::release() {{{
   
   void Object::release() {
   DEBUG("void Object::release()");
      decref();
      object = NULL;
   }
   
   // }}}
   // void Object::validate() {{{
   
   void Object::validate() {
      if ( not accepts( object ) ) {
         // We have the wrong type
         DEBUG(*this);
         release();
         check_errors();

         // Use RTTI to get the type
         std::string s("Error creating object of type ");
         s += (typeid (*this)).name();
         throw TypeError(s);
      }
   }
   
   // }}}
   // Object::Object(const Object & obj) {{{
      
   Object::Object(const Object & obj) {
      set(obj.object);
   }

   // }}}
   // Object::Object(PyObject * obj) {{{
   
   Object::Object(PyObject * obj, bool owned) : object (obj) {
      set(obj, owned);
   }

   // }}}
   // Object & Object::operator=(const Object & obj) {{{
   
   Object & Object::operator=(const Object & obj) {
   DEBUG("Object & Object::operator=(const Object & "<< obj << ")");
      if (&obj != this) {
         set(obj.object);
      }
      return *this;
   }

   // }}}
   // Object & Object::operator=(PyObject * obj) {{{

   Object & Object::operator=(PyObject * obj) {
      set(obj);
      return *this;
   }

   // }}}
   // Object::~Object() {{{ 
   
   Object::~Object() { 
      release();
   }

   // }}}
   // void Object::incref() {{{ 

   void Object::incref() { 
      Py_XINCREF(object); 
   }
   
   // }}}
   // void Object::decref() {{{

   void Object::decref() { 
      if (not m_owned) { // only dec it if we inced it
         //if ( reference_count() == 1 ) {
         //   throw RuntimeError("Object::decref() decreasing count too much on "+ this->as_string());
         //} else {
         //   Py_XDECREF(object);
         //}
      }
   }

   // }}}
   // PyObject * Object::ptr() const {{{

   PyObject * Object::ptr() const {
      return object;
   }

   // }}}
   // PyObject * Object::operator*() const {{{

   PyObject * Object::operator*() const {
      return object;
   }

   // }}}
   // bool Object::accepts(PyObject * pyobj) const {{{

   bool Object::accepts(PyObject * pyobj) const {
      return not (pyobj == NULL);
   }

   // }}}
   // std::string Object::as_string() const{{{

   std::string Object::as_string() const {
      // build a std::string without building and python objects so we
      // can call this in emergencies 
      PyObject * string_rep = PyObject_Str(object);
      return std::string( PyString_AsString( string_rep ), PyString_Size( string_rep ) ); // + " type " + (typeid (*this)).name();

   }

   // }}}
   // int Object::reference_count() const{{{

   int Object::reference_count() const{
      // \warning This could break very hard if PyObject changes
      if ( object ) {
         return object->ob_refcnt;
      } else {
         return 0;
      }
   }

   // }}}
   // Type Object::type() const{{{

   Type Object::type() const{
      return Type( PyObject_Type(object) );
   }

   // }}}
   // String Object::str () const{{{ 

   String Object::str () const {
      return String( PyObject_Str(object) );
   } 

   // }}}
   // String Object::repr () const{{{ 

   String Object::repr () const{
      return String ( PyObject_Repr(object) );
   } 

   // }}}
   // bool Object::has_attr (const std::string& s) const {{{

   bool Object::has_attr (const std::string& s) const {
      return PyObject_HasAttrString( object, const_cast<char *>( s.c_str() ) );
   }

   // }}}
   // Object Object::get_attr (const std::string& s) const{{{ 

   Object Object::get_attr (const std::string& s) const{
      PyObject *tmp = PyObject_GetAttrString( object, const_cast<char *>( s.c_str() ) );
      if ( tmp == NULL ) { throw NameError(s + " does not exist in " +  this->as_string()); }
      return Object( tmp, true );
   } 

   // }}}
   // Object Object::get_item (const Object& key) const {{{

   Object Object::get_item (const Object& key) const {
      return Object ( PyObject_GetAttr( object, *key ), true);
   }

   // }}}
   // long Object::hash_value () const{{{

   long Object::hash_value () const{
      return PyObject_Hash(object);
   } 

   // }}}
   // void Object::set_attr (const std::string& s, const Object& value) {{{

   void Object::set_attr (const std::string& s, const Object& value) {
      if ( PyObject_SetAttrString( object, const_cast<char *>( s.c_str() ), *value) == -1) {
         throw AttributeError("Object::set_attr(" + s + ", " + value.as_string() + ") failed");
      }
   }

   // }}}
   // void Object::del_attr (const std::string& s){{{ 

   void Object::del_attr (const std::string& s){
      if ( PyObject_DelAttrString(object, const_cast<char *>( s.c_str() ) ) == -1 ) {
         throw AttributeError("Object::del_attr(" + s + ") failed");
      }
   } 

   // }}}
   // void Object::del_item (const Object& key){{{

   void Object::del_item (const Object& key){
      if ( PyObject_DelAttr(object, *key ) == -1 ) {
         throw KeyError("Object::del_item(" + key.as_string() + ") failed"); ;
      }
   }

   // }}}
   // bool Object::is_callable () const{{{

   bool Object::is_callable () const{
      return PyCallable_Check(object);
   }

   // }}}
   // bool Object::is_list () const{{{

   bool Object::is_list () const{
      return PyList_Check(object);
   } 

   // }}}
   // bool Object::is_mapping () const{{{

   bool Object::is_mapping () const{
      return PyMapping_Check(object);
   }

   // }}}
   // bool Object::is_numeric () const{{{

   bool Object::is_numeric () const{
      return PyNumber_Check(object);
   } 

   // }}}
   // bool Object::is_sequence () const{{{ 

   bool Object::is_sequence () const{
      return PySequence_Check(object);
   } 

   // }}}
   // bool Object::is_true () const{{{

   bool Object::is_true () const{
      return PyObject_IsTrue(object);
   }

   // }}}
   // bool Object::is_type (const Type& t) const{{{

   bool Object::is_type (const Type& t) const{
      return t==type();
   }

   // }}}
   // bool Object::is_tuple() const{{{

   bool Object::is_tuple() const {
      return PyTuple_Check(object);
   }

   // }}}
   // bool Object::is_string() const{{{

   bool Object::is_string() const{
      return PyString_Check(object);
   }

   // }}}
   // bool Object::is_dict() const{{{

   bool Object::is_dict() const{
      return PyDict_Check(object);
   }

   // }}}
   // bool Object::is_null() const {{{

   bool Object::is_null() const {
      return NULL==object;
   }

   // }}}
   // bool Object::is(PyObject* pother) const{{{

   bool Object::is(PyObject* pother) const{
      return object == pother;
   }

   // }}}
   // bool Object::is(const Object& other) const{{{

   bool Object::is(const Object& other) const {
      return object == other.ptr();
   }

   // }}}
   // bool Object::operator==(const Object& o2) const{{{

   bool Object::operator==(const Object& o2) const {
      int k = PyObject_Compare(object,*o2);
      check_errors();
      return k == 0;
   } 

   // }}}
   // bool Object::operator!=(const Object& o2) const{{{

   bool Object::operator!=(const Object& o2) const{
      int k = PyObject_Compare(object,*o2);
      check_errors();
      return not k == 0;
   }

   // }}}
   // bool Object::operator>=(const Object& o2) const{{{

   bool Object::operator>=(const Object& o2) const{
      int k = PyObject_Compare(object,*o2);
      check_errors();
      return k >= 0;
   }

   // }}}
   // bool Object::operator<=(const Object& o2) const{{{ 

   bool Object::operator<=(const Object& o2) const{
      int k = PyObject_Compare(object,*o2);
      check_errors();
      return k <= 0;
   } 

   // }}}
   // bool Object::operator<(const Object& o2) const{{{

   bool Object::operator<(const Object& o2) const{
      int k = PyObject_Compare(object,*o2);
      check_errors();
      return k < 0;
   }

   // }}}
   // bool Object::operator>(const Object& o2) const{{{

   bool Object::operator>(const Object& o2) const{
      int k = PyObject_Compare(object,*o2);
      check_errors();
      return k > 0;
   }

   // }}}

   std::ostream& operator<< (std::ostream& out, const Object& obj) {
      out << obj.as_string();
      return out;
   }  

   // These functions are ugly, and I don't like them. They change
   // Py_Objects ref count by going around the class. They are used to
   // increase the reference count on a const Object
   PyObject* new_reference_to(PyObject* p) { // TODO remove this
      Py_XINCREF(p);
      return p;
   } 
   PyObject* new_reference_to(const Object& g) {
      PyObject* p = g.ptr();
      Py_XINCREF(p);
      return p;
   }

   // It may be nice to change this to a factory, but that may 
   // have problems like having to use pointers, when the rest of the
   // library normally uses non-pointers
   Object asObject (PyObject *p)
   {
      return Object(p, true);
   }



}


syntax highlighted by Code2HTML, v. 0.9.1