/*
 * 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.
 */

#include "ByteArray.hpp"

#include <cstdlib>  // For wcstombs()
#include <cstring>

#include "config.h"

namespace apache {
namespace geode {
namespace client {
ByteArray ByteArray::fromString(const std::string& str) {
  ByteArray ba;
  if (!str.empty()) {
    ba.m_bytes = std::shared_ptr<uint8_t>(new uint8_t[(str.length() + 1) / 2],
                                          std::default_delete<uint8_t[]>());
    ba.m_size = 0U;
    bool shift = true;
    for (std::string::size_type pos = 0; pos < str.length(); ++pos) {
      unsigned int value = 0U;
      if ('0' <= str.at(pos) && str.at(pos) <= '9') {
        value = str.at(pos) - '0';
      } else if ('A' <= str.at(pos) && str.at(pos) <= 'F') {
        value = str.at(pos) - '7';
      } else if ('a' <= str.at(pos) && str.at(pos) <= 'f') {
        value = str.at(pos) - 'W';
      }
      if (shift) {
        shift = false;
        ba.m_bytes.get()[ba.m_size] = static_cast<uint8_t>(0xFF & (value << 4));
      } else {
        shift = true;
        ba.m_bytes.get()[ba.m_size++] |= static_cast<uint8_t>(0xFF & value);
      }
    }
    // Accommodate strings with an odd number of characters.
    if (!shift) {
      ba.m_size++;
    }
  }
  return ba;
}

std::string ByteArray::toString(const ByteArray& ba) {
  std::string str;
  const uint8_t* bytes = ba.m_bytes.get();
  if (bytes) {
    for (std::size_t i = 0; i < ba.m_size; ++i) {
      for (std::size_t j = 2; j > 0; --j) {
        const uint8_t nibble =
            static_cast<uint8_t>(0x0F & (bytes[i] >> (4 * (j - 1))));
        if (nibble < 10U) {
          str += static_cast<char>('0' + nibble);
        } else {
          str += static_cast<char>('7' + nibble);
        }
      }
    }
  }
  return str;
}

ByteArray::ByteArray() : m_size(0) {
  // NOP
}

ByteArray::ByteArray(const ByteArray& other) : m_size(0) { operator=(other); }

ByteArray::ByteArray(const uint8_t* bytes, const std::size_t size) : m_size(0) {
  if (bytes && 0 < size) {
    m_bytes = std::shared_ptr<uint8_t>(new uint8_t[size],
                                       std::default_delete<uint8_t[]>());
    std::memcpy(m_bytes.get(), bytes, size);
    m_size = size;
  }
}

ByteArray::~ByteArray() {
  // NOP
}

ByteArray& ByteArray::operator=(const ByteArray& other) {
  if (&other != this) {
    m_bytes = other.m_bytes;
    m_size = other.m_size;
  }
  return *this;
}

std::string ByteArray::toString() const { return toString(*this); }
}  // namespace client
}  // namespace geode
}  // namespace apache
