#ifndef TILE_BASE_BYTE_ORDER_H
#define TILE_BASE_BYTE_ORDER_H

#pragma once

#include <cstdint>
#define TILE_BYTE_ORDER __BYTE_ORDER__
#define TILE_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
#define TILE_BIG_ENDIAN __ORDER_BIG_ENDIAN__

#ifdef __linux__
#include <arpa/inet.h>
#else

#if TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
#define htobe16(v) htons(v)
#define htobe32(v) htonl(v)
#define be16toh(v) ntohs(v)
#define be32toh(v) ntohl(v)
#define htole16(v) (v)
#define htole32(v) (v)
#define htole64(v) (v)
#define le16toh(v) (v)
#define le32toh(v) (v)
#define le64toh(v) (v)
#elif TILE_BYTE_ORDER == TILE_BIG_ENDIAN
#define htobe16(v) (v)
#define htobe32(v) (v)
#define be16toh(v) (v)
#define be32toh(v) (v)
#define htole16(v) __builtin_bswap16(v)
#define htole32(v) __builtin_bswap32(v)
#define htole64(v) __builtin_bswap64(v)
#define le16toh(v) __builtin_bswap16(v)
#define le32toh(v) __builtin_bswap32(v)
#define le64toh(v) __builtin_bswap64(v)
#else
#error "Unsupported byte order"
#endif
#endif

namespace tile {

inline uint16_t
ByteSwap16(uint16_t val)
{
    return __builtin_bswap16(val);
}

inline uint32_t
ByteSwap32(uint32_t val)
{
    return __builtin_bswap32(val);
}

inline uint64_t
ByteSwap64(uint64_t val)
{
    return __builtin_bswap64(val);
}

inline bool
IsHostLittleEndian()
{
#if TILE_LITTLE_ENDIAN == TILE_BYTE_ORDER
    return true;
#else
    return false;
#endif
}

inline bool
IsHostBigEndian()
{
#if TILE_BIG_ENDIAN == TILE_BYTE_ORDER
    return true;
#else
    return false;
#endif
}

inline uint16_t
HostToNetwork16(uint16_t val_in_host)
{
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
    return val_in_host;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
    return ByteSwap16(val_in_host);
#else
#error "Unsupported byte order"
#endif
}

inline uint32_t
HostToNetwork32(uint32_t val_in_host)
{
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
    return val_in_host;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
    return ByteSwap32(val_in_host);
#else
#error "Unsupported byte order"
#endif
}

inline uint64_t
HostToNetwork64(uint64_t val_in_host)
{
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
    return val_in_host;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
    return ByteSwap64(val_in_host);
#else
#error "Unsupported byte order"
#endif
}

inline uint16_t
NetworkToHost16(uint16_t val_in_network)
{
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
    return val_in_network;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
    return ByteSwap16(val_in_network);
#else
#error "Unsupported byte order"
#endif
}

inline uint32_t
NetworkToHost32(uint32_t val_in_network)
{
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
    return val_in_network;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
    return ByteSwap32(val_in_network);
#else
#error "Unsupported byte order"
#endif
}

inline uint64_t
NetworkToHost64(uint64_t val_in_network)
{
#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN
    return val_in_network;
#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN
    return ByteSwap64(val_in_network);
#else
#error "Unsupported byte order"
#endif
}

}// namespace tile

#endif// TILE_BASE_BYTE_ORDER_H
