/* 
 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * 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. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#ifndef _GRTPP_VALUE_H_
#define _GRTPP_VALUE_H_

#include "grtpp.h"

namespace grt {
  class GRT;
  class MetaClass;
  
  namespace internal
  {
    class Serializer;
    class Unserializer;
  };

  //------------------------------------------------------------------------------------------------
  
  enum Type 
  {
    UnknownType,
    AnyType = UnknownType,
    IntegerType,
    DoubleType,
    StringType,
    ListType,
    DictType,
    ObjectType
  };
  
  struct MYSQLGRT_PUBLIC SimpleTypeSpec
  {
    Type type;
    std::string object_class;
    
    SimpleTypeSpec() : type(UnknownType) {}
    SimpleTypeSpec(const SimpleTypeSpec &o) : type(o.type), object_class(o.object_class) {}
    inline SimpleTypeSpec &operator= (const SimpleTypeSpec &o)
    {
      type= o.type;
      object_class= o.object_class;
      return *this;
    }
    
    inline bool operator==(const SimpleTypeSpec &o) const
    {
      return o.type == type && o.object_class == object_class;
    }
    
    inline bool operator!=(const SimpleTypeSpec &o) const
    {
      return o.type != type || o.object_class != object_class;
    }
  };
  
  /*!
    \brief Type definition for GRT objects
    Describes type if a member defined by TypeSpec is of type: Integer, Double, String, etc
    Describes class if a member defined by TypeSpec is of Object type
  */
  struct MYSQLGRT_PUBLIC TypeSpec
  {
    SimpleTypeSpec base;        //!< Type of the object itself
    SimpleTypeSpec content;     //!< Type of the stored items in case when object is of type of ListRef<T> or Dict
    bool operator==(const TypeSpec& t) const
    {
      return ( base == t.base && content == t.content );
    }
  };
  
  class ValueRef;
  class BaseListRef;

  //------------------------------------------------------------------------------------------------


  class MYSQLGRT_PUBLIC type_error : public std::logic_error
  {
  public:
    type_error(Type expected, Type actual);
    type_error(TypeSpec expected, TypeSpec actual);
    type_error(Type expected, Type actual, Type container);
    type_error(const std::string &expected, const std::string &actual);
    type_error(const std::string &expected, const std::string &actual, Type container);
    type_error(const std::string &expected, Type actual);
    type_error(const std::string &msg) : std::logic_error(msg) {}
  };

  //------------------------------------------------------------------------------------------------

  namespace internal {
    
    class Object;
  
    class MYSQLGRT_PUBLIC Value
    {
    public:
      virtual Type get_type() const= 0;
    
      Value *retain() { /*uses mutex in some OSs g_atomic_int_inc(&_refcount);*/++_refcount; return this; }
      void release() {
#ifdef DEBUG
        if (_refcount == 0) g_warning("GRT: releasing invalid object");
#endif          
        //if (g_atomic_int_dec_and_test(&_refcount)) // returns true if --refcount==0
        if (--_refcount == 0)
          delete this;
      }

      virtual std::string repr() const= 0;

      inline gint refcount() const { return _refcount; } //g_atomic_int_get(&_refcount); }

      virtual void mark_global() const {}
      virtual void unmark_global() const {}
      
      virtual bool equals(const Value*) const= 0;
      virtual bool less_than(const Value *) const= 0;
      
      // reset_references helps to free memory allocated
      // by Value. The method is overriden in Object, List and Dict
      virtual void reset_references() {}

    protected:
      Value() : _refcount(0) {}
      virtual ~Value() {}

    private:
      Value(const Value&) {}

      mutable gint _refcount;
    };
    
    
    
    class MYSQLGRT_PUBLIC Integer : public Value
    {
    public:
      typedef long storage_type;
      
    public:    
      Integer(storage_type value);
      static Integer* get(storage_type value);

      static Type static_type() { return IntegerType; }
      virtual Type get_type() const { return IntegerType; }
      virtual std::string repr() const;
      
      inline operator storage_type () const { return _value; }
      inline storage_type operator*() const { return _value; }

      virtual bool equals(const Value*) const;
      virtual bool less_than(const Value *) const;
      
    protected:    
      storage_type _value;
    };
    
    //------------------------------------------------------------------------------------------------
    
    class MYSQLGRT_PUBLIC Double : public Value
    {
    public:
      typedef double storage_type;
      
    public:
      Double(storage_type value);
      static Double* get(storage_type value);

      static Type static_type() { return DoubleType; }
      virtual Type get_type() const { return DoubleType; }
      virtual std::string repr() const;
      
      inline operator storage_type () const { return _value; }
      inline storage_type operator*() const { return _value; }

      virtual bool equals(const Value*) const;
      virtual bool less_than(const Value *) const;

    protected:    
      storage_type _value;
    };
    
    //------------------------------------------------------------------------------------------------
    
    class MYSQLGRT_PUBLIC String : public Value
    {
    public:
      typedef std::string storage_type;
      
    public:
      String(const storage_type& value);
      static String* get(const storage_type& value);

      static Type static_type() { return StringType; }
      virtual Type get_type() const { return StringType; }
      virtual std::string repr() const;
      
      inline operator storage_type () const { return _value; }
      inline const storage_type& operator*() const { return _value; }
      inline const char *c_str() const { return _value.c_str(); }
      inline bool empty() const {return _value.empty(); }

      virtual bool equals(const Value*) const;
      virtual bool less_than(const Value *) const;

    protected:    
      storage_type _value;
    };
    
    //------------------------------------------------------------------------------------------------
    
    class MYSQLGRT_PUBLIC List : public Value
    {
    public:
      typedef std::vector<ValueRef> storage_type;
      enum { npos = 0xffffffff };
      
      typedef std::vector<ValueRef>::const_iterator raw_const_iterator;
      typedef std::vector<ValueRef>::const_reverse_iterator raw_const_reverse_iterator;
      typedef std::vector<ValueRef>::iterator raw_iterator;

    public:
      List(GRT *grt, bool allow_null);
      List(GRT *grt, Type type, const std::string &content_class, bool allow_null);
      
      static Type static_type() { return ListType; }
      virtual Type get_type() const { return ListType; }
      inline const SimpleTypeSpec &content_type_spec() const { return _content_type; }
      inline Type content_type() const { return _content_type.type; }
      inline const std::string &content_class_name() const { return _content_type.object_class; }
      virtual std::string repr() const;

      inline const ValueRef &get(size_t index) const
      {
        if (index >= count()) throw bad_item(index, count());
        return _content[index]; 
      }

      virtual void set_unchecked(size_t index, const ValueRef &value);
      virtual void insert_unchecked(const ValueRef &value, size_t index= npos);

      void set_checked(size_t index, const ValueRef &value);
      void insert_checked(const ValueRef &value, size_t index= npos);

      bool check_assignable(const ValueRef &value) const;
      bool null_allowed() const { return _allow_null; }
      
      inline size_t count() const 
      {
        return _content.size(); 
      }
      
      virtual void remove(const ValueRef & value);
      virtual void remove(size_t index);
      void reorder(size_t oi, size_t ni);
      
      size_t get_index(const ValueRef &value);
      
      inline const ValueRef &operator[](size_t i) const { return get(i); }

      virtual bool equals(const Value*) const;
      virtual bool less_than(const Value *) const;

      raw_const_iterator raw_begin() const { return _content.begin(); }
      raw_const_iterator raw_end() const { return _content.end(); }

      raw_const_reverse_iterator raw_rbegin() const { return _content.rbegin(); }
      raw_const_reverse_iterator raw_rend() const { return _content.rend(); }

      raw_iterator raw_begin() { return _content.begin(); }
      raw_iterator raw_end() { return _content.end(); }
      
      inline GRT *get_grt() const { return _grt; }


    public:
      void __retype(Type type, const std::string &content_class);

      virtual void mark_global() const;
      virtual void unmark_global() const;

      virtual void reset_references();
    protected:
      friend class ::grt::GRT;
      friend class internal::Serializer;
      friend class internal::Unserializer;
      
      virtual ~List();

      GRT *_grt;

      storage_type _content;
      SimpleTypeSpec _content_type;
      bool _allow_null;

      mutable short _is_global;
    };

    
    
    class MYSQLGRT_PUBLIC OwnedList : public List
    {
    public:
      OwnedList(GRT *grt, Type type, const std::string &content_class, Object *owner, bool allow_null);

      virtual void set_unchecked(size_t index, const ValueRef &value);
      virtual void insert_unchecked(const ValueRef &value, size_t index= npos);

      virtual void remove(const ValueRef & value);
      virtual void remove(size_t index);

      Object *owner_of_owned_list() const { return _owner; }
    protected:
      Object *_owner; // internal: set if it belongs to an object
    };

    


    //------------------------------------------------------------------------------------------------
    
    class MYSQLGRT_PUBLIC Dict : public Value
    {
    public:
      typedef std::map<std::string, ValueRef> storage_type;
      typedef storage_type::const_iterator const_iterator;
      typedef storage_type::const_iterator iterator;

    public:
      Dict(GRT *grt, bool allow_null);
      Dict(GRT *grt, Type type, const std::string &content_class, bool allow_null);

      static Type static_type() { return DictType; }
      virtual Type get_type() const { return DictType; }
      virtual std::string repr() const;

      Type content_type() const { return _content_type.type; }
      const std::string &content_class_name() const { return _content_type.object_class; }
      
      ValueRef operator [](const std::string &key) const;
      
      const_iterator begin() const { return _content.begin(); }
      const_iterator end() const { return _content.end(); }
      
      iterator begin() { return _content.begin(); }
      iterator end() { return _content.end(); }
      
      bool has_key(const std::string &key) const;
      ValueRef get(const std::string &key) const;
      virtual void set(const std::string &key, const ValueRef &value);
      virtual void remove(const std::string &key);
      virtual void reset_entries();
      size_t count() const { return _content.size(); }

      virtual bool equals(const Value*) const;
      virtual bool less_than(const Value *) const;

      GRT *get_grt() const { return _grt; }

      virtual void mark_global() const;
      virtual void unmark_global() const;
      
      virtual void reset_references();
    protected:
      friend class ::grt::GRT;

      GRT *_grt;
      storage_type _content;
      SimpleTypeSpec _content_type;
      bool _allow_null;

      mutable short _is_global; // whether value is attached to the global GRT tree
    };

    
    
    class MYSQLGRT_PUBLIC OwnedDict : public Dict
    {
    public:
      OwnedDict(GRT *grt, Type type, const std::string &content_class, Object *owner, bool allow_null);

      virtual void set(const std::string &key, const ValueRef &value);
      virtual void remove(const std::string &key);
      virtual void reset_entries();

      Object *owner_of_owned_dict() const { return _owner; }
    protected:
      Object *_owner; // internal: set if it belongs to an object
    };

    //------------------------------------------------------------------------------------------------
    //QQQ Do we really need this, looks like this enum is never used
    enum ListChangeType
    {
      ListAdded,
      ListRemoved,
      ListReplaced
    };
    
    
    /** Object validity flag for weak-references
     * 
     * Allocated by objects to signal whether it's valid and held by weak references
     * to check whether the object is still valid.
     * @ingroup GRT
     */
    class ObjectValidFlag
    {
    public:
      ObjectValidFlag(bool valid) : flagp(new Flag(valid))
      {}
      
      ObjectValidFlag(const ObjectValidFlag &copy) : flagp(copy.flagp)
      {
        ref_added();
      }
      
      inline ObjectValidFlag &operator= (const ObjectValidFlag &other)
      {
        ObjectValidFlag tmp(other); 
        swap(tmp);
        return *this;
      }
      
      ~ObjectValidFlag()
      {
        ref_deleted();
      }
      
      bool valid() const
      {
        return flagp->valid;
      }
      
      void invalidate() 
      {
        flagp->valid= false;
      }
      
      inline void swap(ObjectValidFlag &other)
      {
         if (flagp != other.flagp)
         {
           ref_deleted();
           flagp= other.flagp;
           ref_added();
         }
      }
      
    private:
      struct Flag {
        gint refcount;
        bool valid;
        
        inline Flag(bool initial) : refcount(1), valid(initial) {}
      };
      
      Flag *flagp;
      
      inline void ref_added()
      {
        g_atomic_int_inc(&flagp->refcount);
      }
      
      inline void ref_deleted()
      {
        if (g_atomic_int_dec_and_test(&flagp->refcount))
          delete flagp; // if no more refcounts, delete it
      }
    };

    /** Base GRT Object class.
     * 
     * This is subclassed by automatically generated GRT object classes.
     * 
     * @ingroup GRT
     */ 
    class MYSQLGRT_PUBLIC Object : public Value
    {
    public:
      static std::string static_class_name() { return "Object"; }

      const std::string &id() const;
      MetaClass* get_metaclass() const;
      const std::string &class_name() const;

      static Type static_type() { return ObjectType; }
      virtual Type get_type() const { return ObjectType; }
      virtual std::string repr() const;

      bool is_instance(MetaClass *gclass) const;
      bool is_instance(const std::string &name) const;

      void set_member(const std::string &member, const ValueRef &value);
      ValueRef get_member(const std::string &member) const;
      std::string get_string_member(const std::string &member) const;
      Double::storage_type get_double_member(const std::string &member) const;
      Integer::storage_type get_integer_member(const std::string &member) const;
      bool has_member(const std::string &member) const;

      bool has_method(const std::string &method) const;

      virtual bool equals(const Value*) const;
      virtual bool less_than(const Value *) const;

      virtual ValueRef call(const std::string &method, const BaseListRef &args);

      GRT *get_grt() const;

      bool is_global() const { return _is_global != 0; }

      boost::signals2::signal<void (const std::string&, const ValueRef&)>* signal_changed() { return &_changed_signal; }
      boost::signals2::signal<void (OwnedList*, bool, const grt::ValueRef&)>* signal_list_changed() { return &_list_changed_signal; }
      boost::signals2::signal<void (OwnedDict*, bool, const std::string&)>* signal_dict_changed() { return &_dict_changed_signal; }
      
      virtual void reset_references();
    public:
      virtual void init();
      
      void __set_id(const std::string &id);

    protected:
      friend class OwnedList;
      friend class OwnedDict;
      friend class ::grt::GRT;
      friend class internal::Unserializer;

      explicit Object(MetaClass *gclass);
      Object(GRT *grt, MetaClass *gclass); /* deprecated */
      virtual ~Object()
      {
        _valid_flag.invalidate();
      }

      virtual void mark_global() const;
      virtual void unmark_global() const;

      void owned_member_changed(const std::string &name, const grt::ValueRef &ovalue, const grt::ValueRef &nvalue);
      void member_changed(const std::string &name, const grt::ValueRef &ovalue, const grt::ValueRef &nvalue);

      virtual void owned_list_item_added(OwnedList *list, const grt::ValueRef &value);
      virtual void owned_list_item_removed(OwnedList *list, const grt::ValueRef &value);

      virtual void owned_dict_item_set(OwnedDict *dict, const std::string &key);
      virtual void owned_dict_item_removed(OwnedDict *dict, const std::string &key);

      //use _metaclass->get_grt() GRT *_grt;
      MetaClass* _metaclass;
      std::string _id;
      boost::signals2::signal<void (const std::string&, const ValueRef&)> _changed_signal;
      boost::signals2::signal<void (OwnedList*, bool, const grt::ValueRef&)> _list_changed_signal;
      boost::signals2::signal<void (OwnedDict*, bool, const std::string&)> _dict_changed_signal;

      ObjectValidFlag _valid_flag;
      
      mutable short _is_global; // whether object is attached to the global GRT tree
      
    public:
      const ObjectValidFlag &weakref_valid_flag() const { return _valid_flag; }
    };


    //----------------------------------------------------------------------------------------------------


   
     /** Registry for GRT object classes.
      * 
      * This object is a singleton used to globally store the list of all
      * GRT object implementing C++ classes available. Each class, such
      * as db_Table, must call the registration function once. Whenever a
      * GRT instance is created, it will look at the list of classes and
      * register them with itself (so that allocation functions, member and
      * method pointers can be properly initialized).
      * 
      * @ingroup GRTInternal
      */
     typedef void (*ClassRegistrationFunction)(GRT *grt);

     struct MYSQLGRT_PUBLIC ClassRegistry
     {
     private:
       friend class ::grt::GRT;
          
       std::map<std::string, ClassRegistrationFunction> classes;

       ClassRegistry();
       
       /** Register all known classes in the given GRT context.
        */
       void register_all(GRT *grt);
       
       static ClassRegistry* get_instance();

     public:
       /** Template function to globally register a GRT class.
        */
       template<class C>
         static void register_class()
         {
            get_instance()->classes[C::static_class_name()]= &C::grt_register;
         }
     };

  }; // internal
}; //grt

#endif // _GRTPP_VALUE_H_
