#include "colmap/util/logging.h"

#include "pycolmap/pybind11_extension.h"

#include <filesystem>

#include <glog/logging.h>
#include <pybind11/pybind11.h>

namespace py = pybind11;

struct Logging {
  enum class LogSeverity {
    GLOG_INFO = google::GLOG_INFO,
    GLOG_WARNING = google::GLOG_WARNING,
    GLOG_ERROR = google::GLOG_ERROR,
    GLOG_FATAL = google::GLOG_FATAL,
  };
};  // dummy class

std::pair<std::string, int> GetPythonCallFrame() {
  const auto frame = py::module_::import("sys").attr("_getframe")(0);
  const std::string file = py::str(frame.attr("f_code").attr("co_filename"));
  const std::string function = py::str(frame.attr("f_code").attr("co_name"));
  const int line = py::int_(frame.attr("f_lineno"));
  return std::make_pair(file + ":" + function, line);
}

void BindLogging(py::module& m) {
  py::classh<Logging> PyLogging(m, "logging", py::module_local());

  py::enum_<Logging::LogSeverity>(PyLogging, "Level")
      .value("INFO", Logging::LogSeverity::GLOG_INFO)
      .value("WARNING", Logging::LogSeverity::GLOG_WARNING)
      .value("ERROR", Logging::LogSeverity::GLOG_ERROR)
      .value("FATAL", Logging::LogSeverity::GLOG_FATAL)
      .export_values();

  PyLogging.def_readwrite_static("minloglevel", &FLAGS_minloglevel)
      .def_readwrite_static("stderrthreshold", &FLAGS_stderrthreshold)
      .def_readwrite_static("log_dir", &FLAGS_log_dir)
      .def_readwrite_static("logtostderr", &FLAGS_logtostderr)
      .def_readwrite_static("alsologtostderr", &FLAGS_alsologtostderr)
      .def_readwrite_static("verbose_level", &FLAGS_v)
      .def_static(
          "set_log_destination",
          [](const Logging::LogSeverity severity,
             const std::filesystem::path& path) {
            google::SetLogDestination(
                static_cast<google::LogSeverity>(severity),
                path.string().c_str());
          },
          py::arg("level"),
          py::arg("path"))
      .def_static(
          "verbose",
          [](const int level, const std::string& msg) {
            if (VLOG_IS_ON(level)) {
              const auto frame = GetPythonCallFrame();
              google::LogMessage(frame.first.c_str(), frame.second).stream()
                  << msg;
            }
          },
          py::arg("level"),
          py::arg("message"))
      .def_static(
          "info",
          [](const std::string& msg) {
            const auto frame = GetPythonCallFrame();
            google::LogMessage(frame.first.c_str(), frame.second).stream()
                << msg;
          },
          py::arg("message"))
      .def_static(
          "warning",
          [](const std::string& msg) {
            const auto frame = GetPythonCallFrame();
            google::LogMessage(
                frame.first.c_str(), frame.second, google::GLOG_WARNING)
                    .stream()
                << msg;
          },
          py::arg("message"))
      .def_static(
          "error",
          [](const std::string& msg) {
            const auto frame = GetPythonCallFrame();
            google::LogMessage(
                frame.first.c_str(), frame.second, google::GLOG_ERROR)
                    .stream()
                << msg;
          },
          py::arg("message"))
      .def_static(
          "fatal",
          [](const std::string& msg) {
            const auto frame = GetPythonCallFrame();
            google::LogMessageFatal(frame.first.c_str(), frame.second).stream()
                << msg;
          },
          py::arg("message"));

#if COLMAP_GLOG_HAS_STDOUT_SUPPORT
  PyLogging.def_readwrite_static("logtostdout", &FLAGS_logtostdout)
      .def_readwrite_static("colorlogtostdout", &FLAGS_colorlogtostdout);
#endif
#if COLMAP_GLOG_HAS_COLOR_SUPPORT
  PyLogging.def_readwrite_static("colorlogtostderr", &FLAGS_colorlogtostderr);
#endif

#if defined(GLOG_VERSION_MAJOR) && \
    (GLOG_VERSION_MAJOR > 0 || GLOG_VERSION_MINOR >= 6)
  if (!google::IsGoogleLoggingInitialized())
#else
  if (!py::module_::import("sys").attr("modules").contains("pyceres"))
#endif
  {
#if !defined(COLMAP_NO_INIT_GOOGLE_LOGGING)
    google::InitGoogleLogging("");
    google::InstallFailureSignalHandler();
#endif
  }
#if !defined(COLMAP_NO_INIT_GOOGLE_LOGGING)
  FLAGS_alsologtostderr = true;
#endif
}
