#ifndef VGS_PROCTOOLS_FORMATTER_H
#define VGS_PROCTOOLS_FORMATTER_H

#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>

namespace proctools
{

   class format {
      friend const std::string& str( const format& value );

   public:
      template<typename T>
      struct prec_t {
         prec_t(const T& val, int width): m_val(val), m_width(width) {}
         const T& m_val;
         int m_width;
      };

      template<typename T>
      static prec_t<T> prec(const T& val, int width);

      template<typename T>
      struct width_t {
         width_t(const T& val, int width): m_val(val), m_width(width) {}
         const T& m_val;
         int m_width;
      };

      template<typename T>
      static width_t<T> width(const T& val, int width);

      template<typename T, typename Elem>
      struct fill_t {
         fill_t(const T& val, Elem ch): m_val(val), m_ch(ch) {}
         const T& m_val;
         Elem m_ch;
      };

      template<typename T, typename Elem>
      static fill_t<T,Elem> fill(const T& val, Elem ch);

      template<typename T>
      struct base_t {
         base_t(const T& val, int base): m_val(val), m_base(base) {}
         const T& m_val;
         int m_base;
      };

      template<typename T>
      static base_t<T> base(const T& val, int base);

      format(const std::string& format): m_text(format), m_index(0) {}

      template<typename T>
      format& operator%(const T& object);

      operator const std::string&() const { return m_text;}

   private:
      std::string m_text;
      int m_index;
   };

   template<typename T>
   std::ostream& operator<<(std::ostream& s, const format::prec_t<T>& obj)
   {
      return s << std::setprecision(obj.m_width) << obj.m_val;
   }

   template<typename T>
   format::prec_t<T> format::prec(const T& val, int width)
   {
      return prec_t<T>(val, width);
   }

   template<typename T>
   std::ostream& operator<<(std::ostream& s, const format::width_t<T>& obj)
   {
      return s << std::setw(obj.m_width) << obj.m_val;
   }

   template<typename T>
   format::width_t<T> format::width(const T& val, int width)
   {
      return width_t<T>(val, width);
   }

   template<typename T, typename Elem>
   std::ostream& operator<<(std::ostream& s, const format::fill_t<T, Elem>& obj)
   {
      return s << std::setfill(obj.m_ch) << obj.m_val;
   }

   template<typename T, typename Elem>
   format::fill_t<T, Elem> format::fill(const T& val, Elem ch)
   {
      return fill_t<T, Elem>(val, ch);
   }

   template<typename T>
   std::ostream& operator<<(std::ostream& s, const format::base_t<T>& obj)
   {
      return s << std::setbase(obj.m_base) << obj.m_val;
   }

   template<typename T>
   format::base_t<T> format::base(const T& val, int base)
   {
      return base_t<T>(val, base);
   }

   template<typename T>
   format& format::operator%(const T& object)
   {
      std::stringstream stream, item;
      stream << object;
      item << "{" << m_index << "}";

      std::string::size_type pos;
      if( (pos = m_text.find(item.str())) != std::string::npos )
         m_text.replace(pos, item.str().length(), stream.str());

      ++m_index;
      return *this;
   }

   inline
   const std::string& str( const format& value )
   {
      return value.m_text;
   }

};

#endif // VGS_PROCTOOLS_FORMATTER_H
