/*
 Formatting library for C++ - std::ostream support

 Copyright (c) 2012 - 2016, Victor Zverovich
 All rights reserved.

 For the license information refer to format.h.
 */

#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_

#include "format.h"
#include <ostream>

namespace fmt {

namespace internal {

template <class Char>
class FormatBuf : public std::basic_streambuf<Char> {
 private:
  typedef typename std::basic_streambuf<Char>::int_type int_type;
  typedef typename std::basic_streambuf<Char>::traits_type traits_type;

  Buffer<Char> &buffer_;
  Char *start_;

 public:
  FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) {
    this->setp(start_, start_ + buffer_.capacity());
  }

  FormatBuf(Buffer<Char> &buffer, Char *start) : buffer_(buffer) , start_(start) {
    this->setp(start_, start_ + buffer_.capacity());
  }

  int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
    if (!traits_type::eq_int_type(ch, traits_type::eof())) {
      size_t buf_size = size();
      buffer_.resize(buf_size);
      buffer_.reserve(buf_size * 2);

      start_ = &buffer_[0];
      start_[buf_size] = traits_type::to_char_type(ch);
      this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
    }
    return ch;
  }

  size_t size() const {
    return to_unsigned(this->pptr() - start_);
  }
};

Yes &convert(std::ostream &);

struct DummyStream : std::ostream {
  DummyStream();  // Suppress a bogus warning in MSVC.
  // Hide all operator<< overloads from std::ostream.
  void operator<<(Null<>);
};

No &operator<<(std::ostream &, int);

template<typename T>
struct ConvertToIntImpl<T, true> {
  // Convert to int only if T doesn't have an overloaded operator<<.
  enum {
    value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
  };
};

// Write the content of w to os.
void write(std::ostream &os, Writer &w);

#if FMT_HAS_DECLTYPE_INCOMPLETE_RETURN_TYPES
template<typename T>
class is_streamable {
  template<typename U>
  static auto test(int) -> decltype(std::declval<std::ostream &>() << std::declval<U>(), std::true_type());

  template<typename>
  static auto test(...) -> std::false_type;

public:
  static constexpr bool value = decltype(test<T>(0))::value;
};
#endif
}  // namespace internal

// Formats a value.
template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
                const Char *&format_str, const T &value) {
  internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;

  internal::FormatBuf<Char> format_buf(buffer);
  std::basic_ostream<Char> output(&format_buf);
  output << value;

  BasicStringRef<Char> str(&buffer[0], format_buf.size());
  typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
  format_str = f.format(format_str, MakeArg(str));
}

/**
  \rst
  Prints formatted data to the stream *os*.

  **Example**::

    print(cerr, "Don't {}!", "panic");
  \endrst
 */
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)

#if __cplusplus >= 201103L
template<typename T, typename Char>
typename std::enable_if<
 !std::is_same<
   typename std::remove_cv<typename std::decay<T>::type>::type,
   char *
 >::value,
 BasicWriter<Char>&
>::type
operator<<(BasicWriter<Char> &writer, const T &value) {
  FMT_STATIC_ASSERT(internal::is_streamable<T>::value, "T must be Streamable");

  auto &buffer = writer.buffer();
  Char *start = &buffer[0] + buffer.size();

  internal::FormatBuf<Char> format_buf(buffer, start);
  std::basic_ostream<Char> output(&format_buf);
  output << value;

  buffer.resize(buffer.size() + format_buf.size());
  return writer;
}
#endif
}  // namespace fmt

#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#endif

#endif  // FMT_OSTREAM_H_