#pragma once
#include "log.h"
#include <iosfwd>
#include <stdint.h>
#include <type_traits>
#include <panda/error.h>
#include <panda/excepted.h>
#if defined(_WIN32)
    #include <winsock2.h>
#endif

#define UE_NULL_TERMINATE(what, to)                          \
    auto to = (char*)alloca(sizeof(char)*(what.length()+1)); \
    memcpy(to, what.data(), what.length());                  \
    to[what.length()] = 0;

namespace panda { namespace unievent {

#if defined(_WIN32)
    using fd_t   = int;
    using sock_t = SOCKET;
    using fh_t   = HANDLE;
    using uid_t  = long;
    using gid_t  = long;
#else
    using fd_t   = int;
    using sock_t = int;
    using fh_t   = int;
#endif

enum class Ownership {
    TRANSFER = 0,
    SHARE
};

struct TimeVal {
  int64_t sec;
  int32_t usec;

  double get () const { return (double)sec + (double)usec / 1000000; }

  TimeVal& operator= (double val) {
      sec  = val;
      usec = (val - sec) * 1000000;
      return *this;
  }

  bool operator== (const TimeVal& oth) const { return sec == oth.sec && usec == oth.usec; }
  bool operator!= (const TimeVal& oth) const { return !operator==(oth); }
  bool operator>= (const TimeVal& oth) const { return sec > oth.sec || (sec == oth.sec && usec >= oth.usec); }
  bool operator>  (const TimeVal& oth) const { return sec > oth.sec || (sec == oth.sec && usec > oth.usec); }
  bool operator<= (const TimeVal& oth) const { return !operator>(oth); }
  bool operator<  (const TimeVal& oth) const { return !operator>=(oth); }
};
std::ostream& operator<< (std::ostream& os, const TimeVal&);

struct TimeSpec {
    long sec;
    long nsec;

    double get () const { return (double)sec + (double)nsec / 1000000000; }

    TimeSpec& operator= (double val) {
        sec  = val;
        nsec = (val - sec) * 1000000000;
        return *this;
    }

    bool operator== (const TimeSpec& oth) const { return sec == oth.sec && nsec == oth.nsec; }
    bool operator!= (const TimeSpec& oth) const { return !operator==(oth); }
    bool operator>= (const TimeSpec& oth) const { return sec > oth.sec || (sec == oth.sec && nsec >= oth.nsec); }
    bool operator>  (const TimeSpec& oth) const { return sec > oth.sec || (sec == oth.sec && nsec > oth.nsec); }
    bool operator<= (const TimeSpec& oth) const { return !operator>(oth); }
    bool operator<  (const TimeSpec& oth) const { return !operator>=(oth); }
};
std::ostream& operator<< (std::ostream& os, const TimeSpec&);

template <class F1, class F2>
void scope_guard (F1&& code, F2&& guard) {
    try { code(); }
    catch (...) {
        guard();
        throw;
    }
    guard();
}

template <class T>
inline excepted<T, ErrorCode> handle_fd_excepted (const expected<T, std::error_code>& r) {
    if (r.has_value()) return r.value();

    std::error_code err = r.error();
    if (err == std::errc::not_connected || err == std::errc::bad_file_descriptor || err == std::errc::invalid_argument) {
        return T();
    }

    return make_unexpected(err);
}

}}