#pragma once

#include "TimeAbstraction.h"

#ifdef PLATFORM_ESP32
#include "Arduino.h"
#endif

#ifdef PLATFORM_NATIVE
#include <stdint.h>
#include <string.h>
#include <utility>
#include <stdio.h>
#endif

#define LOG_INFO(...)                        \
  {                                          \
    Logger::getInstance()->log(__VA_ARGS__); \
  }

/*#define LOG_TRACE(...)                       \
  {                                          \
    Logger::getInstance()->log(__VA_ARGS__); \
  }
*/
#define LOG_TRACE(...) {}

#define LOG_WARNING(...)                     \
  {                                          \
    Logger::getInstance()->log(__VA_ARGS__); \
  }



class Logger
{
public:
  using TimeT = unsigned long;

  ~Logger();

  template <class... Args>
  inline bool log(const char *formatStr, Args &&...args)
  {
    const TimeT time = millis();

    if (totalSize_ - currentSize_ <= sizeof(time))
      return false;

    memcpy(&data_[currentSize_], &time, sizeof(time));
    currentSize_ += sizeof(time);

    const auto spaceLeft = totalSize_ - currentSize_;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-security"
    auto charsWritten = snprintf(&data_[currentSize_], spaceLeft, formatStr,
                                 std::forward<Args>(args)...);
#pragma GCC diagnostic pop

    Serial.println(&data_[currentSize_]);
    if(charsWritten > 0)
    {
      currentSize_ += charsWritten + 1; // + 1 for trailing zero
      return true;
    }
    else
      return false;
    return true;
  }

  static Logger *getInstance();
  static void init();

  const size_t totalSize() const { return totalSize_; }
  const size_t currentSize() const { return currentSize_; }
  const char * data() const { return data_; }

  // Iteration
  class iterator
  {
    public:
      using TimeT = unsigned long;

      iterator(const char * ptr) : current_position_(ptr) {}

      TimeT time_millis() const {
        return *reinterpret_cast<const TimeT*>(current_position_);
      }

      const char * message() const {
        return current_position_ + sizeof(TimeT);
      }

      void operator++(){
        current_position_ += sizeof(TimeT) + strlen(message()) + 1;
      }

      bool operator==(const iterator & o) const { return current_position_ == o.current_position_; }
      bool operator!=(const iterator & o) const { return current_position_ != o.current_position_; }

    private:
      const char * current_position_;
  };
  iterator begin() const { return {data_}; }
  iterator end()   const { return {data_ + currentSize_}; }

private:

  Logger();

  char *data_;
  size_t totalSize_ = 0;
  size_t currentSize_ = 0;
};
