// 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 "kudu/common/key_encoder.h"

#include <memory>
#include <vector>

#include "kudu/common/common.pb.h"
#include "kudu/gutil/singleton.h"
#include "kudu/util/faststring.h"

using std::unique_ptr;
using std::vector;

namespace kudu {

// A resolver for Encoders
template <typename Buffer>
class EncoderResolver {
 public:
  const KeyEncoder<Buffer>& GetKeyEncoder(DataType t) {
    DCHECK(HasKeyEncoderForType(t));
    return *encoders_[t];
  }

  bool HasKeyEncoderForType(DataType t) {
    return t < encoders_.size() && encoders_[t];
  }

 private:
  EncoderResolver<Buffer>() {
    AddMapping<UINT8>();
    AddMapping<INT8>();
    AddMapping<UINT16>();
    AddMapping<INT16>();
    AddMapping<UINT32>();
    AddMapping<INT32>();
    AddMapping<UINT64>();
    AddMapping<INT64>();
    AddMapping<BINARY>();
    AddMapping<INT128>();
  }

  template<DataType Type> void AddMapping() {
    KeyEncoderTraits<Type, Buffer> traits;
    if (encoders_.size() <= Type) {
      encoders_.resize(static_cast<size_t>(Type) + 1);
    }
    CHECK(!encoders_[Type]) << "already have mapping for " << DataType_Name(Type);
    encoders_[Type].reset(new KeyEncoder<Buffer>(traits));
  }

  friend class Singleton<EncoderResolver<Buffer>>;
  // We use a vector instead of a map here since this shows up in some hot paths
  // and we know that the valid data types all have low enough IDs that the
  // vector will be small.
  vector<unique_ptr<KeyEncoder<Buffer>>> encoders_;
};

template <typename Buffer>
const KeyEncoder<Buffer>& GetKeyEncoder(const TypeInfo* typeinfo) {
  return Singleton<EncoderResolver<Buffer>>::get()->GetKeyEncoder(typeinfo->physical_type());
}

// Returns true if the type is allowed in keys.
bool IsTypeAllowableInKey(const TypeInfo* typeinfo) {
  return Singleton<EncoderResolver<faststring>>::get()->HasKeyEncoderForType(
      typeinfo->physical_type());
}

//------------------------------------------------------------
//// Template instantiations: We instantiate all possible templates to avoid linker issues.
//// see: https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl
////------------------------------------------------------------

template
const KeyEncoder<std::string>& GetKeyEncoder(const TypeInfo* typeinfo); // NOLINT

template
const KeyEncoder<faststring>& GetKeyEncoder(const TypeInfo* typeinfo);

}  // namespace kudu
