/*
  Streaming.h - Arduino library for supporting the << streaming operator
  Copyright (c) 2010-2012 Mikal Hart.  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

/*
  Version 6 library changes
    Copyright (c) 2019 Gazoodle. All rights reserved.

  1.  _BASED moved to template to remove type conversion to long and
      sign changes which break int8_t and int16_t negative numbers.
      The print implementation still upscales to long for it's internal
      print routine.

  2.  _PAD added to allow padding & filling of characters to the stream

  3.  _WIDTH & _WIDTHZ added to allow width printing with space padding
      and zero padding for numerics

  4.  Simple _FMT mechanism ala printf, but without the typeunsafetyness
      and no internal buffers for replaceable stream printing
*/

#ifndef ARDUINO_STREAMING
#define ARDUINO_STREAMING

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#ifndef STREAMING_CONSOLE
#include "WProgram.h"
#endif
#endif

// Do not use stl library, implement trivial version of std::is_signed
namespace streaming_std {
template<typename T>
  struct is_signed { static const bool value = false; };
  template<>
  struct is_signed<int8_t> { static const bool value = true; };
  template<>
  struct is_signed<int16_t> { static const bool value = true; };
  template<>
  struct is_signed<int32_t> { static const bool value = true; };
  template<>
  struct is_signed<int64_t> { static const bool value = true; };
  template<>
  struct is_signed<float> { static const bool value = true; };
  template<>
  struct is_signed<double> { static const bool value = true; };
};

#define STREAMING_LIBRARY_VERSION 6

#if !defined(typeof)
#define typeof(x) __typeof__(x)
#endif

#define STREAMING_MIN(a,b) ((a)<(b)?(a):(b))

// PrintBuffer implementation of Print, a small buffer to print in
// see its use with pad_float()
template <size_t N>
class PrintBuffer : public Print
{
  size_t  pos = 0;
  char    str[N] {};
public:
  inline const char *operator() ()
  { return str; };

  // inline void clear()
  // { pos = 0; str[0] = '\0'; };

  inline size_t write(uint8_t c)
  { return write(&c, 1); };

  inline size_t write(const uint8_t *buffer, size_t size)
  {
    size_t s = STREAMING_MIN(size, N-1 - pos); // need a /0 left
    if (s)
    {
      memcpy(&str[pos], buffer, s);
      pos += s;
    }
    return s;
  };
};

// Generic template
template<class T>
inline Print &operator <<(Print &strm, const T &arg)
{ strm.print(arg); return strm; }

template<typename T>
struct _BASED
{
  T val;
  int base;
  _BASED(T v, int b): val(v), base(b)
  {}
};

#if ARDUINO >= 100

struct _BYTE_CODE
{
  byte val;
  _BYTE_CODE(byte v) : val(v)
  {}
};
#define _BYTE(a)    _BYTE_CODE(a)

inline Print &operator <<(Print &obj, const _BYTE_CODE &arg)
{ obj.write(arg.val); return obj; }

#else

#define _BYTE(a)    _BASED<typeof(a)>(a, BYTE)

#endif

#define _HEX(a)     _BASED<typeof(a)>(a, HEX)
#define _DEC(a)     _BASED<typeof(a)>(a, DEC)
#define _OCT(a)     _BASED<typeof(a)>(a, OCT)
#define _BIN(a)     _BASED<typeof(a)>(a, BIN)

// Specialization for class _BASED
// Thanks to Arduino forum user Ben Combee who suggested this
// clever technique to allow for expressions like
//   Serial << _HEX(a);

template<typename T>
inline Print &operator <<(Print &obj, const _BASED<T> &arg)
{ obj.print(arg.val, arg.base); return obj; }

#if ARDUINO >= 18
// Specialization for class _FLOAT
// Thanks to Michael Margolis for suggesting a way
// to accommodate Arduino 0018's floating point precision
// feature like this:
//   Serial << _FLOAT(gps_latitude, 6); // 6 digits of precision

struct _FLOAT
{
  double val; // only Print::print(double)
  int digits;
  _FLOAT(double v, int d): val(v), digits(d)
  {}
};

inline Print &operator <<(Print &obj, const _FLOAT &arg)
{ obj.print(arg.val, arg.digits); return obj; }
#endif

// Specialization for enum _EndLineCode
// Thanks to Arduino forum user Paul V. who suggested this
// clever technique to allow for expressions like
//   Serial << "Hello!" << endl;

enum _EndLineCode { endl };

inline Print &operator <<(Print &obj, _EndLineCode)
{ obj.println(); return obj; }

// Specialization for padding & filling, mainly utilized
// by the width printers
//
//  Use like
//    Serial << _PAD(10,' ');   // Will output 10 spaces
//    Serial << _PAD(4, '0');   // Will output 4 zeros
struct _PAD
{
  int8_t width;
  char   chr;
  _PAD(int8_t w, char c) : width(w), chr(c) {}
};

inline Print &operator <<(Print& strm, const _PAD &arg)
{
  for(int8_t i = 0; i < arg.width; i++)
    strm.print(arg.chr);
  return strm;
}

// Specialization for width printing
//
//    Use like                            Result
//    --------                            ------
//    Serial << _WIDTH(1,5)               "    1"
//    Serial << _WIDTH(10,5)              "   10"
//    Serial << _WIDTH(100,5)             "  100"
//    Serial << _WIDTHZ(1,5)              "00001"
//
//  Great for times & dates, or hex dumps
//
//    Serial << _WIDTHZ(hour,2) << ':' << _WIDTHZ(min,2) << ':' << _WIDTHZ(sec,2)
//
//    for(int index=0; index<byte_array_size; index++)
//      Serial << _WIDTHZ(_HEX(byte_array[index]))

template<typename T>
struct __WIDTH
{
  const T val;
  int8_t width;
  char pad;
  __WIDTH(const T& v, int8_t w, char p) : val(v), width(w), pad(p) {}
};

//  Count digits in an integer of specific base
template<typename T>
inline uint8_t digits(T v, int8_t base = 10)
{
  uint8_t digits = 0;
  if ( streaming_std::is_signed<T>::value )
  {
    if ( v < 0 )
    {
      digits++;
      v = -v; // v needs to be postive for the digits counter to work
    }
  }
  do
  {
    v /= base;
    digits++;
  } while( v > 0 );
  return digits;
}

// Generic get the width of a value in base 10
template<typename T>
inline uint8_t get_value_width(T val)
{ return digits(val); }

inline uint8_t get_value_width(const char * val)
{ return strlen(val); }

#ifdef ARDUINO
inline uint8_t get_value_width(const __FlashStringHelper * val)
{ return strlen_P(reinterpret_cast<const char *>(val)); }
#endif

// _BASED<T> get the width of a value
template<typename T>
inline uint8_t get_value_width(_BASED<T> b)
{ return digits(b.val, b.base); }

// Constructor wrapper to allow automatic template parameter deduction
template<typename T>
__WIDTH<T> _WIDTH(T val, int8_t width) { return __WIDTH<T>(val, width, ' '); }
template<typename T>
__WIDTH<T> _WIDTHZ(T val, int8_t width) { return __WIDTH<T>(val, width, '0'); }


// Operator overload to handle width printing.
template<typename T>
inline Print &operator <<(Print &strm, const __WIDTH<T> &arg)
{ strm << _PAD(arg.width - get_value_width(arg.val), arg.pad) << arg.val; return strm; }

// explicit Operator overload to handle width printing of _FLOAT, double and float
template<typename T>
inline Print &pad_float(Print &strm, const __WIDTH<T> &arg, const double val, const int digits = 2) // see Print::print(double, int = 2)
{
  PrintBuffer<32> buf; // it's only ~45B on the stack, no allocation, leak or fragmentation
  size_t size = buf.print(val, digits); // print in buf
  return strm << _PAD(arg.width - size, arg.pad) << buf(); // pad and concat what's in buf
}

inline Print &operator <<(Print &strm, const __WIDTH<float>  &arg)
{ return pad_float(strm, arg, arg.val); }

inline Print &operator <<(Print &strm, const __WIDTH<double> &arg)
{ return pad_float(strm, arg, arg.val); }

inline Print &operator <<(Print &strm, const __WIDTH<_FLOAT> &arg)
{ auto& f = arg.val; return pad_float(strm, arg, f.val, f.digits); }

// a less verbose _FLOATW for _WIDTH(_FLOAT)
#define _FLOATW(val, digits, width) _WIDTH<_FLOAT>(_FLOAT((val), (digits)), (width))

// Specialization for replacement formatting
//
//  Designed to be similar to printf that everyone knows and loves/hates. But without
//  the internal buffers and type agnosticism. This version only has placeholders in
//  the format string, the actual values are supplied using the stream safe operators
//  defined in this library.
//
//  Use like this:
//
//      Serial << FMT(F("Replace % with %"), 1, 2 )
//      Serial << FMT("Time is %:%:%", _WIDTHZ(hours,2), _WIDTHZ(minutes,2), _WIDTHZ(seconds,2))
//      Serial << FMT("Your score is %\\%", score);   // Note the \\ to escape the % sign

// Ok, hold your hats. This is a foray into C++11's variadic template engine ...

inline char get_next_format_char(const char *& format_string)
{
  char format_char = *format_string;
  if ( format_char > 0 ) format_string++;
  return format_char;
}

#ifdef ARDUINO
inline char get_next_format_char(const __FlashStringHelper*& format_string)
{
  char format_char = pgm_read_byte(format_string);
  if ( format_char > 0 ) format_string = reinterpret_cast<const __FlashStringHelper*>(reinterpret_cast<const char *>(format_string)+1);
  return format_char;
}
#endif

template<typename Ft>
inline bool check_backslash(char& format_char, Ft& format_string)
{
  if ( format_char == '\\')
  {
    format_char = get_next_format_char(format_string);
    return true;
  }
  return false;
}

// The template tail printer helper
template<typename Ft, typename... Ts>
struct __FMT
{
  Ft format_string;
  __FMT(Ft f, Ts ... args) : format_string(f) {}
  inline void tstreamf(Print& strm, Ft format) const
  {
    while(char c = get_next_format_char(format))
    {
      check_backslash(c, format);
      if ( c )
        strm.print(c);
    }
  }
};

// The variadic template helper
template<typename Ft, typename T, typename... Ts>
struct __FMT<Ft, T, Ts...> : __FMT<Ft, Ts...>
{
  T val;
  __FMT(Ft f, T t, Ts... ts) : __FMT<Ft, Ts...>(f, ts...), val(t) {}
  inline void tstreamf(Print& strm, Ft format) const
  {
    while(char c = get_next_format_char(format))
    {
      if (!check_backslash(c, format))
      {
        if ( c == '%')
        {
          strm << val;
          // Variadic recursion ... compiler rolls this out during
          // template argument pack expansion
          __FMT<Ft, Ts...>::tstreamf(strm, format);
          return;
        }
      }
      if (c)
        strm.print(c);
    }
  }
};

// The actual operator should you only instanciate the FMT
// helper with a format string and no parameters
template<typename Ft, typename... Ts>
inline Print& operator <<(Print &strm, const __FMT<Ft, Ts...> &args)
{
    args.tstreamf(strm, args.format_string);
    return strm;
}

// The variadic stream helper
template<typename Ft, typename T, typename... Ts>
inline Print& operator <<(Print &strm, const __FMT<Ft, T, Ts...> &args)
{
    args.tstreamf(strm, args.format_string);
    return strm;
}

// As we don't have C++17, we can't get a constructor to use
// automatic argument deduction, but ... this little trick gets
// around that ...
template<typename Ft, typename... Ts>
__FMT<Ft, Ts...> _FMT(Ft format, Ts ... args) { return __FMT<Ft, Ts...>(format, args...); }

// make it easier to print multiple variables one after the other using a comma separator
template<typename T>
inline Print &operator ,(Print &strm, const T arg)
{
  strm.print(" ");
  strm.print(arg);
  return strm;
}

#endif
