#ifndef BOOST_TEXT_UTF32_ENCODING_HPP_INCLUDED
#define BOOST_TEXT_UTF32_ENCODING_HPP_INCLUDED

//
//  utf32_encoding.hpp
//
//  Copyright Anders Dalvander 2011.
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/text/codepoint.hpp>

namespace boost
{
   namespace utf32
   {
      typedef uint32_t codeunit_type;

      class invalid_codeunit : public std::exception
      {
      public:
         explicit invalid_codeunit(codeunit_type cu)
            : cu(cu)
         {
         }

         virtual const char* what() const throw()
         {
            return "Invalid UTF-32 code unit";
         }

         codeunit_type code_unit() const
         {
            return cu;
         }

      private:
         codeunit_type cu;
      }; // class invalid_codeunit

      class leading_codeunit
      {
      public:
         leading_codeunit(uint32_t cu)
            : cu(cu)
         {
            if (cu >= 0xd800u && cu < 0xe000u || cu >= 0xfdd0u && cu < 0xfdf0u || (cu & 0xffffu) >= 0xfffeu || cu >= 0x110000u)
               throw invalid_codeunit(cu);
         }

         operator uint32_t() const
         {
            return cu;
         }

      private:
         uint32_t cu;
      }; // class leading_codeunit

      class encoding
      {
      public:
         typedef codepoint codepoint_type;
         typedef utf32::codeunit_type codeunit_type;
         typedef utf32::leading_codeunit leading_codeunit_type;

         template <typename codeunit_iterator>
         class decode_iterator : public std::iterator<std::forward_iterator_tag, codepoint_type>
         {
         public:
            decode_iterator()
               : current()
               , end()
            {
            }

            decode_iterator(const codeunit_iterator& current, const codeunit_iterator& end)
               : current(current)
               , end(end)
            {
            }

            codepoint_type operator*() const
            {
               const leading_codeunit_type cu = static_cast<uint32_t>(*current);
               const codepoint_type cp = static_cast<uint32_t>(cu);
               return cp;
            }

            bool operator==(const decode_iterator& rhs) const
            {
               return current == rhs.current;
            }

            bool operator!=(const decode_iterator& rhs) const
            {
               return !operator==(rhs);
            }

            decode_iterator& operator++()
            {
               if (current == end)
                  throw incomplete_sequence();
               ++current;
               return *this;
            }

            decode_iterator operator++(int)
            {
               decode_iterator temp = *this;
               ++(*this);
               return temp;
            }

         private:
            codeunit_iterator current;
            codeunit_iterator end;
         }; // class decode_iterator

         template <typename codepoint_iterator>
         class encode_iterator : public std::iterator<std::forward_iterator_tag, codeunit_type>
         {
         public:
            encode_iterator()
               : current()
               , end()
            {
            }

            encode_iterator(const codepoint_iterator& current, const codepoint_iterator& end)
               : current(current)
               , end(end)
            {
            }

            codeunit_type operator*() const
            {
               const codepoint_type cp = *current;
               return static_cast<uint32_t>(cp);
            }

            bool operator==(const encode_iterator& rhs) const
            {
               return current == rhs.current;
            }

            bool operator!=(const encode_iterator& rhs) const
            {
               return !operator==(rhs);
            }

            encode_iterator& operator++()
            {
               if (current == end)
                  throw incomplete_sequence();
               ++current;
               return *this;
            }

            encode_iterator operator++(int)
            {
               encode_iterator temp = *this;
               ++(*this);
               return temp;
            }

         private:
            codepoint_iterator current;
            codepoint_iterator end;
         }; // class encode_iterator
      }; // class encoding
   } // namespace utf32
}

#endif  // #ifndef BOOST_TEXT_UTF32_ENCODING_HPP_INCLUDED
