package database.wallet;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.TreeMap;
import org.mapdb.BTreeKeySerializer;
import org.mapdb.DB;
import org.mapdb.Fun;
import org.mapdb.Fun.Tuple2;
import org.mapdb.BTreeMap;
import qora.account.Account;
import qora.block.Block;
import utils.ObserverMessage;
import utils.Pair;
import utils.ReverseComparator;
import database.DBMap;
import database.serializer.BlockSerializer;
public class BlockMap extends DBMap<Tuple2<String, String>, Block>
{
public static final int TIMESTAMP_INDEX = 1;
public static final int GENERATOR_INDEX = 2;
public static final int BALANCE_INDEX = 3;
public static final int TRANSACTIONS_INDEX = 4;
public static final int FEE_INDEX = 5;
private Map<Integer, Integer> observableData = new HashMap<Integer, Integer>();
public BlockMap(WalletDatabase walletDatabase, DB database)
{
super(walletDatabase, database);
this.observableData.put(DBMap.NOTIFY_ADD, ObserverMessage.ADD_BLOCK_TYPE);
this.observableData.put(DBMap.NOTIFY_REMOVE, ObserverMessage.REMOVE_BLOCK_TYPE);
this.observableData.put(DBMap.NOTIFY_LIST, ObserverMessage.LIST_BLOCK_TYPE);
}
public BlockMap(BlockMap parent)
{
super(parent);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void createIndexes(DB database)
{
//TIMESTAMP INDEX
NavigableSet<Tuple2<Long, Tuple2<String, String>>> timestampIndex = database.createTreeSet("blocks_index_timestamp")
.comparator(Fun.COMPARATOR)
.makeOrGet();
NavigableSet<Tuple2<Long, Tuple2<String, String>>> descendingTimestampIndex = database.createTreeSet("blocks_index_timestamp_descending")
.comparator(new ReverseComparator(Fun.COMPARATOR))
.makeOrGet();
createIndex(TIMESTAMP_INDEX, timestampIndex, descendingTimestampIndex, new Fun.Function2<Long, Tuple2<String, String>, Block>() {
@Override
public Long run(Tuple2<String, String> key, Block value) {
return value.getTimestamp();
}
});
//GENERATOR INDEX
NavigableSet<Tuple2<String, Tuple2<String, String>>> generatorIndex = database.createTreeSet("blocks_index_generator")
.comparator(Fun.COMPARATOR)
.makeOrGet();
NavigableSet<Tuple2<String, Tuple2<String, String>>> descendingGeneratorIndex = database.createTreeSet("blocks_index_generator_descending")
.comparator(new ReverseComparator(Fun.COMPARATOR))
.makeOrGet();
createIndex(GENERATOR_INDEX, generatorIndex, descendingGeneratorIndex, new Fun.Function2<String, Tuple2<String, String>, Block>() {
@Override
public String run(Tuple2<String, String> key, Block value) {
return key.a;
}
});
//BALANCE INDEX
NavigableSet<Tuple2<Long, Tuple2<String, String>>> balanceIndex = database.createTreeSet("blocks_index_balance")
.comparator(Fun.COMPARATOR)
.makeOrGet();
NavigableSet<Tuple2<Long, Tuple2<String, String>>> descendingBalanceIndex = database.createTreeSet("blocks_index_balance_descending")
.comparator(new ReverseComparator(Fun.COMPARATOR))
.makeOrGet();
createIndex(BALANCE_INDEX, balanceIndex, descendingBalanceIndex, new Fun.Function2<Long, Tuple2<String, String>, Block>() {
@Override
public Long run(Tuple2<String, String> key, Block value) {
return value.getGeneratingBalance();
}
});
//TRANSACTIONS INDEX
NavigableSet<Tuple2<Integer, Tuple2<String, String>>> transactionsIndex = database.createTreeSet("blocks_index_transactions")
.comparator(Fun.COMPARATOR)
.makeOrGet();
NavigableSet<Tuple2<Integer, Tuple2<String, String>>> descendingTransactionsIndex = database.createTreeSet("blocks_index_transactions_descending")
.comparator(new ReverseComparator(Fun.COMPARATOR))
.makeOrGet();
createIndex(TRANSACTIONS_INDEX, transactionsIndex, descendingTransactionsIndex, new Fun.Function2<Integer, Tuple2<String, String>, Block>() {
@Override
public Integer run(Tuple2<String, String> key, Block value) {
return value.getTransactionCount();
}
});
//FEE INDEX
NavigableSet<Tuple2<BigDecimal, Tuple2<String, String>>> feeIndex = database.createTreeSet("blocks_index_fee")
.comparator(Fun.COMPARATOR)
.makeOrGet();
NavigableSet<Tuple2<BigDecimal, Tuple2<String, String>>> descendingFeeIndex = database.createTreeSet("blocks_index_fee_descending")
.comparator(new ReverseComparator(Fun.COMPARATOR))
.makeOrGet();
createIndex(FEE_INDEX, feeIndex, descendingFeeIndex, new Fun.Function2<BigDecimal, Tuple2<String, String>, Block>() {
@Override
public BigDecimal run(Tuple2<String, String> key, Block value) {
return value.getTotalFee();
}
});
}
@Override
protected Map<Tuple2<String, String>, Block> getMap(DB database)
{
//OPEN MAP
return database.createTreeMap("blocks")
.keySerializer(BTreeKeySerializer.TUPLE2)
.valueSerializer(new BlockSerializer())
.counterEnable()
.makeOrGet();
}
@Override
protected Map<Tuple2<String, String>, Block> getMemoryMap()
{
return new TreeMap<Tuple2<String, String>, Block>(Fun.TUPLE2_COMPARATOR);
}
@Override
protected Block getDefaultValue()
{
return null;
}
@Override
protected Map<Integer, Integer> getObservableData()
{
return this.observableData;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<Block> get(Account account)
{
List<Block> blocks = new ArrayList<Block>();
try
{
Map<Tuple2<String, String>, Block> accountBlocks = ((BTreeMap) this.map).subMap(
Fun.t2(account.getAddress(), null),
Fun.t2(account.getAddress(), Fun.HI()));
//GET ITERATOR
Iterator<Block> iterator = accountBlocks.values().iterator();
while(iterator.hasNext())
{
blocks.add(iterator.next());
}
}
catch(Exception e)
{
//ERROR
e.printStackTrace();
}
return blocks;
}
public List<Pair<Account, Block>> get(List<Account> accounts)
{
List<Pair<Account, Block>> blocks = new ArrayList<Pair<Account, Block>>();
try
{
//FOR EACH ACCOUNTS
synchronized(accounts)
{
for(Account account: accounts)
{
List<Block> accountBlocks = get(account);
for(Block block: accountBlocks)
{
blocks.add(new Pair<Account, Block>(account, block));
}
}
}
}
catch(Exception e)
{
//ERROR
e.printStackTrace();
}
return blocks;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void delete(Account account)
{
//GET ALL TRANSACTIONS THAT BELONG TO THAT ADDRESS
Map<Tuple2<String, String>, Block> accountBlocks = ((BTreeMap) this.map).subMap(
Fun.t2(account.getAddress(), null),
Fun.t2(account.getAddress(), Fun.HI()));
//DELETE TRANSACTIONS
for(Tuple2<String, String> key: accountBlocks.keySet())
{
this.delete(key);
}
}
public void delete(Block block)
{
this.delete(new Tuple2<String, String>(block.getGenerator().getAddress(), new String(block.getSignature())));
}
public void deleteAll(List<Account> accounts)
{
for(Account account: accounts)
{
this.delete(account);
}
}
public boolean add(Block block)
{
return this.set(new Tuple2<String, String>(block.getGenerator().getAddress(), new String(block.getSignature())), block);
}
public void addAll(Map<Account, List<Block>> blocks)
{
//FOR EACH ACCOUNT
for(Account account: blocks.keySet())
{
//FOR EACH TRANSACTION
for(Block block: blocks.get(account))
{
this.add(block);
}
}
}
}