// Copyright 2026 gRPC authors.
//
// Licensed 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 GRPCPP_IMPL_CALL_CONTEXT_REGISTRY_H
#define GRPCPP_IMPL_CALL_CONTEXT_REGISTRY_H

#include <cstdint>
#include <utility>

namespace grpc_core {
class Arena;
}  // namespace grpc_core

namespace grpc {
namespace impl {

// Must define Propagate and extend CallContextTypeBase
template <typename T>
struct CallContextType;

template <typename T>
struct CallContextTypeBase {
 public:
  static uint16_t id();

 private:
  static void Destroy(void* p) { delete static_cast<T*>(p); }

  static void Propagate(void* p, grpc_core::Arena* a) {
    CallContextType<T>::Propagate(static_cast<T*>(p), a);
  }
};

class CallContextRegistry {
 public:
  // An opaque type to be used in ClientContext.
  using ElementList = void**;

  // Adds an element to elements.
  template <typename T>
  static void SetContext(T element, ElementList& elements) {
    uint16_t id = CallContextType<T>::id();
    if (elements == nullptr) {
      uint16_t num_elements = Count();
      elements = new void*[num_elements]();
    }
    DestroyElement(id, elements[id]);
    elements[id] = new T(std::move(element));
  }

  // Called when starting the C-core call.
  // Passes ownership of all elements into the C-core arena.
  // Deletes elements and resets it to nullptr.
  static void Propagate(ElementList& elements, grpc_core::Arena* arena);

  // Called on ClientContext destruction.  No-op if elements is already null.
  // Otherwise, deletes the context elements and deletes elements.
  static void Destroy(ElementList& elements);

 private:
  static uint16_t Count();

  static void DestroyElement(uint16_t id, void* element);

  static uint16_t Register(void (*destroy)(void*),
                           void (*propagate)(void*, grpc_core::Arena*));

  template <typename U>
  friend struct CallContextTypeBase;
};

template <typename T>
uint16_t CallContextTypeBase<T>::id() {
  static uint16_t id = CallContextRegistry::Register(
      &CallContextTypeBase<T>::Destroy, &CallContextTypeBase<T>::Propagate);
  return id;
}

}  // namespace impl
}  // namespace grpc

#endif  // GRPCPP_IMPL_CALL_CONTEXT_REGISTRY_H
