//  boost/chrono/utility/ios_base_pword_ptr.hpp  ------------------------------------------------------------//

//  Copyright 2011 Vicente J. Botet Escriba

//  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)

//  See http://www.boost.org/libs/chrono for documentation.

#ifndef BOOST_CHRONO_UTILITY_IOS_BASE_STATE_PTR_HPP
#define BOOST_CHRONO_UTILITY_IOS_BASE_STATE_PTR_HPP

#include <ios>
#include <b/assert.hpp>

/**
 *


 */
namespace boost
{
  namespace chrono
  {
    namespace detail
    {

      /**
       * xalloc key holder.
       */
      template <typename T>
      struct xalloc_key_holder
      {
        static int value; //< the xalloc value associated to T.
        static bool initialized; //< whether the value has been initialized or not.
      };

      template <typename T>
      int xalloc_key_holder<T>::value = 0;

      template <typename T>
      bool xalloc_key_holder<T>::initialized = false;

    }

    /**
     * xalloc key initialiazer.
     *
     * Declare a static variable of this type to ensure that the xalloc_key_holder<T> is initialized correctly.
     */
    template <typename T>
    struct xalloc_key_initializer
    {
      xalloc_key_initializer()
      {
        if (!detail::xalloc_key_holder<T>::initialized)
        {
          detail::xalloc_key_holder<T>::value = std::ios_base::xalloc();
          detail::xalloc_key_holder<T>::initialized = true;
        }
      }
    };
    /**
     * @c ios_state_ptr is a smart pointer to a ios_base specific state.
     */
    template <typename Final, typename T>
    class ios_state_ptr
    {
      ios_state_ptr& operator=(ios_state_ptr const& rhs) ;

    public:
      /**
       * The pointee type
       */
      typedef T element_type;
      /**
       * Explicit constructor.
       * @param ios the ios
       * @Effects Constructs a @c ios_state_ptr by storing the associated @c ios.
       */
      explicit ios_state_ptr(std::ios_base& ios) :
        ios_(ios)
      {

      }
      /**
       * Nothing to do as xalloc index can not be removed.
       */
      ~ios_state_ptr()
      {
      }

      /**
       * @Effects Allocates the index if not already done.
       * Registers the callback responsible of maintaining the state pointer coherency, if not already done.
       * Retrieves the associated ios pointer
       * @return the retrieved pointer statically casted to const.
       */
      T const* get() const BOOST_NOEXCEPT
      {
        register_once(index(), ios_);
        void* &pw = ios_.pword(index());
        if (pw == 0)
        {
          return 0;
        }
        return static_cast<const T*> (pw);
      }
      /**
       * @Effects Allocates the index if not already done.
       * Registers the callback responsible of maintaining the state pointer coherency, if not already done.
       * Retrieves the associated ios pointer
       * @return the retrieved pointer.
       */
      T * get() BOOST_NOEXCEPT
      {
        register_once(index(), ios_);
        void* &pw = ios_.pword(index());
        if (pw == BOOST_NULLPTR)
        {
          return BOOST_NULLPTR;
        }
        return static_cast<T*> (pw);
      }
      /**
       * @Effects as if @c return get();
       * @return the retrieved pointer.
       */
      T * operator->()BOOST_NOEXCEPT
      {
        return get();
      }
      /**
       * @Effects as if @c return get();
       * @return the retrieved pointer.
       */
      T const * operator->() const BOOST_NOEXCEPT
      {
        return get();
      }

      /**
       * @Effects as if @c return *get();
       * @return a reference to the retrieved state.
       * @Remark The behavior is undefined if @c get()==0.
       */
      T & operator*() BOOST_NOEXCEPT
      {
        return *get();
      }
      /**
       * @Effects as if @c return *get();
       * @return a reference to the retrieved state.
       * @Remark The behavior is undefined if @c get()==0.
       */
      T const & operator *() const BOOST_NOEXCEPT
      {
        return *get();
      }

      /**
       * @Effects reset the current pointer after storing in a temporary variable the pointer to the current state.
       * @return the stored state pointer.
       */
      T * release() BOOST_NOEXCEPT
      {
        void*& pw = ios_.pword(index());
        T* ptr = static_cast<T*> (pw);
        pw = 0;
        return ptr;
      }

      /**
       *
       * @param new_ptr the new pointer.
       * @Effects deletes the current state and replace it with the new one.
       */
      void reset(T* new_ptr = 0)BOOST_NOEXCEPT
      {
        register_once(index(), ios_);
        void*& pw = ios_.pword(index());
        delete static_cast<T*> (pw);
        pw = new_ptr;
      }

#if defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS)
        typedef T* (ios_state_ptr::*bool_type)();
        operator bool_type() const BOOST_NOEXCEPT
        {
            return (get()!=0)?&ios_state_ptr::release:0;
        }
        bool operator!() const BOOST_NOEXCEPT
        {
          return (get()==0)?&ios_state_ptr::release:0;
        }
#else
        /**
         * Explicit conversion to bool.
         */
        explicit operator bool() const BOOST_NOEXCEPT
        {
          return get()!=0;
        }
#endif

      std::ios_base& getios()BOOST_NOEXCEPT
      {
        return ios_;
      }
      std::ios_base& getios() const BOOST_NOEXCEPT
      {
        return ios_;
      }
      /**
       * Implicit conversion to the ios_base
       */
      operator std::ios_base&() BOOST_NOEXCEPT
      {
        return ios_;
      }
      /**
       * Implicit conversion to the ios_base const
       */
      operator std::ios_base&() const BOOST_NOEXCEPT
      {
        return ios_;
      }
    private:
      static inline bool is_registerd(std::ios_base& ios)
      {
        long iw = ios.iword(index());
        return (iw == 1);
      }
      static inline void set_registered(std::ios_base& ios)
      {
        long& iw = ios.iword(index());
        iw = 1;
      }
      static inline void callback(std::ios_base::event evt, std::ios_base& ios, int index)
      {
        switch (evt)
        {
        case std::ios_base::erase_event:
        {
          void*& pw = ios.pword(index);
          if (pw != BOOST_NULLPTR)
          {
            T* ptr = static_cast<T*> (pw);
            delete ptr;
            pw = BOOST_NULLPTR;
          }
          break;
        }
        case std::ios_base::copyfmt_event:
        {
          void*& pw = ios.pword(index);
          if (pw != BOOST_NULLPTR)
          {
            pw = new T(*static_cast<T*> (pw));
          }
          break;
        }
        default:
          break;
        }
      }

      static inline int index()
      {
        return detail::xalloc_key_holder<Final>::value;
      }

      static inline void register_once(int indx, std::ios_base& ios)
      {
        // needs a mask registered
        if (!is_registerd(ios))
        {
          set_registered(ios);
          ios.register_callback(callback, indx);
        }
      }


    protected:
      std::ios_base& ios_;
      //static detail::xalloc_key_initializer<Final> xalloc_key_initializer_;

    };
    //template <typename Final, typename T>
    //detail::xalloc_key_initializer<Final> ios_state_ptr<Final,T>::xalloc_key_initializer_;


    /**
     * @c ios_state_not_null_ptr is a non null variant of @c ios_state_ptr.
     * @tparm T
     * @Requires @c T must be @c DefaultConstructible and @c HeapAllocatable
     */
    template <typename Final, typename T>
    class ios_state_not_null_ptr: public ios_state_ptr<Final, T>
    {
      typedef ios_state_ptr<Final, T> base_type;
    public:
      explicit ios_state_not_null_ptr(std::ios_base& ios) :
      base_type(ios)
      {
        if (this->get() == BOOST_NULLPTR)
        {
          this->base_type::reset(new T());
        }
      }
      ~ios_state_not_null_ptr()
      {
      }

      void reset(T* new_value) BOOST_NOEXCEPT
      {
        BOOST_ASSERT(new_value!=BOOST_NULLPTR);
        this->base_type::reset(new_value);
      }

    };

    /**
     * This class is useful to associate some flags to an std::ios_base.
     */
    template <typename Final>
    class ios_flags
    {
    public:
      /**
       *
       * @param ios the associated std::ios_base.
       * @Postcondition <c>flags()==0</c>
       */
      explicit ios_flags(std::ios_base& ios) :
        ios_(ios)
      {
      }
      ~ios_flags()
      {
      }
      /**
       * @Returns The format control information.
       */
      long flags() const BOOST_NOEXCEPT
      {
        return value();
      }

      /**
       * @param v the new bit mask.
       * @Postcondition <c>v == flags()</c>.
       * @Returns The previous value of @c flags().
       */
      long flags(long v)BOOST_NOEXCEPT
      {
        long tmp = flags();
        ref() = v;
        return tmp;
      }

      /**
       * @param v the new value
       * @Effects: Sets @c v in @c flags().
       * @Returns: The previous value of @c flags().
       */
      long setf(long v)
      {
        long tmp = value();
        ref() |= v;
        return tmp;
      }

      /**
       * @param mask the bit mask to clear.
       * @Effects: Clears @c mask in @c flags().
       */
      void unsetf(long mask)
      {
        ref() &= ~mask;
      }

      /**
       *
       * @param v
       * @param mask
       * @Effects: Clears @c mask in @c flags(), sets <c>v & mask</c> in @c flags().
       * @Returns: The previous value of flags().
       */
      long setf(long v, long mask)
      {
        long tmp = value();
        unsetf(mask);
        ref() |= v & mask;
        return tmp;
      }

      /**
       * implicit conversion to the @c ios_base
       */
      operator std::ios_base&()BOOST_NOEXCEPT
      {
        return ios_;
      }
      /**
       * implicit conversion to the @c ios_base const
       */
      operator std::ios_base const&() const BOOST_NOEXCEPT
      {
        return ios_;
      }
    private:
      long value() const BOOST_NOEXCEPT
      {
        return ios_.iword(index());
      }
      long& ref()BOOST_NOEXCEPT
      {
        return ios_.iword(index());
      }
      static inline int index()
      {
        return detail::xalloc_key_holder<Final>::value;
      }
      ios_flags& operator=(ios_flags const& rhs) ;

      std::ios_base& ios_;
      //static detail::xalloc_key_initializer<Final> xalloc_key_initializer_;

    };
    //template <typename Final>
    //detail::xalloc_key_initializer<Final> ios_flags<Final>::xalloc_key_initializer_;

  } // namespace chrono
} // namespace boost

#endif // header
