text: /*
   * FlashChainStore.cpp
   *
   *  Created on: 2019/01/23
   *      Author: iizuka
   */
  
  #include "flash_store/FlashChainStore.h"
  #include "flash_store/FlashBlockStore.h"
  #include "flash_store/FlashBlockHeightIndex.h"
  #include "flash_store/FlashBlockList.h"
  
  #include "base_io/File.h"
  
  #include "osenv/funcs.h"
  
  #include "random_access_file/DiskCacheManager.h"
  
  #include "base_thread/ConcurrentGate.h"
  
  #include "flash/AbstractFlashBlock.h"
  #include "flash/FlashBlockHash.h"
  
  #include "base/StackRelease.h"
  
  
  namespace codablecash {
  
  FlashChainStore::FlashChainStore(const File* baseDir) {
  	this->baseDir = baseDir->get(FLASH_CHAIN_DIR);
  	this->cacheManager = new DiskCacheManager(Os::getSystemPageSize() * 4 * 4);
  
  	this->blocks = nullptr;
  	this->heightIndex = nullptr;
  
  	this->rwLock = new ConcurrentGate();
  }
  
  FlashChainStore::~FlashChainStore() {
  	close();
  
  	delete this->baseDir;
  	delete this->cacheManager;
  
  	delete this->rwLock;
  }
  
  void FlashChainStore::init() {
  	this->blocks = new FlashBlockStore(this->baseDir, this->cacheManager);
  	if(!this->blocks->exists()){
  		this->blocks->create();
  	}
  
  	this->heightIndex = new FlashBlockHeightIndex(this->baseDir, this->cacheManager);
  	if(!this->heightIndex->exists()){
  		this->heightIndex->create();
  	}
  
  	this->blocks->open();
  	this->heightIndex->open();
  }
  
  void FlashChainStore::close() {
  	if(this->blocks != nullptr){
  		this->blocks->close();
  		this->heightIndex->close();
  
  		delete this->blocks, this->blocks = nullptr;
  		delete this->heightIndex, this->heightIndex = nullptr;
  	}
  
  }
  
  bool FlashChainStore::exist() const noexcept {
  	FlashBlockStore s(this->baseDir, this->cacheManager);
  	FlashBlockHeightIndex h(this->baseDir, this->cacheManager);
  
  	return s.exists() && h.exists();
  }
  
  void FlashChainStore::create() {
  	FlashBlockStore s(this->baseDir, this->cacheManager);
  	FlashBlockHeightIndex h(this->baseDir, this->cacheManager);
  
  	s.create();
  	h.create();
  }
  
  void FlashChainStore::addBlock(const AbstractFlashBlock* block) {
  	StackWriteLock __lock(this->rwLock);
  
  	uint64_t height = block->getHeight();
  
  	uint64_t fpos = this->blocks->storeBlock(block);
  
  	this->heightIndex->addBlock(height, fpos);
  }
  
  ArrayList<AbstractFlashBlock>* FlashChainStore::getBlocksAtHeight(uint64_t height) {
  	StackReadLock __lock(this->rwLock);
  
  	FlashBlockList* list = this->heightIndex->getBlocksAtHeight(height); __STP(list);
  
  	if(list == nullptr){
  		return nullptr;
  	}
  
  	ArrayList<AbstractFlashBlock>* blocks = new ArrayList<AbstractFlashBlock>();
  
  	int maxLoop = list->size();
  	for(int i = 0; i != maxLoop; ++i){
  		uint64_t fpos = list->get(i);
  
  		AbstractFlashBlock* block = this->blocks->loadBlock(fpos);
  		blocks->addElement(block);
  	}
  
  	return blocks;
  }
  
  void FlashChainStore::removeBlock(FlashBlockHash* hash, uint64_t height) {
  	StackWriteLock __lock(this->rwLock);
  
  	FlashBlockList* list = this->heightIndex->getBlocksAtHeight(height); __STP(list);
  	if(list == nullptr){
  		return;
  	}
  
  	uint64_t fpos2remove = 0;
  
  	int maxLoop = list->size();
  	for(int i = 0; i != maxLoop; ++i){
  		uint64_t fpos = list->get(i);
  
  		AbstractFlashBlock* block = this->blocks->loadBlock(fpos); __STP(block);
  		FlashBlockHash* h = block->getBlockHash(); __STP(h);
  
  		if(h->compareTo(hash) == 0){
  			fpos2remove = fpos;
  			break;
  		}
  	}
  
  	this->heightIndex->removeIndex(height, fpos2remove);
  	this->blocks->removeBlock(fpos2remove);
  }
  
  } /* namespace codablecash */
  