#ifndef CHUNKS_H_
#define CHUNKS_H_

#include <system.h>
#include <req.h>
#include <defs.h>
#include <nand.h>
#include <hint_data.h>

#define CH_REQ_GET_SPECIFIC(REQ) ((chm_specific_t*)((REQ)->specific))
#define CHM_PREPARE_TO_RECLAIM_REQ_MARKER ((req_t*)(IDX_EMPTY-1)) /* specifal marker of non-existent req, just to know that this chip is not really free, just waiting for reclaim req to be allocated*/

#define CH_PAGE_BITMAP_SIZE               (CH_CHUNKS_PER_PAGE / sizeof(uint8_t))
#define CH_DIRTY_CACHE_ENTRIES_THRESHOLD  CH_CHUNKS_PER_PAGE

/* flasg indicating whther chm req is a hint*/
#define HINT_FLAG_NO   (0)
#define HINT_FLAG_YES  (1)

#define RECLAMATION_FLAG_NO   (0)
#define RECLAMATION_FLAG_YES (1)

/*****************************************************/
/* 						Data structures												 */
/*****************************************************/

/* public structs*/
typedef struct ch_buffer_entry_st{
	uint32_t idx;
	uint32_t root_offset;
	uint8_t  buf[CH_CHUNK_SIZE];
	ch_addr  address; /* current physical address of chunk*/
  enum {
		CH_BUF_ENTRY_FREE, /*0 free*/
		CH_BUF_ENTRY_ALLOCATED, /* 1 empty, but will be filled when a read operation completes*/
		CH_BUF_ENTRY_ALLOCATED_OBS, /*2  same as ALLOCATED, but the same chunk was written to some other entry in the meantime (as hint...)*/

		CH_BUF_ENTRY_CLEAN, /* 3 in use by read op, but identical to an on-flash copy */
		CH_BUF_ENTRY_CLEAN_WRITE, /* 4 in use by write op, but identical to an on-flash copy. needed to prevent possible starvation of write request */
		CH_BUF_ENTRY_DIRTY, /* 5 in use and not stored on flash*/
		CH_BUF_ENTRY_WRITTEN /* 6 part of a pending flash write operation*/
  } state;

  uint32_t tm_req_count;
  list_t* tm_pending_list;

  bool_t reserved; /*is reserved for tm_reclaim*/
  req_t* written_req; /*testing field, to indicate which req changes state to WRITTEN*/
  bool_t hinted; /* is chunk cached from hint*/
  bool_t sent; /* was chunk sent back to host*/
#ifdef PROFILING
  uint32_t accessed;
#endif
}ch_buffer_entry;

typedef enum chm_enq_reason{
	CHM_ENQUEUE_REASON_NONE,
	CHM_ENQUEUE_REASON_NO_BUFFER,
	CHM_ENQUEUE_REASON_BUFFER_TAKEN,
	CHM_ENQUEUE_REASON_RECLAMTION_KICKOFF
} enq_reason_t;

typedef struct chm_specific_st {
  enum {
  	CHM_INITIAL, //0
  	CHM_LBA_INITIAL,//1
  	CHM_READ_READY,//2
		CHM_READ_DONE,//3
		CHM_NANDBUS_READ_READY,//4
		CHM_NAND_READ_DONE,//5
		CHM_NAND_WRITE_DONE,//6
		CHM_WRITE_READY,//7
		CHM_NANDBUS_WRITE_READY,//8
		CHM_WRITE_DONE,//9

		CHM_RECLAIM_START,//10
		CHM_RECLAIM_NAND_ERASE_DONE,//11
		CHM_RECLAIM_NANDBUS_ERASE,//12
		CHM_RECLAIM_ERASE,//13
		CHM_RECLAIM_NAND_WRITE_DONE,//14
		CHM_RECLAIM_NEXT_WRITE, //15
		CHM_RECLAIM_NANDBUS_PAGE_WRITE,//16
		CHM_RECLAIM_NAND_READ_DONE,//17
		CHM_RECLAIM_NANDBUS_PAGE_READ,//18
		CHM_READ_BEFORE_RECLAIM,//19

		CHM_WRITE_THRESHOLD_REACHED,//20
		CHM_WRITE_BUFFER_QUEUE,//21
		CHM_HANDLE_CACHED_READ,//22
		CHM_HANDLE_CACHED_WRITE,//23
		CHM_HINT_INITIAL,//24
		CHM_NAND_HINT_READ_DONE, //25
		CHM_HINT_DONE, //26

    CHM_DONE//27
  } state;

  enum{
  	CHM_READ,
  	CHM_READ_BEFORE_WRITE,
  	CHM_WRITE,
  	CHM_RECLAIM,
  	CHM_WRITE_HINT,
  	CHM_MAGIC_CACHE_FLUSH
  }rw;

  enq_reason_t enq_reason;
  bool_t dequeued;
  uint32_t lba;

  ch_buffer_entry* chunk_p;
  uint32_t read_res; /* container for read response*/

  uint32_t new_lba_page; /*new page to update in a chunk*/
  uint32_t page;

  bool_t need_to_reclaim;

  req_t* next;
  req_t* prev;

  hint_payload_t *hint;
} chm_specific_t;

typedef struct ch_block_st{
	uint32_t address;

	struct {
		uint8_t bitmap[CH_PAGE_BITMAP_SIZE]; /* chunk bitmap per page*/
	}page[NAND_PAGES_PER_ERASE_UNIT];
	uint32_t obs_count; /* # of obsolete chunks*/

	enum{
		CH_BLOCK_EMPTY, //0
		CH_BLOCK_PARTIAL, //1
		CH_BLOCK_FULL //2
	}state;
}ch_block;

typedef struct rec_buf_st{
	uint32_t address;
	uint8_t  buf[NAND_TOTAL_SIZE];
	uint32_t chunk_offset;
	uint32_t root_offset[CH_CHUNKS_PER_PAGE];
}rec_buf;

typedef struct chm_struct{
	/* root mapping*/
  struct {
  	uint32_t version;
  	ch_addr  address;
  }chunk[CH_CHUNK_COUNT];

  /* open eu for writes*/
  struct{
  	uint32_t blk_idx;
  	uint32_t address; /* physical address of erase unit*/
  	uint8_t  offset;  /* last programed page offset in unit*/

    req_t* current; /*program req in progress*/

    bool_t write_in_progress;
    req_t* reclamation_in_progress; /*is there a reclamation in progress*/
  }open_eu;

  /* recalamtion fields*/
  struct{
  	int32_t  blk_idx;
  	uint32_t address; /*address of reclaimed block*/
		uint32_t offset; /*page offset until which we reclaimed so far */

		rec_buf read_buf;
		rec_buf write_buf;
  } rec_eu;

  /* cached chunks */
  ch_buffer_entry buffer_entry[CH_TM_RECLAIM_BUFFER_ENTRIES];
  uint32_t reserved_used; /*usage count of cache chunks reserved for tm_reclaim request*/
  uint8_t write_buf[NAND_TOTAL_SIZE]; /*buffer for chunk writes to flash*/

  list_t* resource_pending;
  list_t* buffer_pending;
  int unforced_counter;
  list_t* write_pending;
  list_t* reclamation_pending;

  /* chunk blocks (bitmap per block)*/
  ch_block block[CH_BLOCK_COUNT];

  /* struct access mutex*/
  pthread_mutex_t mutex;
  uint32_t mutex_counter;

	/* deferred requests queue*/
  pthread_mutex_t defer_mutex;
  req_t* chmDeferred; /* tmRequest with no chmRequest*/
}chm_t;

/******************************************/
/*     Prototypes    										  */
/******************************************/

void chmInit();

void chmAlloc(req_t* caller_req);

error_t chmRequest(req_t *req,
                   uint8_t rw,
                   uint32_t lba,
                   uint32_t new_lba_page,
                   bool_t is_hint);

void chmHandler(req_t* req, void* response);

req_t* chm_list_pop_specific_req(list_t** list, req_t* req);

bool_t chm_is_write_buf_available();

req_t* chm_list_pop_head(list_t** list);

void chm_update_buffer_pending_queue();

uint32_t chm_get_cache_chunk(req_t* req);

void chm_update_reserve_cache();

uint32_t chm_get_cache_chunk_hint_accessible_idx();

int
chm_count_dirty_cache_entries();
#endif
