package com.pardot.rhombus.util.faker;
import com.datastax.driver.core.utils.UUIDs;
import com.google.common.collect.*;
import com.pardot.rhombus.Criteria;
import com.pardot.rhombus.RhombusException;
import com.pardot.rhombus.cobject.CDefinition;
import com.pardot.rhombus.cobject.CField;
import com.pardot.rhombus.cobject.CIndex;
import com.pardot.rhombus.cobject.CObjectOrdering;
import com.pardot.rhombus.cobject.shardingstrategy.*;
import java.math.BigInteger;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* User: Rob Righter
* Date: 3/13/14
*/
public class FakeCIndex {
private CIndex index;
private FakeIdRange uniqueRange = null;
private List<FakeCIndex> indexesThatIAmASubsetOf = null;
private Range<Long> wideRowCounterRange;
private List<String> nonIndexValuesOnAnyIndex;
private CDefinition def;
public FakeCIndex(CIndex index,
CDefinition def,
Object startId,
Long totalWideRows,
Long totalObjectsPerWideRange,
Long objectsPerShard ){
this.index = index;
this.indexesThatIAmASubsetOf = Lists.newArrayList();
// Each index's id range is represented by a fakeIdRange object, which represents all the ids for that index,
// across all the index's shards/wide rows
this.uniqueRange = new FakeIdRange(def.getPrimaryKeyCDataType(),startId,totalObjectsPerWideRange,objectsPerShard,index.getShardingStrategy(), index.getKey());
this.wideRowCounterRange = Range.closed(1L, totalWideRows);
this.def = def;
this.nonIndexValuesOnAnyIndex = Lists.newArrayList();
List<String> masterIndexValuesList = Lists.newArrayList();
for(CIndex i : def.getIndexes().values()){
masterIndexValuesList.addAll(i.getCompositeKeyList());
}
for(CField f : def.getFields().values()){
if(!masterIndexValuesList.contains(f.getName())){
this.nonIndexValuesOnAnyIndex.add(f.getName());
}
}
}
public boolean isCovering(CIndex otherIndex)
{
return this.index.getKey().contains(otherIndex.getKey());
}
public FakeIdRange getUniqueRange() {
return uniqueRange;
}
public void addCoveringIndex(FakeCIndex findex) {
indexesThatIAmASubsetOf.add(findex);
}
public Map<String, Object> makeObject(Long topCounter, FakeIdRange.IdInRange idInRange){
Map<String,Object> ret = Maps.newHashMap();
//set the Id
ret.put("id", idInRange.getId());
//set the index values
for(String indexValue : index.getCompositeKeyList()){
CField f = def.getField(indexValue);
ret.put(indexValue,getFieldValueAtCounter(topCounter,f));
}
//set the non-index values
for(String nonIndexValue : this.nonIndexValuesOnAnyIndex){
CField f = def.getField(nonIndexValue);
ret.put(nonIndexValue,getFieldValueAtCounter(idInRange.getCounterValue(),f));
}
return ret;
}
public Object getSuggestedIdForStartofNextIndex(TimebasedShardingStrategy shardingStrategyOfNextIndex) throws RhombusException {
return this.getUniqueRange().getSuggestedIdForStartofNextIndex(shardingStrategyOfNextIndex);
}
/**
*
* @return
*/
public Iterator<Map<String, Object>> getMasterIterator(CObjectOrdering ordering) throws RhombusException{
return getIterator(ordering, null, null);
}
public Iterator<Map<String, Object>> getIterator(CObjectOrdering ordering, Object startId, Object endId) throws RhombusException {
ContiguousSet<Long> wideRowCounterSet = ContiguousSet.create(this.wideRowCounterRange, DiscreteDomain.longs());
Iterator<Long> topLevelWideRowIterator = (ordering == CObjectOrdering.ASCENDING) ? wideRowCounterSet.iterator() : wideRowCounterSet.descendingIterator();
return new FakeCIndexIterator(topLevelWideRowIterator, this.getUniqueRange(), ordering, startId, endId);
}
/**
*
* @param key Key of object to get
* @return Object of type with key or null if it does not exist
*/
public Map<String, Object> getByKeyAndCounter(Long topLevelCounter, Object key) throws RhombusException {
Map<String,Object> ret = null;
if(uniqueRange.isIdInRange(key)){
return makeObject(topLevelCounter, uniqueRange.getIdInRangeAtCounter(uniqueRange.getCounterAtId(key)));
}
else {
for(FakeCIndex fr : this.indexesThatIAmASubsetOf){
if(fr.getUniqueRange().isIdInRange(key)){
return makeObject(topLevelCounter, fr.getUniqueRange().getIdInRangeAtCounter(uniqueRange.getCounterAtId(key)));
}
}
}
return null;
}
public Iterator<Map<String, Object>> list(String objectType, Criteria criteria) {
//TODO: get the counter range for this criteria and make an iterator for it
return null;
}
public Object getFieldValueAtCounter(Long counter, CField field){
switch (field.getType()) {
case ASCII:
case VARCHAR:
case TEXT:
return counter+"";
case INT:
return Integer.valueOf(counter.intValue());
case BIGINT:
case COUNTER:
return Long.valueOf(counter);
case BLOB:
throw new IllegalArgumentException();
case BOOLEAN:
return ((counter%2)==0)? Boolean.valueOf(true) : Boolean.valueOf(false);
case DECIMAL:
case FLOAT:
return Float.valueOf(counter.floatValue());
case DOUBLE:
return Double.valueOf(counter.floatValue());
case TIMESTAMP:
return new Date(counter);
case UUID:
case TIMEUUID:
return UUIDs.startOf(counter);
case VARINT:
return BigInteger.valueOf(counter);
default:
return null;
}
}
public class FakeCIndexIterator implements Iterator<Map<String,Object>> {
private FakeIdRange fRange;
private Iterator<FakeIdRange.IdInRange> rowIt;
private Iterator<Long> wideRowCounterIt;
private CObjectOrdering ordering;
Object startId;
Object endId;
Long currentWideRowCounter;
public FakeCIndexIterator(Iterator<Long> wideRowCounterIt, FakeIdRange fRange, CObjectOrdering ordering, Object startId, Object endId) throws RhombusException{
this.fRange = fRange;
this.ordering = ordering;
this.startId = startId;
this.endId = endId;
resetRowIt();
this.wideRowCounterIt = wideRowCounterIt;
this.currentWideRowCounter = wideRowCounterIt.next();
}
public void resetRowIt() throws RhombusException {
if((startId != null) && (endId != null)){
this.rowIt = fRange.getIterator(ordering,startId,endId);
}
else{
this.rowIt = fRange.getIterator(ordering);
}
}
public boolean hasNext(){
return rowIt.hasNext();
}
public Map<String,Object> next() {
if(rowIt.hasNext()){
FakeIdRange.IdInRange lastIdInRange = rowIt.next();
return makeObject(lastIdInRange.getCounterValue(),lastIdInRange);
}
else {
return null;
}
}
public void remove(){
wideRowCounterIt.remove();
rowIt.remove();
}
}
}