//
// Created by 马也驰 on 2025/1/4.
//

#ifndef LEVELDB_VLOG_CACHE_H
#define LEVELDB_VLOG_CACHE_H

#include <cassert>
#include <mutex>
#include <string>

#define BLOCK_SIZE  4096

struct frame_info {
  std::mutex info_latch_;
  bool used;
  std::string vlog_name;
  size_t vlog_block_num;
  bool is_dirty;

  inline void set_used() {
    info_latch_.lock();
    assert(used == false);
    used = true;
    info_latch_.unlock();
  }
  inline void set_unused() {
    info_latch_.lock();
    assert(used == true);
    used = false;
    info_latch_.unlock();
  }
  inline void set_dirty() {
    info_latch_.lock();
    assert(is_dirty == false);
    is_dirty = true;
    info_latch_.unlock();
  }
  inline void set_nondirty() {
    info_latch_.lock();
    assert(is_dirty == true);
    is_dirty = false;
    info_latch_.unlock();
  }
  inline void set_vlog_block_num(size_t vb) {
    info_latch_.lock();
    vlog_block_num = vb;
    info_latch_.unlock();
  }
  inline void set_vlog_name(std::string &vn) {
    info_latch_.lock();
    vlog_name = vn;
    info_latch_.unlock();
  }
  inline bool cache_hit(std::string &vn, size_t vbn) {
    return used && (vlog_name == vn) && (vlog_block_num == vbn);
  }
};


struct block_frame {
  // 读,写,块置换 必须对这个mutex上锁，保证线性化
  // 读写必须对这个mutex上锁，保证线性化
  std::mutex mtx;
  frame_info frame_info_;
  char *block_buff;

  block_frame() {
    frame_info_.used = false;
    frame_info_.is_dirty = false;
    block_buff = static_cast<char*>(malloc(BLOCK_SIZE));
  }

  void free_block_buff() {
    free(block_buff);
  }
};




class VlogCache {
#define BLOCK_NUMS                        1024
#define VALUE_ACROSS_BLOCK(offset, len)   (((offset)/BLOCK_SIZE) != (((offset)+(len)-1)/BLOCK_SIZE))
#define DATA_SIZE_MASK                    0x7fff
#define DATA_DELE_MASK                    0x8000

 public:
  VlogCache() {
    for (auto i = 0; i < BLOCK_NUMS; i++) {
      block_frames[i] = new block_frame();
    }
  }

  ~VlogCache() {
    flush_all_blocks();
    for (auto i = 0; i < BLOCK_NUMS; i++) {
      block_frames[i]->free_block_buff();
    }
  }

 public:
  void read_data(std::string &vlog_name, std::string& value, size_t offset, size_t len);
  void write_data(std::string &vlog_name, std::string& value, size_t offset, size_t len);
  uint16_t delete_data(std::string &vlog_name, size_t offset);

 private:
  void read_data_inblock(std::string &vlog_name, std::string& value, size_t offset, size_t len);
  void write_data_inblock(std::string &vlog_name, std::string& value, size_t offset, size_t len);
  void write_back_block(size_t block_num);
  void read_in_block(size_t block_num, std::string &vlog_name, size_t vlog_block_num);

 private:
  void flush_all_blocks();
  inline size_t offset_2_blocknum(size_t offset) {
    return (offset / BLOCK_SIZE) % BLOCK_NUMS;
  }
  inline size_t offset_2_inblock_offset(size_t offset) {
    return offset % BLOCK_SIZE;
  }

 private:
  block_frame *block_frames[BLOCK_NUMS];
};



#endif  // LEVELDB_VLOG_CACHE_H
