/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
 * CANN Open Software License Agreement Version 2.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 */

#ifndef H5CF96432_BE55_46BE_B9E1_8F7A5C662D50
#define H5CF96432_BE55_46BE_B9E1_8F7A5C662D50

#include <memory>
#include "memory/allocator/scalable_config.h"
#include "memory/span/span_layer_allocator.h"
#include "memory/span/span_allocator.h"
#include "memory/span/span_layer_lut.h"
#include "memory_pool.h"

#define DLOG_EVENT 0x10

#define LOG_BY_TYPE(type, fmt, ...)                                                                                 \
  do {                                                                                                              \
    if (type == DLOG_INFO) {                                                                                        \
      dlog_info(LLM_MODULE_NAME, "[HIXL] %lu %s %s:" fmt, LlmLog::GetTid(), GetId().c_str(), __FUNCTION__,          \
                ##__VA_ARGS__);                                                                                     \
    } else if (type == DLOG_ERROR) {                                                                                \
      LLMLOGE(ge::FAILED, fmt, ##__VA_ARGS__);                                                                      \
    } else if (type == DLOG_EVENT) {                                                                                \
      dlog_info(static_cast<int32_t>(static_cast<uint32_t>(RUN_LOG_MASK) | static_cast<uint32_t>(LLM_MODULE_NAME)), \
                "[HIXL] %lu %s %s:" fmt, LlmLog::GetTid(), GetId().c_str(), __FUNCTION__, ##__VA_ARGS__);           \
    } else {                                                                                                        \
    }                                                                                                               \
  } while (false)

namespace llm {
class ScalableAllocator : public MemoryPool {
 public:
  explicit ScalableAllocator(SpanAllocator &span_allocator, const ScalableConfig &cfg = ScalableConfig());
  ~ScalableAllocator() override;

  PageSpan *Alloc(ge::Allocator &allocator, const MemSize size) override;
  void Free(ge::MemBlock *block) override;
  void PrintDetails(const int32_t level = DLOG_ERROR) override;
  ge::Status InitFixSizedAllocator(ge::Allocator &allocator, void *base_addr, size_t size);

  const ScalableConfig &GetScalableConfig() const {
    return config_;
  }
  const std::string &GetId() const override;
  float GetReachTheoryRate() const;

 protected:
  ge::Status Finalize();
  virtual PageSpan *BlockAlloc(ge::Allocator &allocator, const BlockAddr block_addr, const MemAddr addr,
                               const size_t size);
  virtual PageSpan *SplitSpan(ge::Allocator &allocator, const SpanLayerId fix_layer_id, const SpanLayerId fit_layer_id,
                              PageSpan *const span, const MemSize size);

 private:
  PageSpan *FetchLayerSpan(const SpanLayerId layer_id);
  PageSpan *FetchSplitedSpan(ge::Allocator &allocator, const SpanLayerId fix_layer_id, const SpanLayerId fit_layer_id,
                             const MemSize size);
  PageSpan *PickOutBuddy(PageSpan *const buddy_span);
  PageSpan *TryMergePrev(PageSpan &span);
  PageSpan *TryMergeNext(PageSpan &span);

  PageSpan *PopSpanFromLayer(SpanLayer &layer);
  void PushSpanToLayer(SpanLayer &layer, PageSpan &span);
  void RemoveFromLayer(SpanLayer &layer, PageSpan &span);

  void OccupySpan(PageSpan &span, PageLen page_len);
  void FreeSpan(PageSpan &span);
  PageSpan *AllocImp(ge::Allocator &allocator, const MemSize size);
  SpanLayer *FetchSpanLayer(const SpanLayerId layer_id);
  MemSize GetAllocSize(const MemSize size) const;
  PageLen GetlayerSpanCapacity(const SpanLayerId layer_id) const;

 protected:
  size_t allocator_id_;
  std::string allocator_id_with_type_;
  const ScalableConfig config_;

 private:
  MemSize page_mem_size_;
  SpanLayerId span_layer_capacity_;
  SpanLayerId uncacheable_layer_start_;
  PageLen span_layer_page_capacity_;
  size_t alloc_succ_count_{0U};
  size_t free_succ_count_{0U};
  Link<PageSpan> occupied_spans_;
  std::vector<SpanLayer *> span_layers_;
  std::unique_ptr<SpanLayerLut> span_layer_lut_;
  SpanAllocator &span_allocator_;
  SpanLayerAllocator layer_allocator_;
  static std::atomic_size_t global_allocator_id_;
  MemSize max_occupied_size_{0U};
  void *base_addr_{nullptr};
  size_t theory_size_{0U};
  size_t theory_min_size_{0U};
  size_t real_theory_size_{0U};
  size_t real_theory_min_size_{0U};
  bool is_fix_sized_{false};
};
}  // namespace llm

#endif