// string_adaptor.hh
// This file is part of libpbe; see http://anyterm.org/
// (C) 2007-2008 Philip Endecott

// Distributed under the Boost Software License, Version 1.0:
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
// 
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#ifndef libpbe_charset_string_adaptor_hh
#define libpbe_charset_string_adaptor_hh

#include "charset/charset_t.hh"
#include "charset/charset_traits.hh"
#include "charset/const_character_iterator.hh"

#include <boost/iterator/reverse_iterator.hpp>

#include <string>
#include <algorithm>


namespace pbe {

// String adaptor
// --------------
//
// This class adapts a std::string containing a sequence of units into a
// std::string-like object containing a sequence of characters.  It stores
// a reference to the underlying unit-string.


template <charset_t cset, typename Alloc>
class string_adaptor {

  typedef charset_traits<cset> traits_t;
  typedef typename traits_t::char_t char_t;
  typedef typename traits_t::unit_t unit_t;

public:
  typedef std::basic_string<unit_t, std::char_traits<unit_t>, Alloc> unit_string_t;

private:
  unit_string_t& ustr;

public:
  string_adaptor(unit_string_t& ustr_):
    ustr(ustr_)
  {}

  unit_string_t& unit_str() { return ustr; }
  const unit_string_t& unit_str() const { return ustr; }

  typedef char_t        value_type;
  typedef char_t*       pointer;
  typedef char_t&       reference;
  typedef const char_t& const_reference;
  typedef size_t        size_type;
  typedef ptrdiff_t     difference_type; // ??
  static const size_type npos = -1;
  // iterator ??
  typedef const_character_iterator<cset, typename unit_string_t::const_iterator> const_iterator;
  // reverse_iterator ??
  typedef boost::reverse_iterator<const_iterator> const_reverse_iterator;

  // begin
  // end
  const_iterator begin()          const { return const_iterator(ustr.begin()); }
  const_iterator end()            const { return const_iterator(ustr.end());   }
  // rbegin
  // rend
  const_reverse_iterator rbegin() const { return const_reverse_iterator(end());   }
  const_reverse_iterator rend()   const { return const_reverse_iterator(begin()); }

  // size   // ?? could do this in O(n)
  // length
  size_type max_size() const { return ustr.max_size(); } // ???
  size_type capacity() const { return ustr.capacity(); } // ???
  bool empty() const { return ustr.empty(); }
  // operator[]
  // operator[] const
  // c_str() ???
  // data() ???
  
  string_adaptor& operator=(const string_adaptor& other) { ustr=other.ustr; return *this; }
  string_adaptor& operator=(char_t c) { clear(); push_back(c); }
  void reserve(size_t sz) { ustr.reserve(sz); } // *typ_units_per_char
  void swap(string_adaptor& other) { ustr.swap(other.ustr); }

  // insert

  string_adaptor& append(const string_adaptor& s) { ustr.append(s.unit_str()); return *this; }
  string_adaptor& append(size_type n, char_t c) {
    for (size_type i=0; i<n; ++i) {
      push_back(c);
    }
  }
  template <typename InputIterator>
  string_adaptor& append(InputIterator first, InputIterator last) {
    for (; first!=last; ++first) {
      push_back(*first);
    }
  }

  void push_back(char_t c) {
    std::back_insert_iterator<unit_string_t> i(ustr);
    traits_t::encode(i,c);
  }

  string_adaptor& operator+=(const string_adaptor& s) { append(s); }
  string_adaptor& operator+=(char_t c) {  push_back(c); }

#if 0
  iterator erase(iterator p) {
    ustr.erase(p.base(), p.base()+traits_t::char_length(p.base())-1);
  }
  iterator erase(iterator first, iterator last) {
    ustr.erase(first.base(), last.base()+traits_t::char_length(last.base())-1);
  }
#endif

  void clear() { ustr.clear(); }

  // resize

  string_adaptor& assign(const string_adaptor& other) { operator=(other); }
  string_adaptor& assign(size_type n, char_t c) { clear(); append(n,c); }
  template <typename InputIterator>
  string_adaptor& assign(InputIterator first, InputIterator last) { clear(); append(first,last); }

  // replace

  // copy

  // find
  // find variants

  // substr

  int compare(const string_adaptor& s) const {
    // FIXME apparently this is an SGI thing that's not in the standard
    // return std::lexicographical_compare_3way(s.begin(), s.end(), begin(), end());
  }
  // Note that for UTF8 we can do the comparison directly on the units, which
  // should be faster.

};


template <charset_t cset, typename Alloc>
bool operator==(const string_adaptor<cset,Alloc>& s1,
                const string_adaptor<cset,Alloc>& s2) {
  return s1.ustr == s2.ustr;
}

template <charset_t cset, typename Alloc>
bool operator!=(const string_adaptor<cset,Alloc>& s1,
                const string_adaptor<cset,Alloc>& s2) {
  return s1.ustr != s2.ustr;
}

template <charset_t cset, typename Alloc>
bool operator<(const string_adaptor<cset,Alloc>& s1,
               const string_adaptor<cset,Alloc>& s2) {
  return s1.compare(s2)<0;
}

template <charset_t cset, typename Alloc>
void swap(string_adaptor<cset,Alloc>& s1,
          string_adaptor<cset,Alloc>& s2) {
  s1.swap(s2);
}


};

#endif
