URingPaxos-master/clients/dMap/src/java/ch/usi/da/dmap/local/TreeNode.java
package ch.usi.da.dmap.local;

import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

/*
 * Server process; should be addressable with port (multi instances per server)
 */
public class TreeNode<K extends Comparable<K>,V> implements Comparable<TreeNode<K,V>> {
	
	private TreeNode<K,V> parent = null;
	private final int order;
	private final int maxKeys;
	private K minKey = null; // or splitKey
	private TreeNode<K,V> nextNode = null;
	private final int maxChildren;
	private SortedSet<TreeNode<K,V>> children;
	private SortedMap<K,V> data;
	
	public TreeNode(TreeNode<K,V> parent,int order){
		this.parent = parent;
		this.order = order;
		this.maxKeys = order-1;
		this.maxChildren = order;
		this.children = new TreeSet<TreeNode<K,V>>();
		this.data = new TreeMap<K,V>();
	}
	
	public void setParent(TreeNode<K,V> node){
		parent = node;
	}

	public TreeNode<K,V> getParent(){
		return parent;
	}
	
	public Result<V> get(K k){
		if(children.isEmpty() && (nextNode == null || k.compareTo(nextNode.minKey) < 0)){ 
			return new Result<V>(true,data.get(k));
		}else{
			return new Result<V>(false,null);
		}
	}
		
	public K getMinKey(){
		return minKey;
	}
		
	public void setMinKey(K k){
		minKey = k;
	}
	
	public TreeNode<K,V> getNextNode(){
		return nextNode;
	}
	
	public void setNextNode(TreeNode<K,V> n){
		nextNode = n;
	}
	
	public SortedMap<K,V> getData(){
		return data;
	}
	
	public SortedSet<TreeNode<K, V>> getChildren(){
		return children;
	}
	
	public boolean put(K k,V v){
		boolean valid = false;
		if(minKey == null){
			minKey = k;
		}else if(k.compareTo(minKey) <= 0){
			minKey = k;
		}
		// test is leaf and max boundary (allow node split detection)
		if(children.isEmpty() && (nextNode == null || k.compareTo(nextNode.minKey) < 0)){ 
			data.put(k,v);
			valid = true;
		}
		if(data.size() > maxKeys){ // split data
			if(parent == null){ // root
				TreeNode<K,V> left = new TreeNode<K,V>(this,order);
				TreeNode<K,V> right = new TreeNode<K,V>(this,order);
				int middle = data.size()/2;
				@SuppressWarnings("unchecked")
				K splitKey = (K) data.keySet().toArray()[middle];
				SortedMap<K,V> lmap = new TreeMap<K,V>(data.headMap(splitKey));
				left.getData().putAll(lmap);
				left.setMinKey(lmap.firstKey());
				SortedMap<K,V> rmap = new TreeMap<K,V>(data.tailMap(splitKey));
				right.getData().putAll(rmap);
				right.setMinKey(rmap.firstKey());
				left.setNextNode(right);
				children.add(left);
				children.add(right);
				data.clear();
			}else{
				TreeNode<K,V> right = new TreeNode<K,V>(parent,order);
				int middle = data.size()/2;
				@SuppressWarnings("unchecked")
				K splitKey = (K) data.keySet().toArray()[middle];
				SortedMap<K,V> rmap = new TreeMap<K,V>(data.tailMap(splitKey));
				right.getData().putAll(rmap);
				right.setMinKey(rmap.firstKey());
				data = new TreeMap<K,V>(data.headMap(splitKey));
				right.setNextNode(this.getNextNode());
				this.setNextNode(right);
				minKey = data.firstKey();
				parent.addChild(right);
			}
		}
		return valid;
	}

	public void addChild(TreeNode<K,V> node){
		children.add(node);
		if(children.size() > maxChildren){ // split inner node
			if(parent == null){ // root
				TreeNode<K,V> left = new TreeNode<K,V>(this,order);
				TreeNode<K,V> right = new TreeNode<K,V>(this,order);
				int middle = children.size()/2;
				@SuppressWarnings("unchecked")
				TreeNode<K,V> splitKey = (TreeNode<K, V>) children.toArray()[middle];
				SortedSet<TreeNode<K,V>> lset = new TreeSet<TreeNode<K,V>>(children.headSet(splitKey));
				left.getChildren().addAll(lset);
				left.setMinKey(lset.first().getMinKey());
				for(TreeNode<K,V> n : left.getChildren()){
					n.setParent(left);
				}
				SortedSet<TreeNode<K,V>> rset = new TreeSet<TreeNode<K,V>>(children.tailSet(splitKey));
				right.getChildren().addAll(rset);
				right.setMinKey(rset.first().getMinKey());
				for(TreeNode<K,V> n : right.getChildren()){
					n.setParent(right);
				}
				children.clear();
				children.add(left);
				children.add(right);
			}else{
				TreeNode<K,V> right = new TreeNode<K,V>(parent,order);
				int middle = children.size()/2;
				@SuppressWarnings("unchecked")
				TreeNode<K,V> splitKey = (TreeNode<K, V>) children.toArray()[middle];
				SortedSet<TreeNode<K,V>> rset = new TreeSet<TreeNode<K,V>>(children.tailSet(splitKey));
				right.getChildren().addAll(rset);
				right.setMinKey(rset.first().getMinKey());
				for(TreeNode<K,V> n : right.getChildren()){
					n.setParent(right);
				}
				children = new TreeSet<TreeNode<K,V>>(children.headSet(splitKey));
				minKey = children.first().getMinKey();
				parent.addChild(right);
			}
		}
	}

	@Override
	public String toString(){
		StringBuffer buffer = new StringBuffer();
		if(data.size() > 0){
			buffer.append("D: " + data);
		}else{
			buffer.append("I: " + children);
		}
		//buffer.append(" (" + minKey + "/" + nextNode.getMinKey() + ")");
		return buffer.toString();
	}

	@Override
	public int compareTo(TreeNode<K,V> o) {
		return this.getMinKey().compareTo(o.getMinKey());
	}
	
}
