package com.chap.memo.memoNodes.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import com.chap.memo.memoNodes.MemoNode;
import com.chap.memo.memoNodes.MemoUtils;
import com.chap.memo.memoNodes.bus.MemoReadBus;
import com.chap.memo.memoNodes.bus.MemoWriteBus;
import com.eaio.uuid.UUID;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ObjectArrays;
public class ArcList {
MemoReadBus readBus = MemoReadBus.getBus();
MemoWriteBus writeBus = MemoWriteBus.getBus();
long lastUpdate = 0;
// UUID[] nodes = new UUID[0];
ArrayList<ArcOp> arcops = null;
int type; // 0: parent list, 1:child list
UUID nodeId;
private long timestamp = 0;
private static final UuidCmp cmp = new UuidCmp();
public ArcList(UUID nodeId, int type) {
this.type = type;
this.nodeId = nodeId;
}
public long getTimestamp_long() {
return timestamp;
}
public ImmutableList<MemoNode> getNodes() {
Builder<MemoNode> listBuilder = new ImmutableList.Builder<MemoNode>();
for (UUID id : getNodesIds()) {
listBuilder.add(new MemoNode(id));
}
return listBuilder.build();
}
public UUID[] getNodesIds(){
synchronized (this) {
if (readBus.opsChanged(lastUpdate)) this.arcops = null;
if (this.arcops == null) {
this.arcops = readBus.getOps(nodeId, type, 0);
}
// if (this.arcops == null || readBus.opsChanged(lastUpdate)) {
// if (this.arcops == null) {
// this.arcops = readBus.getOps(nodeId, type, 0);
// } else {
// this.arcops
// .addAll(readBus.getOps(nodeId, type, lastUpdate));
// }
lastUpdate = System.currentTimeMillis();
// }
if (arcops == null){
return new UUID[0];
}
HashMap<UUID,ArcOp> list = new HashMap<UUID,ArcOp>(arcops.size() / 2);
for (ArcOp op : arcops) {
if (op.timestamp > this.timestamp) this.timestamp = op.timestamp;
ArcOp otherOp = list.get(op.get(this.type));
if (otherOp != null){
if (op.timestamp<otherOp.timestamp){
continue;
}
}
list.put(op.get(this.type),op);
}
List<UUID> result = new ArrayList<UUID>(list.size());
for (ArcOp op : list.values()){
if (op.getType() == OpsType.ADD){
result.add(op.get(this.type));
}
}
Collections.sort(result,cmp);
UUID[] res = result.toArray(new UUID[0]);
if (result.size()>1){
int pivot = Math.abs(Collections.binarySearch(result, nodeId, cmp));
if (pivot < res.length){
UUID[] front = Arrays.copyOfRange(res, pivot, res.length);
UUID[] tail = Arrays.copyOf(res, pivot);
// System.out.println("Pivot:"+pivot+"/"+res.length+":"+result.size()+" -> "+front.length+":"+tail.length);
res = ObjectArrays.concat(front,tail,UUID.class);
}
}
if (result.size() != res.length) System.out.println("Error: Lost a child!"+res.length+":"+result.size());
return res;
}
}
public void addNode(UUID other) {
if (this.arcops == null) {
getNodesIds();
}
UUID[] arc = new UUID[2];
arc[this.type] = other;
arc[Math.abs(this.type - 1)] = this.nodeId;
ArcOp op = new ArcOp(OpsType.ADD, arc, System.currentTimeMillis());
writeBus.store(op);
synchronized (this) {
arcops.add(op);
}
}
public void delNode(UUID other) {
if (this.arcops == null) {
getNodesIds();
}
UUID[] arc = new UUID[2];
arc[this.type] = other;
arc[Math.abs(this.type - 1)] = this.nodeId;
ArcOp op = new ArcOp(OpsType.DELETE, arc, System.currentTimeMillis());
writeBus.store(op);
synchronized (this) {
arcops.add(op);
}
}
public int getLength() {
return getNodesIds().length;
}
public void clear() {
UUID[] nodes = getNodesIds();
arcops.ensureCapacity(arcops.size() + nodes.length);
for (UUID other : nodes) {
this.delNode(other);
}
}
}
class UuidCmp implements Comparator<UUID>{
public int compare(UUID a,UUID b) {
return a.time==b.time?0:(MemoUtils.gettime(a)>MemoUtils.gettime(b)?1:-1);
}
}