/*
  Copyright (C) 2011-2016  Brazil
  Copyright (C) 2022  Sutou Kouhei <kou@clear-code.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#pragma once

#include "string.hpp"

namespace grn {
namespace dat {

class GRN_DAT_API Key {
 public:
  const UInt8 &operator[](UInt32 i) const {
    GRN_DAT_DEBUG_THROW_IF(i >= length());
    return buf_[i];
  }

  bool is_valid() const {
    return id() != INVALID_KEY_ID;
  }

  String str() const {
    return String(ptr(), length());
  }

  const void *ptr() const {
    return buf_;
  }
  UInt32 length() const {
    const UInt32 length = (length_high_ << 4) | (id_and_length_low_ & 0x0F);
    // 4096 bytes key overflows to 0, so 0 is treated as 4096.
    return (length == 0) ? MAX_KEY_LENGTH : length;
  }
  UInt32 id() const {
    return id_and_length_low_ >> 4;
  }

  bool equals_to(const void *ptr, UInt32 length, UInt32 offset = 0) const {
    if (length != this->length()) {
      return false;
    }
    for ( ; offset < length; ++offset) {
      if ((*this)[offset] != static_cast<const UInt8 *>(ptr)[offset]) {
        return false;
      }
    }
    return true;
  }

  // Creates an object of Key from given parameters. Then, the created object
  // is embedded into a specified buffer.
  static const Key &create(UInt32 *buf, UInt32 key_id,
                           const void *key_ptr, UInt32 key_length) {
    GRN_DAT_DEBUG_THROW_IF(buf == NULL);
    GRN_DAT_DEBUG_THROW_IF(key_id > MAX_KEY_ID);
    GRN_DAT_DEBUG_THROW_IF((key_ptr == NULL) && (key_length != 0));
    GRN_DAT_DEBUG_THROW_IF(key_length > MAX_KEY_LENGTH);

    *buf = (key_id << 4) | (key_length & 0x0F);
    UInt8 *ptr = reinterpret_cast<UInt8 *>(buf + 1);
    *ptr++ = static_cast<UInt8>(key_length >> 4);
    for (UInt32 i = 0; i < key_length; ++i) {
      ptr[i] = static_cast<const UInt8 *>(key_ptr)[i];
    }
    return *reinterpret_cast<const Key *>(buf);
  }

  // Calculates how many UInt32s are required for a string. It is guaranteed
  // that the estimated size is not less than the actual size.
  static UInt32 estimate_size(UInt32 length) {
    return static_cast<UInt32>(2 + (length / sizeof(UInt32)));
  }

  // Returns a reference to an invalid key.
  static const Key &invalid_key() {
    static const Key invalid_key;
    return invalid_key;
//    static const UInt32 invalid_key_buf[2] = { INVALID_KEY_ID << 4, 0 };
//    return *reinterpret_cast<const Key *>(invalid_key_buf);
  }

 private:
  UInt32 id_and_length_low_;
  UInt8 length_high_;
  UInt8 buf_[3];

  // Disallows instantiation.
  Key() : id_and_length_low_(INVALID_KEY_ID << 4), length_high_(0) {}
  ~Key() {}

  // Disallows copy and assignment.
  Key(const Key &);
  Key &operator=(const Key &);
};

}  // namespace dat
}  // namespace grn
