text: // Copyright (c) 2010, Adam Petersen <adam@adampetersen.se>. All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without modification, are
  // permitted provided that the following conditions are met:
  //
  //   1. Redistributions of source code must retain the above copyright notice, this list of
  //      conditions and the following disclaimer.
  //
  //   2. Redistributions in binary form must reproduce the above copyright notice, this list
  //      of conditions and the following disclaimer in the documentation and/or other materials
  //      provided with the distribution.
  //
  // THIS SOFTWARE IS PROVIDED BY Adam Petersen ``AS IS'' AND ANY EXPRESS OR IMPLIED
  // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Adam Petersen OR
  // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  #ifndef ERL_LIST_H
  #define ERL_LIST_H
  
  #include "impl/list_matcher.h"
  #include "impl/string_matcher.h"
  #include "erl_object.h"
  #include <boost/bind.hpp>
  #include <list>
  #include <algorithm>
  #include <cassert>
  
  namespace tinch_pp {
  namespace erl {
  
  template<typename T>
  class list : public object
  {
  public:
    typedef std::list<T> list_type;
    typedef list<T> own_type;
  
    list(const list_type& contained)
      : val(contained),
        to_assign(0),
        match_fn(boost::bind(&own_type::match_value, ::_1, ::_2, ::_3))
    {
    }
  
    list(list_type* contained)
      : to_assign(contained),
        match_fn(boost::bind(&own_type::assign_matched, ::_1, ::_2, ::_3, contained))
    {
    }
  
    virtual void serialize(msg_seq_out_iter& out) const
    {
      using namespace boost;
      
      list_head_g g;
      karma::generate(out, g, val.size());
  
      std::for_each(val.begin(), val.end(), bind(&object::serialize, ::_1, boost::ref(out)));
      karma::generate(out, karma::byte_(tinch_pp::type_tag::nil_ext));
    }
  
    virtual bool match(msg_seq_iter& f, const msg_seq_iter& l) const
    {
      return match_fn(this, f, l);
    }
  
    list_type value() const 
    {
       return val;
    }
  
  private:
    static bool match_value(const own_type* self, msg_seq_iter& f, const msg_seq_iter& l)
    {
      return matcher::match(self->value(), f, l);
    }
  
    static bool assign_matched(const own_type*, msg_seq_iter& f, const msg_seq_iter& l, list_type* dest)
    {
      assert(0 != dest);
      return matcher::assign_match(dest, f, l);
    }
  
  private:
    typedef detail::list_matcher<list_type> matcher;
  
    list_type val;
    list_type* to_assign;
    
    typedef boost::function<bool (const own_type*, msg_seq_iter&, const msg_seq_iter&)> match_list_fn_type;
    match_list_fn_type match_fn;
  };
  
  template<>
  class list<erl::int_> : public object
  {
  public:
    typedef std::list<erl::int_> list_type;
    typedef list<erl::int_> own_type;
  
    list(const list_type& contained)
      : val(contained),
        to_assign(0),
        match_fn(boost::bind(&own_type::match_value, ::_1, ::_2, ::_3))
    {
    }
  
    list(list_type* contained)
      : to_assign(contained),
        match_fn(boost::bind(&own_type::assign_matched, ::_1, ::_2, ::_3, contained))
    {
    }
  
    virtual void serialize(msg_seq_out_iter& out) const
    {
      using namespace boost;
      
      list_head_g g;
      karma::generate(out, g, val.size());
  
      std::for_each(val.begin(), val.end(), bind(&erl::int_::serialize, ::_1, boost::ref(out)));
      karma::generate(out, karma::byte_(tinch_pp::type_tag::nil_ext));
    }
  
    virtual bool match(msg_seq_iter& f, const msg_seq_iter& l) const
    {
      return match_fn(this, f, l);
    }
  
    list_type value() const 
    {
       return val;
    }
  
  private:
    static bool match_value(const own_type* self, msg_seq_iter& f, const msg_seq_iter& l) 
    {
      // Erlang has an optimization for sending lists of small values (<=255), where 
      // the values are packed into a string.
      const bool is_packed_as_string = (type_tag::string_ext == *f);
  
      return is_packed_as_string ? matcher_s::match(self->value(), f, l) : matcher_l::match(self->value(), f, l);
    }
  
    static bool assign_matched(const own_type*, msg_seq_iter& f, const msg_seq_iter& l, list_type* dest)
    {
      // Erlang has an optimization for sending lists of small values (<=255), where 
      // the values are packed into a string.
      const bool is_packed_as_string = (type_tag::string_ext == *f);
  
      assert(0 != dest);
      return is_packed_as_string ? matcher_s::assign_match(dest, f, l) : matcher_l::assign_match(dest, f, l);
    }
  
  private:
    
    typedef detail::list_matcher<list_type> matcher_l;
    typedef detail::string_matcher matcher_s;
  
    list_type val;
    list_type* to_assign;
  
    typedef boost::function<bool (const own_type*, msg_seq_iter&, const msg_seq_iter&)> match_list_fn_type;
    match_list_fn_type match_fn;
  };
  
  template<typename T>
  tinch_pp::erl::list<typename T::value_type> make_list(const T& t) // T is a std::list
  {
    tinch_pp::erl::list<typename T::value_type> wrapper(t);
  
    return wrapper;
  }
  
  template<typename T>
  tinch_pp::erl::list<typename T::value_type> make_list(T* t) // T is a std::list
  {
    tinch_pp::erl::list<typename T::value_type> wrapper(t);
  
    return wrapper;
  }
  
  }
  }
  
  #endif
  