#ifndef CORE_UTILITY_HPP
#define CORE_UTILITY_HPP

#include "utils/Check.hpp"
#include "utils/Error.hpp"

#define CORE_SIZE(t) static_cast<int>(sizeof(t))

namespace Core {
    template<typename T>
    int popCount(const T& t) {
        static constexpr int map[16] = {0, 1, 1, 2, 1, 2, 2, 3,
                                        1, 2, 2, 3, 2, 3, 3, 4};
        int sum = 0;
        for(int i = 0; i < CORE_SIZE(T) * 8; i += 4) {
            sum += map[(t >> i) & 0xF];
        }
        return sum;
    }

    using ExitHandler = void (*)(int, void*);
    void exitWithHandler(const char* file, int line, int value);
    void setExitHandler(ExitHandler eh, void* data);
#define CORE_EXIT(exitValue)                                                   \
    Core::exitWithHandler(__FILE__, __LINE__, exitValue)

    check_return Error toString(signed short s, char* buffer, int size);
    check_return Error toString(unsigned short s, char* buffer, int size);
    check_return Error toString(signed int i, char* buffer, int size);
    check_return Error toString(unsigned int i, char* buffer, int size);
    check_return Error toString(signed long l, char* buffer, int size);
    check_return Error toString(unsigned long l, char* buffer, int size);
    check_return Error toString(signed long long ll, char* buffer, int size);
    check_return Error toString(unsigned long long ll, char* buffer, int size);
    check_return Error toString(float f, char* buffer, int size);
    check_return Error toString(double d, char* buffer, int size);
    check_return Error toString(long double ld, char* buffer, int size);

    check_return Error putChar(int c);

    void memorySet(void* p, int c, int n);
    void memoryCopy(void* dest, const void* src, int n);
    bool memoryCompare(const void* a, const void* b, int n);
    check_return Error reallocate(char*& p, int n);
    void free(void* p);
}

#endif