/* Copyright (c) 1997-2010
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.de

   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; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   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.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: Value.h 9999 2010-12-15 22:59:13Z gawrilow $
*/

#ifndef POLYMAKE_PERL_VALUE_H
#define POLYMAKE_PERL_VALUE_H

#include "polymake/GenericIO.h"
#include "polymake/internal/assoc.h"
#include "polymake/internal/CharBuffer.h"
#include "polymake/internal/Wary.h"

#include <limits>
#include <cmath>
#include <string>
#include <typeinfo>

// TODO: eliminate all references to SV outside the perl-aware code, use opaque ScalarHolder<ownership> instead

#ifndef POLYMAKE_WITHIN_PERL
struct sv;
#define SV ::sv
struct PerlInterpreter;
#endif

namespace pm { namespace perl {

class SVHolder {
protected:
   SV *sv;

   SVHolder();

   explicit SVHolder(SV *sv_arg)
      : sv(sv_arg) {}

   SVHolder(SV *sv_arg, True);
   void set_copy(SV *sv_arg);
   void forget();
public:
   SV* get() const { return sv; }
   SV* get_temp();
};

class Scalar : public SVHolder {
public:
   Scalar() {}
   Scalar(const Scalar& x) : SVHolder(x.get(), True()) {}

   Scalar& operator= (const Scalar& x)
   {
      set_copy(x.get());
      return *this;
   }
   ~Scalar() { forget(); }

   // The following static functions have nothing to do with this class as such;
   // it's just a suitable place to gather them withour polluting the namespace.
   // Each of them produces finally a scalar value or converts it to something else.

   static SV* undef();

   static SV* const_string(const char *s, size_t l);
   static SV* const_string(const char *s)
   {
      return const_string(s,strlen(s));
   }

   static SV* const_string_with_int(const char *s, size_t l, int i);
   static SV* const_string_with_int(const char *s, int i)
   {
      return const_string_with_int(s,strlen(s),i);
   }

   static int convert_to_int(SV *sv);
   static double convert_to_float(SV *sv);
};

class Value; class Object; class ObjectType; class PropertyValue;
class Hash;   class OptionSet;  class ListResult;
class Stack; class Main;   class FunCall;

template <typename Element=Value> class ArrayOwner;
typedef ArrayOwner<> Array;

template <typename T> class type_cache;

class ArrayHolder : public SVHolder {
   static SV* init_me(int size);
protected:
   ArrayHolder(SV *sv_arg, True) : SVHolder(sv_arg, True()) {}
public:
   explicit ArrayHolder(int reserve=0)
      : SVHolder(init_me(reserve)) {}

   explicit ArrayHolder(SV *sv_arg)
      : SVHolder(sv_arg) {}

   explicit ArrayHolder(const Value&);

   ArrayHolder(const SVHolder& x, int reserve)
      : SVHolder(x)
   {
      upgrade(reserve);
   }

   ArrayHolder(SV *sv_arg, value_flags flags)
      : SVHolder(sv_arg)
   {
      if (flags & value_not_trusted) verify();
   }

   void upgrade(int size);
   void verify() const;
   void set_contains_aliases();

   int size() const;
   int dim(bool& has_sparse_representation) const;
   void resize(int n);
   SV* operator[] (int i) const;

   SV* shift();
   SV* pop();
   void unshift(SV *x);
   void push(SV *x);

   void unshift(const SVHolder& x) { unshift(x.get()); }
   void push(const SVHolder& x) { push(x.get()); }
};

class HashHolder : public SVHolder {
protected:
   HashHolder() : SVHolder(init_me()) {}

   HashHolder(SV *sv_arg, True) : SVHolder(sv_arg, True()) {}

   bool exists(const char *key, size_t klen) const;
   static SV* init_me();
   Value _access(const char* key, size_t klen, bool create) const;
public:
   explicit HashHolder(SV *sv_arg)
      : SVHolder(sv_arg) {}

   void verify();

   bool exists(const std::string& key) const
   {
      return exists(key.c_str(), key.size());
   }

   template <size_t klen>
   bool exists (const char (&key)[klen]) const
   {
      return exists(key, klen-1);
   }
};

class istreambuf : public streambuf_with_input_width {
public:
   istreambuf(SV *sv);

   int lines()
   {
      return CharBuffer::count_lines(this);
   }
};

class ostreambuf : public std::streambuf {
public:
   ostreambuf(SV *sv);
   ~ostreambuf();
protected:
   SV *val;
   int_type overflow(int_type c=traits_type::eof());
};

template <typename StreamBuffer>
class BufferHolder {
protected:
   StreamBuffer my_buf;

   explicit BufferHolder(SV *sv) : my_buf(sv) {}
};

class istream : public BufferHolder<istreambuf>,
                public std::istream {
   friend class Value;
public:
   explicit istream(SV *sv);

   void finish()
   {
      if (good() && CharBuffer::next_non_ws(&my_buf)>=0)
         setstate(failbit);
   }
protected:
   std::runtime_error parse_error() const;
};

class ostream : public BufferHolder<ostreambuf>,
                public std::ostream {
public:
   explicit ostream(SVHolder& sv)
      : BufferHolder<ostreambuf>(sv.get()), std::ostream(&my_buf)
   {
      precision(10);
      exceptions(failbit | badbit);
   }
};

class undefined : public std::runtime_error {
public:
   undefined();
};

class exception : public std::runtime_error {
public:
   exception();
   exception(const char* msg) : std::runtime_error(msg) {}
};

template <typename X> struct TryCanned;
template <typename X> struct Canned;
template <typename X> struct Enum;
template <typename X> struct ReturningList;

struct Anything {
   template <typename X> Anything(const X&) {}
};

template <typename Options=void> class ValueOutput;
template <typename Options=void> class ValueInput;

template <typename ElementType, typename Options>
class ListValueInput
   : public ArrayHolder,
     public GenericInputImpl< ListValueInput<ElementType,Options> >,
     public GenericIOoptions< ListValueInput<ElementType,Options>, Options, 1 > {
   int i, _size, _dim;

   typedef GenericIOoptions< ListValueInput<ElementType,Options>, Options, 1 > _opts;
public:
   typedef ElementType value_type;

   ListValueInput(SV *sv_arg)
      : ArrayHolder(sv_arg, _opts::template get_option< TrustedValue<True> >::value ? value_trusted : value_not_trusted),
        i(0), _size(ArrayHolder::size()), _dim(-1) {}

   template <typename T> inline
   ListValueInput& operator>> (T& x);

   int size() const { return _size; }
   bool at_end() const { return i>=_size; }

   void finish()
   {
      if (_opts::template get_option< CheckEOF<False> >::value && !at_end())
         throw std::runtime_error("list input - size mismatch");
   }

   template <typename T>
   int lookup_lower_dim(bool tell_size_if_dense);

   void skip_item() { ++i; }
   void skip_rest() { i=_size; }

   bool sparse_representation()
   {
      if (extract_bool_param<Options, SparseRepresentation>::specified)
         return extract_bool_param<Options, SparseRepresentation>::value;
      bool has_sparse_representation;
      _dim=ArrayHolder::dim(has_sparse_representation);
      return has_sparse_representation;
   }

   int lookup_dim(bool tell_size_if_dense)
   {
      return sparse_representation() ? _dim :
             tell_size_if_dense ? _size : -1;
   }

   int index()
   {
      if (!sparse_representation())
         throw std::runtime_error("dense/sparse input mismatch");
      int ix=-1;
      *this >> ix;
      if (!_opts::template get_option< TrustedValue<True> >::value && (ix<0 || ix>=_dim))
         throw std::runtime_error("sparse index out of range");
      return ix;
   }
};

template <typename Options>
class ValueInput
   : public SVHolder,
     public GenericInputImpl< ValueInput<Options> >,
     public GenericIOoptions< ValueInput<Options>, Options > {
   template <typename, typename> friend class ListValueInput;
public:
   ValueInput(SV *sv_arg)
      : SVHolder(sv_arg) {}

   template <typename Data>
   typename enable_if<void, io_test::has_std_input_operator<Data>::value>::type
   fallback(Data& x)
   {
      istream is(sv);
      is >> x;  is.finish();
   }

   template <typename Data>
   typename disable_if<void, io_test::has_std_input_operator<Data>::value>::type
   fallback(Data& x)
   {
      throw std::runtime_error(std::string("no serialization defined for type ") + typeid(Data).name());
   }

   template <typename ObjectRef>
   struct list_cursor {
      typedef typename remove_bool_param<Options,SparseRepresentation>::type
         cursor_options;
      typedef ListValueInput<typename deref<ObjectRef>::type::value_type, cursor_options>
         type;
   };

   template <typename ObjectRef>
   struct composite_cursor {
      typedef typename replace_params<
              typename remove_bool_param<Options,
                                         SparseRepresentation>::type,
                                         CheckEOF<True> >::type
         cursor_options;
      typedef ListValueInput<void, cursor_options> type;
   };

   template <typename T>
   typename list_cursor<T>::type begin_list(T*)
   {
      return sv;
   }

   template <typename T>
   typename composite_cursor<T>::type begin_composite(T*)
   {
      return sv;
   }
};

template <typename Options, bool _list_return=extract_bool_param<Options, ReturningList>::value>
class ListValueOutput
   : public ArrayHolder,
     public GenericIOoptions< ListValueOutput<Options>, Options > {
   ListValueOutput();
public:
   typedef SVHolder super;
   typedef SV* super_arg;
   static const bool stack_based=false;

   template <typename X>
   ListValueOutput& operator<< (const X& x)
   {
      Value elem;
      elem << x;
      push(elem);
      return *this;
   }

   void finish() const {}
};

template <typename Options>
class ValueOutput
   : public ListValueOutput<Options>::super,
     public GenericOutputImpl< ValueOutput<Options> >,
     public GenericIOoptions< ValueOutput<Options>, Options > {

   typedef typename ListValueOutput<Options>::super super;
   typedef typename ListValueOutput<Options>::super_arg super_arg;
public:
   template <typename Data>
   typename enable_if<void, (io_test::has_std_output_operator<Data>::value &&
                             !ListValueOutput<Options>::stack_based)>::type
   fallback(const Data& x)
   {
      ostream os(*this);
      os << x;
   }

   template <typename Data>
   typename enable_if<void, (io_test::has_std_output_operator<Data>::value &&
                             ListValueOutput<Options>::stack_based)>::type
   fallback(const Data& x)
   {
      SVHolder s;
      ostream os(s);
      os << x;
      this->push_temp(s);
   }

   template <typename Data>
   typename disable_if<void, io_test::has_std_output_operator<Data>::value>::type
   fallback(const Data& x)
   {
      throw std::runtime_error(std::string("no serialization defined for type ") + typeid(Data).name());
   }

private:
   void store_string(const char* x, size_t l, False);
   void store_string(const char* x, size_t l, True);
public:
   void
   fallback(const char* x, size_t l)
   {
      store_string(x,l,bool2type<ListValueOutput<Options>::stack_based>());
   }

   ValueOutput() {}

   explicit ValueOutput(super_arg sv_arg)
      : super(sv_arg) {}

   template <typename ObjectRef>
   struct list_cursor {
      typedef ListValueOutput<Options>& type;
   };

   template <typename ObjectRef>
   struct composite_cursor {
      typedef typename deref<ObjectRef>::type Object;
      static const int
         total  = list_length<typename object_traits<Object>::elements>::value,
         ignored= list_accumulate_unary<list_count, ignore_in_composite, typename object_traits<Object>::elements>::value;
      static const bool
         compress= ignored>0 && total-ignored<=1;
      typedef typename if_else<compress, ValueOutput, ListValueOutput<Options> >::type& type;
   };

   template <typename ObjectRef>
   struct sparse_cursor : list_cursor<ObjectRef> {};

   template <typename T>
   ListValueOutput<Options>& begin_list(const T* x)
   {
      ListValueOutput<Options>& pvl=static_cast<ListValueOutput<Options>&>(static_cast<super&>(*this));
      pvl.upgrade(x && !object_traits<T>::is_lazy && container_traits<T>::is_forward ? x->size() : 0);
      return pvl;
   }

private:
   template <typename T>
   ListValueOutput<Options>& _begin_composite(const T*, False)
   {
      ListValueOutput<Options>& pvl=static_cast<ListValueOutput<Options>&>(static_cast<super&>(*this));
      pvl.upgrade(list_length<typename object_traits<T>::elements>::value);
      return pvl;
   }

   template <typename T>
   ValueOutput& _begin_composite(const T*, True)
   {
      return *this;
   }

public:
   template <typename T>
   typename composite_cursor<T>::type
   begin_composite(const T*)
   {
      return _begin_composite((const T*)0, bool2type<composite_cursor<T>::compress>());
   }

   template <typename T>
   ListValueOutput<Options>& begin_sparse(const T* x)
   {
      return begin_list(x);
   }

   template <typename T>
   bool prefer_sparse_representation(const T&) const { return false; }
};

class Stack {
protected:
   PerlInterpreter *pi;
   Stack();
   Stack(SV **start);
private:
   // inhibited
   void operator= (const Stack&);
public:
   Stack(bool room_for_object, int reserve);

   void push(SV* x) const;
   void push(SVHolder& x) const
   {
      push(x.get());
   }
   void push_temp(SVHolder& x) const
   {
      push(x.get_temp());
   }

   void cancel();
};

class ListReturn : public Stack {
private:
   // inhibited
   void operator= (const ListReturn&);
public:
   ListReturn() {}
   ListReturn(SV **stack_arg) : Stack(stack_arg) {}

   template <typename T>
   ListReturn& operator<< (const T& x);

   void upgrade(int size);
};

class ArgList : protected ArrayHolder {
   friend class FunCall;
public:
   ArgList(int reserve=0) : ArrayHolder(reserve) {}

   template <typename T>
   ArgList& operator<< (const T& x);
};

template <typename Options>
class ListValueOutput<Options, true>
   : public ListReturn,
     public GenericIOoptions< ListValueOutput<Options, true>, Options > {
   ListValueOutput();
public:
   typedef Stack super;
   typedef SV** super_arg;
   static const bool stack_based=true;

   void finish() const {}
};

typedef ValueOutput< ReturningList<True> > ListSlurp;

} // end namespace perl

template <>
struct is_printable<perl::Value> : False {};
template <>
struct is_parseable<perl::Value> : False {};

namespace perl {

template <typename T,
          bool _has_generic=has_generic_type<T>::value>
struct generic_representative {
   typedef typename object_traits<T>::persistent_type type;
};

template <typename T>
struct generic_representative<T, true> {
   typedef typename T::generic_type::persistent_type type;
};

// primitive perl types which need special handling if returned by reference in lvalue context
typedef list primitive_lvalues(bool, int, unsigned int, long, unsigned long, double, std::string);

template <typename T, typename Serialized=typename object_traits<T>::serialized>
struct serialized_of : object_traits<T> {
   typedef Serialized type;
   static const bool obscure=false;
};

template <typename T>
struct serialized_of<T, void> {
   typedef T type;
   static T& serialize(T& x) { return x; }
   static const T& serialize(const T& x) { return x; }
   static const bool obscure=identical<typename object_traits<T>::model, is_container>::value && !has_iterator<T>::value;
};

template <typename Top>
class MaybeUndefined : public Generic<Top> {};

template <typename Target>
struct access;
template <typename Target, bool _try, bool _unwary=Unwary<typename attrib<Target>::minus_const>::value>
struct access_canned;
template <typename Target>
struct check_for_magic_storage;

class Value : public SVHolder {
public:
   explicit Value(value_flags opt_arg=value_trusted) : options(opt_arg) {}

   explicit Value(SV *sv_arg)
      : SVHolder(sv_arg), options(value_trusted) {}

   Value(SV *sv_arg, value_flags opt_arg)
      : SVHolder(sv_arg), options(opt_arg) {}

   value_flags get_flags() const { return options; }

protected:
   enum number_flags { not_a_number, number_is_zero, number_is_int, number_is_float, number_is_object };

   value_flags options;

   static const char* frame_lower_bound();

   bool is_defined() const;
   bool is_TRUE() const;
   long int_value() const;
   long enum_value() const;
   double float_value() const;
   bool is_plain_text() const;
   void check_forbidden_types() const;

   void set_perl_type(SV* proto);

   static const std::type_info* get_canned_typeinfo(SV *sv_arg);
   static char*                 get_canned_value(SV *sv_arg);

   const std::type_info* get_canned_typeinfo() const { return get_canned_typeinfo(sv); }
   char* get_canned_value() const { return get_canned_value(sv); }
   int get_canned_dim(bool tell_size_if_dense) const;

   void* allocate_canned(SV *proto) const;

   void store_canned_ref(SV *descr, void *obj, SV *anchor, value_flags flags);

   template <typename Numtype> static
   void assign_int(Numtype& x, long i, False, True) { x=i; }

   static void assign_int(long& x, long i, True, True) { x=i; }

   template <typename Numtype> static
   void assign_int(Numtype& x, long i, True, True)
   {
      if (i < std::numeric_limits<Numtype>::min() || i > std::numeric_limits<Numtype>::max())
         throw std::runtime_error("input integer property out of range");
      x=Numtype(i);
   }

   template <typename Numtype> static
   void assign_int(Numtype& x, long i, True, False)
   {
      if (i<0 || static_cast<unsigned long>(i) > std::numeric_limits<Numtype>::max())
         throw std::runtime_error("input integer property out of range");
      x=Numtype(i);
   }

   template <typename Numtype> static
   void assign_float(Numtype& x, double d, False) { x=d; }

   template <typename Numtype> static
   void assign_float(Numtype& x, double d, True)
   {
      if (d < std::numeric_limits<Numtype>::min() || d > std::numeric_limits<Numtype>::max())
         throw std::runtime_error("input integer property out of range");
      x=Numtype(lrint(d));
   }

   number_flags classify_number() const;

   template <typename Numtype>
   void num_input(Numtype& x) const
   {
      switch (classify_number()) {
      case number_is_int:
         assign_int(x, int_value(), bool2type<std::numeric_limits<Numtype>::is_bounded>(),
                                    bool2type<std::numeric_limits<Numtype>::is_signed>());
         break;
      case number_is_float:
         assign_float(x, float_value(), bool2type<std::numeric_limits<Numtype>::is_bounded>());
         break;
      case number_is_object:
         assign_int(x, Scalar::convert_to_int(sv), bool2type<std::numeric_limits<Numtype>::is_bounded>(),
                                                   bool2type<std::numeric_limits<Numtype>::is_signed>());
         break;
      case number_is_zero:
         x=0;
         break;
      default:
         throw std::runtime_error("invalid value for an input numerical property");
      }
   }

   False* retrieve(std::string& x) const;
   False* retrieve(const char* &x) const;
   False* retrieve(char &x) const;
   False* retrieve(double& x) const;
   False* retrieve(bool& x) const;

   False* retrieve(int& x) const { num_input(x); return NULL; }
   False* retrieve(unsigned int& x) const { num_input(x); return NULL; }
   False* retrieve(long& x) const { num_input(x); return NULL; }
   False* retrieve(unsigned long& x) const { num_input(x); return NULL; }

   False* retrieve(float& x) const
   {
      double xi;
      retrieve(xi);
      x=static_cast<float>(xi);
      return NULL;
   }

   False* retrieve(Array& x) const;
   False* retrieve(Object& x) const;
   False* retrieve(ObjectType& x) const;

   template <typename Options, typename Target>
   void do_parse(Target& x) const
   {
      istream my_stream(sv);
      PlainParser<Options> parser(my_stream);
      try {
         parser >> x;
         my_stream.finish();
      } catch (const std::ios::failure&) {
         throw my_stream.parse_error();
      }
   }

   // opaque type
   template <typename Target>
   void retrieve(Target& x, False) const
   {
      if (options & value_not_trusted)
         ValueInput< TrustedValue<False> >(sv) >> x;
      else
         ValueInput<>(sv) >> x;
   }

   // numeric scalar type
   template <typename Target>
   void retrieve(Target& x, True) const
   {
      this->num_input(x);
   }

   template <typename Target>
   typename enable_if<void, (check_for_magic_storage<Target>::value && is_parseable<Target>::value)>::type
   retrieve_nomagic(Target& x) const
   {
      if (is_plain_text()) {
         parse(x);
      } else {
         check_forbidden_types();
         retrieve(x, bool2type<std::numeric_limits<Target>::is_specialized>());
      }
   }

   template <typename Target>
   typename enable_if<void, (check_for_magic_storage<Target>::value && !is_parseable<Target>::value)>::type
   retrieve_nomagic(Target& x) const
   {
      retrieve(x, False());
   }

   template <typename Target>
   typename disable_if<void, check_for_magic_storage<Target>::value>::type
   retrieve_nomagic(Target& x) const
   {
      retrieve(x);
   }

   template <typename Target>
   True* retrieve(Target& x) const
   {
      if (!(options & value_ignore_magic)) {
         if (const std::type_info *t=get_canned_typeinfo()) {
            if (*t==typeid(Target)) {
               if (MaybeWary<Target>::value && (options & value_not_trusted))
                  maybe_wary(x)=*reinterpret_cast<const Target*>(get_canned_value());
               else
                  x=*reinterpret_cast<const Target*>(get_canned_value());
               return NULL;
            }
            typedef void (*ass_f)(Target&, const Value&);
            if (ass_f assignment=reinterpret_cast<ass_f>(type_cache<Target>::get_assignment_operator(sv))) {
               assignment(x,*this);
               return NULL;
            }
         }
      }
      retrieve_nomagic(x);
      return NULL;
   }

   template <typename Source>
   void store_as_perl(const Source& x)
   {
      static_cast<ValueOutput<>&>(static_cast<SVHolder&>(*this)) << x;
      set_perl_type(type_cache<typename object_traits<Source>::persistent_type>::get_proto());
   }

   template <typename Stored, typename Source>
   void store(const Source& x)
   {
      new(allocate_canned(type_cache<Stored>::get_descr())) Stored(x);
   }

   template <typename Source>
   void store_ref(const Source& x, SV* anchor)
   {
      store_canned_ref(type_cache<Source>::get_descr(), (void*)&x, anchor, options);
   }

   template <typename Source>
   void store_magic(const Source& x, False, False, False)                       // non-persistent regular type
   {
      typedef typename object_traits<Source>::persistent_type Persistent;
      if (options & value_allow_non_persistent)
         store<Source>(x);
      else
         store<Persistent>(x);
   }

   template <typename Source, typename _Masquerade, typename _Persistent>
   void store_magic(const Source& x, _Masquerade, True, _Persistent)            // lazy type
   {
      typedef typename object_traits<Source>::persistent_type Persistent;
      store<Persistent>(x);
   }

   template <typename Source, typename _Masquerade>
   void store_magic_ref(const Source& x, SV *anchor, _Masquerade, False, False)  // non-persistent regular type
   {
      typedef typename object_traits<Source>::persistent_type Persistent;
      if (options & value_allow_non_persistent)
         store_ref(x,anchor);
      else
         store<Persistent>(x);
   }

   template <typename Source, typename _Masquerade, typename _Persistent>
   void store_magic_ref(const Source& x, SV*, _Masquerade, True, _Persistent)   // lazy type
   {
      typedef typename object_traits<Source>::persistent_type Persistent;
      store<Persistent>(x);
   }

   template <typename Source>
   void store_magic(const Source& x, False, False, True)                        // persistent regular type
   {
      store<Source>(x);
   }

   template <typename Source>
   void store_magic_ref(const Source& x, SV *anchor, False, False, True)
   {
      store_ref(x,anchor);
   }

   template <typename Source>
   void store_magic(const Source& x, True, False, False)        // masquerade type belonging to a generic family
   {
      typedef typename object_traits<Source>::persistent_type Persistent;
      store<Persistent>(x);
   }

   template <typename Source>
   void store_magic(const Source& x, True, False, True) // masquerade type without persistent substitute
   {
      store_as_perl(x);
   }

   template <typename Source>
   void store_magic_ref(const Source& x, SV *anchor, True, False, True)
   {
      if (options & value_allow_non_persistent)
         store_ref(x,anchor);
      else
         store_as_perl(x);
   }

   void finalize_primitive_ref(const char *xptr, SV *descr, bool take_ref);

   void store_primitive_ref(const bool& x, SV *descr, bool take_ref);
   void store_primitive_ref(const int& x, SV *descr, bool take_ref);
   void store_primitive_ref(const unsigned int& x, SV *descr, bool take_ref);
   void store_primitive_ref(const long& x, SV *descr, bool take_ref);
   void store_primitive_ref(const unsigned long& x, SV *descr, bool take_ref);
   void store_primitive_ref(const double& x, SV *descr, bool take_ref);
   void store_primitive_ref(const std::string& x, SV *descr, bool take_ref);

   void set_string_value(const char* x);
   void set_string_value(const char* x, size_t l);
   void set_copy(const SVHolder& x);
public:
   void put(int x,            SV* =0, const char* =0, int=0) { put(long(x)); }
   void put(unsigned int x,   SV* =0, const char* =0, int=0) { put((unsigned long)(x)); }
   void put(long x,           SV* =0, const char* =0, int=0);
   void put(unsigned long x,  SV* =0, const char* =0, int=0);
   void put(bool x,           SV* =0, const char* =0, int=0);
   void put(double x,         SV* =0, const char* =0, int=0);
   void put(const undefined&, SV* =0, const char* =0, int=0);

   void put(const char* x,    SV* =0, const char* =0, int=0)
   {
      if (x)
         set_string_value(x);
      else
         put(undefined(),0,0,0);
   }

   template <size_t ll>
   void put(const char (&x)[ll], SV* =0, const char* =0, int=0)
   {
      set_string_value(x, ll-1);
   }

   void put(const std::string& x, SV* =0, const char* =0, int=0)
   {
      set_string_value(x.c_str(), x.size());
   }

   void put(char x, SV* =0, const char* =0, int=0)
   {
      set_string_value(&x, 1);
   }

   void put(const Object& x,        SV* =0, const char* =0, int=0);
   void put(const ObjectType& x,    SV* =0, const char* =0, int=0);
   void put(const PropertyValue& x, SV* =0, const char* =0, int=0);
   void put(const Scalar& x,        SV* =0, const char* =0, int=0);
   void put(const Array& x,         SV* =0, const char* =0, int=0);
   void put(const Hash& x,          SV* =0, const char* =0, int=0);
   void put(const ListReturn& x,    SV* =0, const char* =0, int=0);

   template <typename Source, typename PerlPkg>
   typename enable_if<void, serialized_of<Source>::obscure>::type
   put(const Source& x, SV *arg0, const char *fup, PerlPkg prescribed_pkg)
   {
      // obscure type (something weird, like a set complement)
      if (fup && (options & value_allow_non_persistent) &&
          type_cache<Source>::magic_allowed(prescribed_pkg))
         store_canned_ref(type_cache<Source>::get_descr(), (void*)&x, arg0, value_flags(options | value_read_only));
      else
         throw std::runtime_error("can't store an opaque C++ type without perl binding");
   }

   template <typename Source, typename PerlPkg>
   typename disable_if<void, (serialized_of<Source>::obscure ||
                              derived_from_instance<Source, MaybeUndefined>::value ||
                              is_instance3_of<Source,sparse_elem_proxy>::value ||
                              is_pointer<Source>::value)>::type
   put(const Source& x, SV *arg0, const char *fup, PerlPkg prescribed_pkg)
   {
      typedef typename object_traits<Source>::persistent_type Persistent;
      if (type_cache<Source>::magic_allowed(prescribed_pkg)) {
         if (fup && !object_traits<Source>::is_lazy) {
            const char *flo=frame_lower_bound(), *val=reinterpret_cast<const char*>(&x);
            if ((val<flo)==(val<fup)) {
               // the wrapped function has returned a reference to an object stored elsewhere
               store_magic_ref(x, arg0, is_masquerade<Source>(), bool2type<object_traits<Source>::is_lazy>(), identical<Source,Persistent>());
               return;
            }
         }
         // taking reference not allowed or just temporary object
         store_magic(x, is_masquerade<Source>(), bool2type<object_traits<Source>::is_lazy>(), identical<Source,Persistent>());
      } else {
         store_as_perl(x);
      }
   }

   template <typename Source>
   typename enable_if<void, identical<typename object_traits<typename deref<Source>::type>::model, is_opaque>::value>::type
   put(Source* ptr, SV*, const char* =0, int=0)
   {
      if ((options & value_allow_non_persistent) &&
          type_cache<Source>::magic_allowed(0)) {
         store_ref(*ptr,0);
      } else {
         throw std::runtime_error("can't store an opaque C++ type without perl binding");
      }
   }

   template <typename Source, typename PerlPkg>
   void put(const MaybeUndefined<Source>& x, SV *arg0, const char *fup, PerlPkg prescribed_pkg)
   {
      if (x.top().defined()) {
         put(*x.top(), arg0, fup, prescribed_pkg);
      } else {
         put(undefined(),0,0,0);
      }
   }

   template <typename Base, typename E, typename Params, typename PerlPkg>
   void put(const sparse_elem_proxy<Base,E,Params>& x, SV *arg0, const char*, PerlPkg prescribed_pkg)
   {
      if ((options & (value_allow_non_persistent | value_expect_lval | value_read_only)) == 
                     (value_allow_non_persistent | value_expect_lval) &&
          type_cache< sparse_elem_proxy<Base,E,Params> >::magic_allowed(prescribed_pkg)) {
         store< sparse_elem_proxy<Base,E,Params> >(x);
      } else {
         put(x.get(), arg0, 0, prescribed_pkg);
      }
   }

   template <typename Source, typename PerlPkg>
   typename disable_if<void, list_contains<primitive_lvalues,Source>::value>::type
   put_lval(const Source& x, SV *arg0, const char *fup, PerlPkg prescribed_pkg)
   {
      const std::type_info *arg0_t;
      if (arg0 && (arg0_t=get_canned_typeinfo(arg0)) && *arg0_t==typeid(Source) &&
          reinterpret_cast<const Source*>(get_canned_value(arg0)) == &x) {
         forget();
         sv=arg0;
      } else {
         put(x, arg0, fup, prescribed_pkg);
         if (arg0) get_temp();
      }
   }

   template <typename Source>
   typename enable_if<void, list_contains<primitive_lvalues,Source>::value>::type
   put_lval(const Source& x, SV *arg0, const char *fup, int)
   {
      const char *flo=frame_lower_bound(), *val=reinterpret_cast<const char*>(&x);
      store_primitive_ref(x, type_cache<Source>::get_descr(), (val<flo)==(val<fup));
      if (arg0) get_temp();
   }

   void put_lval(const Scalar& x, SV*, const char*, int);
   void put_lval(const Array& x,  SV*, const char*, int);
   void put_lval(const Hash& x,   SV*, const char*, int);

   template <typename Target>
   void parse(Target& x) const
   {
      if (options & value_not_trusted)
         do_parse< TrustedValue<False> >(x);
      else
         do_parse<void>(x);
   }

   // some code duplication with generic retrieve() is deliberate
   template <typename Target>
   operator Target () const
   {
      if (check_for_magic_storage<Target>::value) {
         if (!sv || !is_defined()) {
            if (options & value_allow_undef) return Target();
            throw undefined();
         }
         if (!(options & value_ignore_magic))
            if (const std::type_info *t=get_canned_typeinfo()) {
               if (*t==typeid(Target))
                  return get_canned<Target>();
               typedef Target (*conv_f)(const Value&);
               if (conv_f conversion=reinterpret_cast<conv_f>(type_cache<Target>::get_conversion_operator(sv)))
                  return conversion(*this);
            }
         Target x;
         retrieve_nomagic(x);
         return x;
      } else {
         Target x=Target();  // avoid 'may be used uninitialized' warning for POD types
         // may be overloaded; if not, some specialized retrieve instance will be called
         *this >> x;
         return x;
      }
   }

   operator void* () const { return is_TRUE() ? (void*)1 : 0; }
   bool operator! () const { return !is_TRUE(); }

   template <typename Target> friend
   bool operator>> (const Value& me, Target& x)
   {
      if (!me.sv || !me.is_defined()) {
         if (!(me.options & value_allow_undef))
            throw undefined();
         return false;
      }
      me.retrieve(x);
      return true;
   }

   template <typename Source> friend
   void operator<< (const Value& me, const Source& x)
   {
      const_cast<Value&>(me).put(x,0,0,0);
   }

   template <size_t ll> friend
   void operator<< (const Value& me, const char (&x)[ll])
   {
      const_cast<Value&>(me).set_string_value(x,ll-1);
   }

   template <typename Target>
   void* allocate()
   {
      return allocate_canned(type_cache<Target>::get_descr());
   }

   template <typename Target>
   typename access<Target>::return_type get() const
   {
      return access<Target>::get(*this);
   }
   using SVHolder::get;

   // CAUTION: only use after having checked for valid canned magic
   template <typename T>
   T& get_canned() const
   {
      return *reinterpret_cast<T*>(get_canned_value());
   }

   template <typename T>
   int lookup_dim(bool tell_size_if_dense)
   {
      int d=-1;
      if (is_plain_text()) {
         istream my_stream(sv);
         if (options & value_not_trusted)
            d=PlainParser< TrustedValue<False> >(my_stream).begin_list((T*)0).lookup_dim(tell_size_if_dense);
         else
            d=PlainParser<>(my_stream).begin_list((T*)0).lookup_dim(tell_size_if_dense);

      } else if (get_canned_typeinfo()) {
         d=get_canned_dim(tell_size_if_dense);

      } else {
         if (options & value_not_trusted)
            d=ListValueInput< T, TrustedValue<False> >(sv).lookup_dim(tell_size_if_dense);
         else
            d=ListValueInput<T, void>(sv).lookup_dim(tell_size_if_dense);
      }
      return d;
   }

   template <typename> friend class access;
   template <typename, bool, bool> friend class access_canned;
   template <typename> friend class check_for_magic_storage;
   friend class ArrayHolder;
   friend class OptionSet;
   friend class ListResult;
   template <typename> friend class ValueOutput;
   template <typename, typename> friend class ListValueInput;
};

inline ArrayHolder::ArrayHolder(const Value& v) : SVHolder(v.sv)
{
   if (v.options & value_not_trusted) verify();
}

template <typename Target>
struct check_for_magic_storage {
   struct helper {
      static derivation::yes Test(True*);
      static derivation::no Test(False*);
      static Target& piece();
   };
   static const bool value= sizeof(helper::Test(Value().retrieve(helper::piece()))) == sizeof(derivation::yes);
};

template <typename ElementType, typename Options>
template <typename T> inline
ListValueInput<ElementType, Options>&
ListValueInput<ElementType, Options>::operator>> (T& x)
{
   if (_opts::template get_option< CheckEOF<False> >::value && at_end())
      throw std::runtime_error("list input - size mismatch");
   Value elem((*this)[i++], _opts::template get_option< TrustedValue<True> >::value ? value_trusted : value_not_trusted );
   elem >> x;
   return *this;
}

template <typename ElementType, typename Options>
template <typename T> inline
int ListValueInput<ElementType, Options>::lookup_lower_dim(bool tell_size_if_dense)
{
   if (_size==0) return 0;
   Value first_elem((*this)[0], _opts::template get_option< TrustedValue<True> >::value ? value_trusted : value_not_trusted);
   return first_elem.lookup_dim<T>(tell_size_if_dense);
}

template <typename Options> inline
void ValueOutput<Options>::store_string(const char* x, size_t l, False)
{
   static_cast<Value*>(static_cast<super*>(this))->set_string_value(x,l);
}

template <typename Options> inline
void ValueOutput<Options>::store_string(const char* x, size_t l, True)
{
   Value v;
   v.set_string_value(x,l);
   this->push_temp(v);
}

template <typename T> inline
ListReturn& ListReturn::operator<< (const T& x)
{
   Value elem;
   elem << x;
   push_temp(elem);
   return *this;
}

template <typename T> inline
ArgList& ArgList::operator<< (const T& x)
{
   Value elem;
   elem << x;
   push(elem);
   return *this;
}

template <typename Element>
class Array_access {
protected:
   ArrayHolder ary;
public:
   Array_access(SV *sv_arg=0) : ary(sv_arg) {}

   typedef int argument_type;
   typedef Element result_type;
   Element operator() (int i) const
   {
      return Value(ary[i], identical<Element,Value>::value ? value_not_trusted : value_trusted);
   }
};

template <typename Element>
class ArrayOwner : public ArrayHolder,
                   public modified_container_impl< ArrayOwner<Element>,
                                                   list ( Container< sequence >,
                                                          Operation< Array_access<Element> > ) > {
   friend class Value;  friend class Object;
protected:
   explicit ArrayOwner(SV *sv_arg) : ArrayHolder(sv_arg) {}
public:
   ArrayOwner() {}
   explicit ArrayOwner(int n) { resize(n); }

   ArrayOwner(const ArrayOwner& x) : ArrayHolder(x.get(), True()) {}

   ArrayOwner& operator= (const ArrayOwner& x)
   {
      set_copy(x.get());
      return *this;
   }

   ~ArrayOwner() { forget(); }

   using ArrayHolder::size;
   using modified_container_impl<ArrayOwner>::operator[];

   bool empty() const { return size()==0; }

   sequence get_container() const { return sequence(0,size()); }
   Array_access<Element> get_operation() const { return sv; }

   void clear() { resize(0); }
};

class OptionSet : public HashHolder {
public:
   OptionSet(const Value& v)
      : HashHolder(v.sv) { verify(); }
public:
   template <size_t klen>
   Value operator[] (const char (&key)[klen]) const
   {
      return _access(key, klen-1, false);
   }
   Value operator[] (const std::string& key) const
   {
      return _access(key.c_str(), key.size(), false);
   }

   friend class Hash;
};

class Hash : public HashHolder {
public:
   Hash() {}
   Hash(const Hash& x) : HashHolder(x.get(), True()) {}

   Hash& operator= (const Hash& x)
   {
      set_copy(x.get());
      return *this;
   }

   ~Hash() { forget(); }

   template <size_t klen>
   Value operator[] (const char (&key)[klen]) const
   {
      return _access(key, klen-1, false);
   }
   Value operator[] (const std::string& key) const
   {
      return _access(key.c_str(), key.size(), false);
   }

   template <size_t klen>
   Value operator[] (const char (&key)[klen])
   {
      return _access(key, klen-1, true);
   }
   Value operator[] (const std::string& key)
   {
      return _access(key.c_str(), key.size(), true);
   }
};

inline void Value::put(const Scalar& x,      SV*, const char*, int) { set_copy(x); }
inline void Value::put(const Array& x,       SV*, const char*, int) { set_copy(x); }
inline void Value::put(const Hash& x,        SV*, const char*, int) { set_copy(x); }
inline void Value::put(const ListReturn& x,  SV*, const char*, int) { forget(); sv=NULL; }
inline void Value::put_lval(const Scalar& x, SV*, const char*, int) { forget(); sv=x.get(); }
inline void Value::put_lval(const Array& x,  SV*, const char*, int) { forget(); sv=x.get(); }
inline void Value::put_lval(const Hash& x,   SV*, const char*, int) { forget(); sv=x.get(); }

} }

#endif // POLYMAKE_PERL_VALUE_H

// Local Variables:
// mode:C++
// c-basic-offset:3
// indent-tabs-mode:nil
// End:
