//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
//
// 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)
//
#include "td/utils/benchmark.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"

#include <cstdio>
#include <fstream>
#include <iostream>
#include <ostream>
#include <streambuf>
#include <string>

#include <unistd.h>

std::string create_tmp_file() {
#if TD_ANDROID
  std::string name = "/data/local/tmp/large_file.txt";
  unlink(name.c_str());
  return name;
#else
  char file_name[] = "largefileXXXXXX";
  int fd = mkstemp(file_name);
  if (fd == -1) {
    perror("Can't cretate temporary file");
  }
  CHECK(fd != -1);

  close(fd);
  return file_name;
#endif
}

class IostreamWriteBench final : public td::Benchmark {
 protected:
  std::string file_name_;
  std::ofstream stream;
  static constexpr std::size_t BUFFER_SIZE = 1 << 20;
  char buffer[BUFFER_SIZE];

 public:
  std::string get_description() const final {
    return "ostream (to file, no buf, no flush)";
  }

  void start_up() final {
    file_name_ = create_tmp_file();
    stream.open(file_name_.c_str());
    CHECK(stream.is_open());
    //    stream.rdbuf()->pubsetbuf(buffer, BUFFER_SIZE);
  }

  void run(int n) final {
    for (int i = 0; i < n; i++) {
      stream << "This is just for test" << 987654321 << '\n';
    }
  }

  void tear_down() final {
    stream.close();
    unlink(file_name_.c_str());
  }
};

class FILEWriteBench final : public td::Benchmark {
 protected:
  std::string file_name_;
  FILE *file;
  static constexpr std::size_t BUFFER_SIZE = 1 << 20;
  char buffer[BUFFER_SIZE];

 public:
  std::string get_description() const final {
    return "std::fprintf (to file, no buf, no flush)";
  }

  void start_up() final {
    file_name_ = create_tmp_file();
    file = fopen(file_name_.c_str(), "w");
    //    setvbuf(file, buffer, _IOFBF, BUFFER_SIZE);
  }

  void run(int n) final {
    for (int i = 0; i < n; i++) {
      std::fprintf(file, "This is just for test%d\n", 987654321);
      //      std::fflush(file);
    }
  }

  void tear_down() final {
    std::fclose(file);
    unlink(file_name_.c_str());
  }
};

#if TD_ANDROID
#include <android/log.h>
#define ALOG(...) __android_log_print(ANDROID_LOG_VERBOSE, "XXX", __VA_ARGS__)
class ALogWriteBench final : public td::Benchmark {
 public:
  std::string get_description() const final {
    return "android_log";
  }
  void start_up() final {
  }
  void run(int n) final {
    for (int i = 0; i < n; i++) {
      ALOG("This is just for test%d\n", 987654321);
    }
  }
  void tear_down() final {
  }
};
#endif

class LogWriteBench final : public td::Benchmark {
 protected:
  std::string file_name_;
  std::ofstream stream;
  std::streambuf *old_buf;
  static constexpr std::size_t BUFFER_SIZE = 1 << 20;
  char buffer[BUFFER_SIZE];

 public:
  std::string get_description() const final {
    return "td_log (slow in debug mode)";
  }

  void start_up() final {
    file_name_ = create_tmp_file();
    stream.open(file_name_.c_str());
    CHECK(stream.is_open());
    old_buf = std::cerr.rdbuf(stream.rdbuf());
  }

  void run(int n) final {
    for (int i = 0; i < n; i++) {
      LOG(DEBUG) << "This is just for test" << 987654321;
    }
  }

  void tear_down() final {
    stream.close();
    unlink(file_name_.c_str());
    std::cerr.rdbuf(old_buf);
  }
};

int main() {
  td::bench(LogWriteBench());
#if TD_ANDROID
  td::bench(ALogWriteBench());
#endif
  td::bench(IostreamWriteBench());
  td::bench(FILEWriteBench());
}
