/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
#ifndef BYTEORDER_H_INCLUDED
#define BYTEORDER_H_INCLUDED

#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <boost/detail/endian.hpp>
#include "RocketMQClient.h"
#include "UtilAll.h"
//==============================================================================
/** Contains static methods for converting the byte order between different
    endiannesses.
*/
namespace rocketmq {

class ROCKETMQCLIENT_API ByteOrder {
 public:
  //==============================================================================
  /** Swaps the upper and lower bytes of a 16-bit integer. */
  static uint16 swap(uint16 value);

  /** Reverses the order of the 4 bytes in a 32-bit integer. */
  static uint32 swap(uint32 value);

  /** Reverses the order of the 8 bytes in a 64-bit integer. */
  static uint64 swap(uint64 value);

  //==============================================================================
  /** Swaps the byte order of a 16-bit int if the CPU is big-endian */
  static uint16 swapIfBigEndian(uint16 value);

  /** Swaps the byte order of a 32-bit int if the CPU is big-endian */
  static uint32 swapIfBigEndian(uint32 value);

  /** Swaps the byte order of a 64-bit int if the CPU is big-endian */
  static uint64 swapIfBigEndian(uint64 value);

  /** Swaps the byte order of a 16-bit int if the CPU is little-endian */
  static uint16 swapIfLittleEndian(uint16 value);

  /** Swaps the byte order of a 32-bit int if the CPU is little-endian */
  static uint32 swapIfLittleEndian(uint32 value);

  /** Swaps the byte order of a 64-bit int if the CPU is little-endian */
  static uint64 swapIfLittleEndian(uint64 value);

  //==============================================================================
  /** Turns 4 bytes into a little-endian integer. */
  static uint32 littleEndianInt(const void* bytes);

  /** Turns 8 bytes into a little-endian integer. */
  static uint64 littleEndianInt64(const void* bytes);

  /** Turns 2 bytes into a little-endian integer. */
  static uint16 littleEndianShort(const void* bytes);

  /** Turns 4 bytes into a big-endian integer. */
  static uint32 bigEndianInt(const void* bytes);

  /** Turns 8 bytes into a big-endian integer. */
  static uint64 bigEndianInt64(const void* bytes);

  /** Turns 2 bytes into a big-endian integer. */
  static uint16 bigEndianShort(const void* bytes);

  //==============================================================================
  /** Converts 3 little-endian bytes into a signed 24-bit value (which is
   * sign-extended to 32 bits). */
  static int littleEndian24Bit(const void* bytes);

  /** Converts 3 big-endian bytes into a signed 24-bit value (which is
   * sign-extended to 32 bits). */
  static int bigEndian24Bit(const void* bytes);

  /** Copies a 24-bit number to 3 little-endian bytes. */
  static void littleEndian24BitToChars(int value, void* destBytes);

  /** Copies a 24-bit number to 3 big-endian bytes. */
  static void bigEndian24BitToChars(int value, void* destBytes);

  //==============================================================================
  /** Returns true if the current CPU is big-endian. */
  static bool isBigEndian();
};

//==============================================================================

inline uint16 ByteOrder::swap(uint16 n) {
  return static_cast<uint16>((n << 8) | (n >> 8));
}

inline uint32 ByteOrder::swap(uint32 n) {
  return (n << 24) | (n >> 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8);
}

inline uint64 ByteOrder::swap(uint64 value) {
  return (((uint64)swap((uint32)value)) << 32) | swap((uint32)(value >> 32));
}

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__  //__BYTE_ORDER__ is defined by GCC
inline uint16 ByteOrder::swapIfBigEndian(const uint16 v) {
  return v;
}
inline uint32 ByteOrder::swapIfBigEndian(const uint32 v) {
  return v;
}
inline uint64 ByteOrder::swapIfBigEndian(const uint64 v) {
  return v;
}
inline uint16 ByteOrder::swapIfLittleEndian(const uint16 v) {
  return swap(v);
}
inline uint32 ByteOrder::swapIfLittleEndian(const uint32 v) {
  return swap(v);
}
inline uint64 ByteOrder::swapIfLittleEndian(const uint64 v) {
  return swap(v);
}
inline uint32 ByteOrder::littleEndianInt(const void* const bytes) {
  return *static_cast<const uint32*>(bytes);
}
inline uint64 ByteOrder::littleEndianInt64(const void* const bytes) {
  return *static_cast<const uint64*>(bytes);
}
inline uint16 ByteOrder::littleEndianShort(const void* const bytes) {
  return *static_cast<const uint16*>(bytes);
}
inline uint32 ByteOrder::bigEndianInt(const void* const bytes) {
  return swap(*static_cast<const uint32*>(bytes));
}
inline uint64 ByteOrder::bigEndianInt64(const void* const bytes) {
  return swap(*static_cast<const uint64*>(bytes));
}
inline uint16 ByteOrder::bigEndianShort(const void* const bytes) {
  return swap(*static_cast<const uint16*>(bytes));
}
inline bool ByteOrder::isBigEndian() {
  return false;
}
#else
inline uint16 ByteOrder::swapIfBigEndian(const uint16 v) {
  return swap(v);
}
inline uint32 ByteOrder::swapIfBigEndian(const uint32 v) {
  return swap(v);
}
inline uint64 ByteOrder::swapIfBigEndian(const uint64 v) {
  return swap(v);
}
inline uint16 ByteOrder::swapIfLittleEndian(const uint16 v) {
  return v;
}
inline uint32 ByteOrder::swapIfLittleEndian(const uint32 v) {
  return v;
}
inline uint64 ByteOrder::swapIfLittleEndian(const uint64 v) {
  return v;
}
inline uint32 ByteOrder::littleEndianInt(const void* const bytes) {
  return swap(*static_cast<const uint32*>(bytes));
}
inline uint64 ByteOrder::littleEndianInt64(const void* const bytes) {
  return swap(*static_cast<const uint64*>(bytes));
}
inline uint16 ByteOrder::littleEndianShort(const void* const bytes) {
  return swap(*static_cast<const uint16*>(bytes));
}
inline uint32 ByteOrder::bigEndianInt(const void* const bytes) {
  return *static_cast<const uint32*>(bytes);
}
inline uint64 ByteOrder::bigEndianInt64(const void* const bytes) {
  return *static_cast<const uint64*>(bytes);
}
inline uint16 ByteOrder::bigEndianShort(const void* const bytes) {
  return *static_cast<const uint16*>(bytes);
}
inline bool ByteOrder::isBigEndian() {
  return true;
}
#endif

inline int ByteOrder::littleEndian24Bit(const void* const bytes) {
  return (((int)static_cast<const int8*>(bytes)[2]) << 16) | (((int)static_cast<const uint8*>(bytes)[1]) << 8) |
         ((int)static_cast<const uint8*>(bytes)[0]);
}
inline int ByteOrder::bigEndian24Bit(const void* const bytes) {
  return (((int)static_cast<const int8*>(bytes)[0]) << 16) | (((int)static_cast<const uint8*>(bytes)[1]) << 8) |
         ((int)static_cast<const uint8*>(bytes)[2]);
}
inline void ByteOrder::littleEndian24BitToChars(const int value, void* const destBytes) {
  static_cast<uint8*>(destBytes)[0] = (uint8)value;
  static_cast<uint8*>(destBytes)[1] = (uint8)(value >> 8);
  static_cast<uint8*>(destBytes)[2] = (uint8)(value >> 16);
}
inline void ByteOrder::bigEndian24BitToChars(const int value, void* const destBytes) {
  static_cast<uint8*>(destBytes)[0] = (uint8)(value >> 16);
  static_cast<uint8*>(destBytes)[1] = (uint8)(value >> 8);
  static_cast<uint8*>(destBytes)[2] = (uint8)value;
}
}  // namespace rocketmq
#endif  // BYTEORDER_H_INCLUDED
