package database;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.mapdb.BTreeMap;
import org.mapdb.Bind;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.Fun;
import org.mapdb.Fun.Tuple2;
import org.mapdb.Fun.Tuple3;
import qora.assets.Order;
import qora.assets.Trade;
import utils.ObserverMessage;
import database.DBSet;
import database.serializer.TradeSerializer;
public class TradeMap extends DBMap<Tuple2<BigInteger, BigInteger>, Trade>
{
private Map<Integer, Integer> observableData = new HashMap<Integer, Integer>();
@SuppressWarnings("rawtypes")
private BTreeMap pairKeyMap;
@SuppressWarnings("rawtypes")
private BTreeMap reverseKeyMap;
public TradeMap(DBSet databaseSet, DB database)
{
super(databaseSet, database);
this.observableData.put(DBMap.NOTIFY_ADD, ObserverMessage.ADD_TRADE_TYPE);
this.observableData.put(DBMap.NOTIFY_REMOVE, ObserverMessage.REMOVE_TRADE_TYPE);
//this.observableData.put(DBMap.NOTIFY_LIST, ObserverMessage.LIST_ORDER_TYPE);
}
public TradeMap(TradeMap parent)
{
super(parent);
}
protected void createIndexes(DB database){}
@Override
protected Map<Tuple2<BigInteger, BigInteger>, Trade> getMap(DB database)
{
//OPEN MAP
return this.openMap(database);
}
@Override
protected Map<Tuple2<BigInteger, BigInteger>, Trade> getMemoryMap()
{
DB database = DBMaker.newMemoryDB().make();
//OPEN MAP
return this.openMap(database);
}
@SuppressWarnings("unchecked")
private Map<Tuple2<BigInteger, BigInteger>, Trade> openMap(final DB database)
{
//OPEN MAP
BTreeMap<Tuple2<BigInteger, BigInteger>, Trade> map = database.createTreeMap("trades")
.valueSerializer(new TradeSerializer())
.makeOrGet();
//CHECK IF NOT MEMORY DATABASE
if(databaseSet != null)
{
//PAIR KEY
this.pairKeyMap = database.createTreeMap("trades_key_pair")
.comparator(Fun.COMPARATOR)
.makeOrGet();
//BIND PAIR KEY
Bind.secondaryKey(map, this.pairKeyMap, new Fun.Function2<Tuple3<String, Long, Tuple2<BigInteger, BigInteger>>, Tuple2<BigInteger, BigInteger>, Trade>()
{
@Override
public Tuple3<String, Long, Tuple2<BigInteger, BigInteger>> run(Tuple2<BigInteger, BigInteger> key, Trade value)
{
Order order = value.getInitiatorOrder((DBSet) databaseSet);
long have = order.getHave();
long want = order.getWant();
String pairKey;
if(have > want)
{
pairKey = have + "/" + want;
}
else
{
pairKey = want + "/" + have;
}
return new Tuple3<String, Long, Tuple2<BigInteger, BigInteger>>(pairKey, Long.MAX_VALUE - value.getTimestamp(), key);
}
});
//REVERSE KEY
this.reverseKeyMap = database.createTreeMap("trades_key_reverse")
.comparator(Fun.COMPARATOR)
.makeOrGet();
//BIND REVERSE KEY
Bind.secondaryKey(map, this.reverseKeyMap, new Fun.Function2<Tuple2<BigInteger, BigInteger>, Tuple2<BigInteger, BigInteger>, Trade>()
{
@Override
public Tuple2<BigInteger, BigInteger> run(Tuple2<BigInteger, BigInteger> key, Trade value)
{
return new Tuple2<BigInteger, BigInteger>(key.b, key.a);
}
});
Bind.secondaryKey(map, this.reverseKeyMap, new Fun.Function2<Tuple2<BigInteger, BigInteger>, Tuple2<BigInteger, BigInteger>, Trade>()
{
@Override
public Tuple2<BigInteger, BigInteger> run(Tuple2<BigInteger, BigInteger> key, Trade value)
{
return new Tuple2<BigInteger, BigInteger>(key.a, key.b);
}
});
}
//RETURN
return map;
}
@Override
protected Trade getDefaultValue()
{
return null;
}
@Override
protected Map<Integer, Integer> getObservableData()
{
return this.observableData;
}
public void add(Trade trade)
{
this.set(new Tuple2<BigInteger, BigInteger>(trade.getInitiator(), trade.getTarget()), trade);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection<Tuple2> getKeys(Order order) {
Map uncastedMap = this.map;
//FILTER ALL KEYS
Collection<Tuple2> keys = ((BTreeMap<Tuple2, Order>) uncastedMap).subMap(
Fun.t2(order.getId(), null),
Fun.t2(order.getId(), Fun.HI())).keySet();
//IF THIS IS A FORK
if(this.parent != null)
{
//GET ALL KEYS FOR FORK
Collection<Tuple2> forkKeys = ((TradeMap) this.parent).getKeys(order);
//COMBINE LISTS
Set<Tuple2> combinedKeys = new TreeSet<Tuple2>(keys);
combinedKeys.addAll(forkKeys);
//DELETE DELETED
for(Tuple2 deleted: this.deleted)
{
combinedKeys.remove(deleted);
}
//CONVERT SET BACK TO COLLECTION
keys = combinedKeys;
}
return keys;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<Trade> getInitiatedTrades(Order order)
{
//FILTER ALL TRADES
Collection<Tuple2> keys = this.getKeys(order);
//GET ALL TRADES FOR KEYS
List<Trade> trades = new ArrayList<Trade>();
for(Tuple2 key: keys)
{
trades.add(this.get(key));
}
//RETURN
return trades;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SortableList<Tuple2<BigInteger, BigInteger>, Trade> getTrades(Order order)
{
//ADD REVERSE KEYS
Collection<Tuple2<BigInteger, BigInteger>> keys = ((BTreeMap<Tuple2, Tuple2<BigInteger, BigInteger>>) this.reverseKeyMap).subMap(
Fun.t2(order.getId(), null),
Fun.t2(order.getId(), Fun.HI())).values();
//RETURN
return new SortableList<Tuple2<BigInteger, BigInteger>, Trade>(this, keys);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SortableList<Tuple2<BigInteger, BigInteger>, Trade> getTradesSortableList(long have, long want)
{
String pairKey;
if(have > want)
{
pairKey = have + "/" + want;
}
else
{
pairKey = want + "/" + have;
}
//FILTER ALL KEYS
Collection<Tuple2<BigInteger, BigInteger>> keys = ((BTreeMap<Tuple3, Tuple2<BigInteger, BigInteger>>) this.pairKeyMap).subMap(
Fun.t3(pairKey, null, null),
Fun.t3(pairKey, Fun.HI(), Fun.HI())).values();
//RETURN
return new SortableList<Tuple2<BigInteger, BigInteger>, Trade>(this, keys);
}
public void delete(Trade trade)
{
this.delete(new Tuple2<BigInteger, BigInteger>(trade.getInitiator(), trade.getTarget()));
}
}