/**
 * 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 H3A8A51B9_C45F_4713_89E4_03F02FA1F99F
#define H3A8A51B9_C45F_4713_89E4_03F02FA1F99F

#include "kernel/memory/type/mem_addr.h"
#include "kernel/memory/type/ref_count.h"
#include "kernel/memory/util/link_node.h"
#include "kernel/memory/span/span_layer_id.h"
#include "kernel/memory/span/span_buddy_link.h"
#include "kernel/memory/mem_block.h"
#include "graph/types.h"

namespace gert {
class ScalableAllocator;
constexpr ge::float64_t kDelaySplitRatio = 0.01;

class PageSpan : public ge::MemBlock, public LinkNode<PageSpan> {
 public:
  PageSpan(ge::Allocator &allocator, ScalableAllocator &scalable_allocator, BlockAddr block_addr, MemAddr addr,
           size_t mem_size)
      : ge::MemBlock(allocator, addr, mem_size), block_addr_{block_addr}, scalable_allocator_{scalable_allocator} {
  }

  ~PageSpan() override {
    page_len_ = 0;
    link_.next_ = nullptr;
    link_.prev_ = nullptr;
    ref_span_ = nullptr;
    try_split_page_len_ = 0;
    real_size_ = 0U;
    new_va_span_ = false;
    pa_list_.clear();
  }

  void Alloc(PageLen page_len) {
    this->page_len_ = page_len;
    SetSize(GetSize());
    if (GetCount() == 0U) {
      AddCount();
    }
  }

  BlockAddr GetBlockAddr() const {
    return block_addr_;
  }

  void SetAddr(BlockAddr addr) {
    this->block_addr_ = addr;
  }

  void SetPageLen(PageLen page_len) {
    this->page_len_ = page_len;
    SetSize(GetSize());
  }

  PageLen GetPageLen() const {
    return page_len_;
  }

  PageSpan *GetPrevBuddy() {
    return buddy_link_.GetPrev();
  }

  PageSpan *GetNextBuddy() {
    return buddy_link_.GetNext();
  }

  bool HasSplited() const {
    return !buddy_link_.IsEmpty();
  }

  bool IsBuddyHeader() const {
    return buddy_link_.IsHeader();
  }

  void SetBuddy(PageSpan &buddy_span);
  void MergeBuddy(PageSpan &buddy_span);

  size_t GetSize() const;

  bool IsSplitable(const PageLen page_len);

  void SetRealSize(size_t real_size) {
    real_size_ = real_size;
  }

  size_t GetRealSize() const {
    return real_size_;
  }

  void SetRefSpan(PageSpan *ref_span) {
    ref_span_ = ref_span;
  }

  PageSpan *GetRefSpan() const {
    return ref_span_;
  }

  bool IsNewVaSpan() const {
    return new_va_span_;
  }

  void SetNewVaSpan(bool new_va_span) {
    new_va_span_ = new_va_span;
  }

  std::vector<size_t> &GetPaList() {
    return pa_list_;
  }

  void SetSplitable(bool splitable);
 private:
  BlockAddr block_addr_{nullptr};
  PageLen page_len_{0};
  PageLen try_split_page_len_{0};

  SpanBuddyLink buddy_link_;
  ScalableAllocator &scalable_allocator_;
  std::vector<size_t> pa_list_;
  size_t real_size_{0U};
  PageSpan *ref_span_{nullptr};
  bool new_va_span_{false};
  bool splitable_{false};
};
}

#endif