/*
Copyright 2022 The Photon Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#pragma once
#include <cstdint>
#include <cstddef>
#include <type_traits>
#include <assert.h>
#include <utility>
#include "string_view.h"
// #include <string>

#define _unused(x) ((void)(x))

template<typename...Ts> inline
void __unused__(const Ts&...) { }

// usage: auto obj = NewObj<Xxx>(a,b,c) -> init(d,e,f);
template<typename T>
class NewObj
{
    T* _obj;
public:
    template<typename...Ts>
    NewObj(Ts&&... xs)
    {
        _obj = new T(std::forward<Ts>(xs)...);
    }

    template<typename...Ts>
    T* init(Ts&&...xs) const
    {
        auto err = _obj->init(std::forward<Ts>(xs)...);
        if (!err) return _obj;
        delete _obj;
        return nullptr;
    }

    const NewObj<T>* operator->() const
    {
        return this;
    }

    NewObj<T>* operator->()
    {
        return this;
    }
};

template<typename T>
struct ptr_array_t
{
    T* pbegin;
    T* pend;
    T* begin() { return pbegin; }
    T* end()   { return pend; }
    T& front() { return pbegin[0]; }
    T& back()  { return pend[-1]; }
};

template<typename T>
ptr_array_t<T> ptr_array(T* pbegin, size_t n)
{
    return {pbegin, pbegin + n};
}

#define __INLINE__ __attribute__((always_inline))
#define __FORCE_INLINE__ __INLINE__ inline

template<typename T>
class Defer
{
public:
    Defer(T fn) __INLINE__ : m_func(fn) {}
    ~Defer() __INLINE__ { m_func(); }
    void operator=(const Defer<T>&) = delete;
    operator bool () { return true; }   // if (DEFER(...)) { ... }

private:
    T m_func;
};

template<typename T> __FORCE_INLINE__
Defer<T> make_defer(T func) { return Defer<T>(func); }

#define _CONCAT_(a, b) a##b
#define _CONCAT(a, b) _CONCAT_(a, b)

#define DEFER(func) auto _CONCAT(__defer__, __LINE__) = \
    make_defer([&]() __INLINE__ { func; })



template<typename T, size_t n>
constexpr size_t LEN(T (&x)[n]) { return n; }


template<typename T>
struct is_function_pointer
{
    static const bool value = std::is_pointer<T>::value &&
        std::is_function<typename std::remove_pointer<T>::type>::value;
};

#define ENABLE_IF(COND) typename _CONCAT(__x__, __LINE__) = typename std::enable_if<COND>::type
#define IS_SAME(T, P)  (std::is_same<typename std::remove_cv<T>::type, P>::value)
#define ENABLE_IF_SAME(T, P)        ENABLE_IF(IS_SAME(T, P))
#define ENABLE_IF_NOT_SAME(T, P)    ENABLE_IF(!IS_SAME(T, P))
#define IS_BASE_OF(A, B) (std::is_base_of<A, B>::value)
#define ENABLE_IF_BASE_OF(A, B)     ENABLE_IF(IS_BASE_OF(A, B))
#define ENABLE_IF_NOT_BASE_OF(A, B) ENABLE_IF(!IS_BASE_OF(A, B))
#define IS_POINTER(T)  (std::is_pointer<T>::value)
#define ENABLE_IF_POINTER(T)        ENABLE_IF(IS_POINTER(T))
#define ENABLE_IF_NOT_POINTER(T)    ENABLE_IF(!IS_POINTER(T))


inline constexpr bool is_power_of_2(uint64_t x) {
    return !x || __builtin_popcountl(x) == 1;
}

inline constexpr uint8_t log2_truncate(size_t x) {
    assert(x > 0);
    uint8_t exp = sizeof(x) * 8 - 1 - __builtin_clzl(x);
    assert(x & (1UL << exp));
    return exp;
}

inline constexpr uint8_t log2_round(size_t x) {
    assert(x > 0);
    uint8_t exp = log2_truncate(x);
    assert(x & (1UL << exp));
    bool carry = exp && (x & (1UL << (exp - 1)));
    return exp + carry;
}

inline constexpr uint8_t log2_round_up(size_t x) {
    assert(x > 0);
    return (x <= 1) ? x : log2_truncate(x - 1) + 1;
}

inline size_t round_up_to_exp2(size_t x) {
    if (x == 0) return 1;
    uint32_t y =  1UL << log2_truncate(x);
    assert(x&y);
    return y << (!!(x^y));
}

template<typename INT>
struct xrange_t
{
    const INT _begin, _end;
    const int64_t _step;
    struct iterator
    {
        const xrange_t* _xrange;
        INT i;
        iterator& operator++()
        {
            i += _xrange->_step;
            return *this;
        }
        INT operator*()
        {
            return i;
        }
        bool operator == (const iterator& rhs) const
        {
            return _xrange == rhs._xrange && i == rhs.i;
        }
        bool operator != (const iterator& rhs) const
        {
            return !(*this == rhs);
        }
    };
    iterator begin() const
    {
        return iterator{this, _begin};
    }
    iterator end() const
    {
        return iterator{this, _end};
    }
};

// the xrange() series function are utilities to iterate a range of
// integers from begin (inclusive) to end (exclusive), imitating the
// xrange() function of Python
// usage: for (auto i: xrange(2, 8)) { ... }

template<typename T> inline
xrange_t<T> xrange(T begin, T end, int64_t step = 1) {
    static_assert(std::is_integral<T>::value, "...");
    assert(begin < end && (end - begin) % step == 0);
    return xrange_t<T>{begin, end, step};
}

template<typename T> inline
xrange_t<T> xrange(T end) { return xrange<T>(0, end); }
/*
template<typename T, ENABLE_IF(!std::is_signed<T>::value)>
xrange_t<uint64_t> xrange(T begin, T end, int64_t step = 1)
{
    static_assert(std::is_integral<T>::value, "...");
    return xrange_t<uint64_t>{begin, end, step};
}

template<typename T, ENABLE_IF(!std::is_signed<T>::value)>
xrange_t<uint64_t> xrange(T end)
{
    return xrange<T>(0, end);
}
*/

#define FOR_LOOP(N) for (auto i = N; i; --i)

inline uint64_t align_down(uint64_t x, uint64_t alignment)
{
    return x & ~(alignment - 1);
}

inline uint64_t align_up(uint64_t x, uint64_t alignment)
{
    return align_down(x + alignment - 1, alignment);
}

template<typename T>
inline T* align_ptr(T* p, uint64_t alignment)
{
    return (T*)align_up((uint64_t)p, alignment);
}

#define ALIGNED_MEM(name, size, alignment)  \
    char __buf##name[(size) + (alignment)]; \
    char* name = align_ptr(__buf##name, alignment);

#define ALIGNED_MEM4K(buf, size) ALIGNED_MEM(buf, size, 4096)

template<typename T>
void safe_delete(T*& obj)
{
    delete obj;
    obj = nullptr;
}

class OwnedPtr_Base
{
protected:
    void* m_ptr;
    OwnedPtr_Base(void* ptr, bool ownership)
    {
        m_ptr = (void*)((uint64_t)ptr & (uint64_t)ownership);
    }
    const static uint64_t mask = 1;
    void* get()
    {
        return (void*)((uint64_t)m_ptr & ~mask);
    }
    bool owned()
    {
        return (uint64_t)m_ptr & mask;
    }
};

template<typename T>
class OwnedPtr : OwnedPtr_Base
{
public:
    OwnedPtr(T* ptr, bool ownership) :
        OwnedPtr_Base(ptr, ownership) { }
    ~OwnedPtr()
    {
        if (owned())
            delete (T*)get();
    }
    T* operator->()
    {
        return (T*)get();
    }
    operator T* ()
    {
        return (T*)get();
    }
};

// release resource by RAII
#define WITH(init_expr) if (init_expr)

// release resource explicitly
#define WITH_Release(init_expr, release_expr)  \
    if (init_expr) if (DEFER(release_expr))

/* example of WITH
	WITH_Release(auto x=getx(), release(x)) {
		printf("x=%d, y=%d\n", x, __y);
	}

	WITH(auto x = getx()) {
                printf("x=%d, y=%d\n", x, __y);
	}
*/

#ifndef likely
constexpr bool likely(bool expr) { return __builtin_expect(expr, true); }
#endif /* likely */

#ifndef unlikely
constexpr bool unlikely(bool expr) { return __builtin_expect(expr, false); }
#endif /* unlikely */

int version_compare(std::string_view a, std::string_view b, int& result);
int kernel_version_compare(std::string_view dst, int& result);
void print_stacktrace();

namespace photon {

// Saturating addition, no upward overflow
__attribute__((always_inline)) inline
uint64_t sat_add(uint64_t x, uint64_t y) {
	uint64_t z, c = __builtin_uaddl_overflow(x, y, (unsigned long*)&z);
	return -c | z;
}

// Saturating subtract, no downward overflow
__attribute__((always_inline)) inline
uint64_t sat_sub(uint64_t x, uint64_t y) {
    uint64_t z, c = __builtin_usubl_overflow(x, y, (unsigned long*)&z);
    return c ? 0 : z;
}

}



