package spimedb;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.node.ArrayNode;
import jcog.tree.rtree.point.DoubleND;
import org.eclipse.collections.impl.factory.Iterables;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import spimedb.util.JSON;
import java.util.TreeSet;
import java.util.function.Supplier;
/**
* dimensions: time (t), lon (x), lat (y), alt (z), ...
* <p>
* https://github.com/FasterXML/jackson-annotations/
* <p>
* https://github.com/infinispan/infinispan/blob/master/query/src/test/java/org/infinispan/query/test/Person.java
* https://github.com/infinispan/infinispan/blob/master/query/src/test/java/org/infinispan/query/queries/spatial/QuerySpatialTest.java#L78
*/
@JsonSerialize(using = NObject.NObjectSerializer.class)
public class MutableNObject extends ImmutableNObject {
public MutableNObject() {
this(SpimeDB.uuidString());
}
public MutableNObject(String id) {
this(id, null);
}
public MutableNObject(NObject copy) {
super(copy.bounded() ? copy.min() : unbounded, copy.bounded() ? copy.max() : unbounded, copy.id(), copy.name());
copy.forEach(this::put);
}
public MutableNObject(String id, String name) {
super(DoubleND.fill(4, Float.NEGATIVE_INFINITY), DoubleND.fill(4, Float.POSITIVE_INFINITY), id, name);
}
public MutableNObject putLater(String key, float pri, Supplier lazy) {
return putLater(key, pri, null, lazy);
}
public MutableNObject putLater(String key, float pri, Object intermediate, Supplier lazy) {
return put(key, new LazyValue(key, intermediate, pri, lazy));
}
public MutableNObject put(String key, Object value) {
switch (key) {
case TAG:
String[] tt;
if (value instanceof String[])
tt = ((String[]) value).clone();
else if (value instanceof String)
tt = ((String)value).split(" "); //new String[] { (String) value };
else if (value instanceof ArrayNode) {
tt = JSON.toStrings((ArrayNode)value);
} else
throw new RuntimeException("invalid tag property");
withTags(tt);
return this;
// case "<":
// //HACK
// setOutside(Sets.newHashSet((String[])value));
// return this;
case ID:
if ((value instanceof String) && (value.equals(id))) {
//already being set to same ID
return this;
}
throw new RuntimeException(this + " can not change ID");
case NAME:
name(value.toString());
return this;
}
synchronized (data) {
if (value == null) {
data.remove(key);
} else {
data.put(key, value);
}
}
return this;
}
public MutableNObject description(String d) {
if (d.isEmpty()) {
if (data != null)
data.remove(DESC);
} else {
put(DESC, d);
}
return this;
}
public MutableNObject name(String name) {
this.name = name;
return this;
}
public MutableNObject withTags(String... tags) {
return withTags(Iterables.iList(tags));
}
/**
* sets the inside property
*/
public MutableNObject withTags(Iterable<? extends String> tags) {
TreeSet<String> s = new TreeSet();
for (String t : tags) {
if (t.equals(id()))
throw new RuntimeException("object can not be inside itself");
s.add(t);
}
if (s.isEmpty()) {
this.tag = null;
} else {
this.tag = s.toArray(new String[s.size()]);
}
return this;
}
public MutableNObject when(long when) {
min.coord[0] = max.coord[0] = when;
return this;
}
public MutableNObject when(long start, long end) {
min.coord[0] = start;
max.coord[0] = end;
return this;
}
public void when(float when) {
min.coord[0] = when;
max.coord[0] = when;
}
public void setEternal() {
min.coord[0] = Float.NEGATIVE_INFINITY;
max.coord[0] = Float.POSITIVE_INFINITY;
}
public void where(float x, float y) {
min.coord[1] = x;
max.coord[1] = x;
min.coord[2] = y;
max.coord[2] = y;
}
public void where(float x, float y, float z) {
where(x, y);
if (z!=z) { //avoid NaN
min.coord[3] = Float.NEGATIVE_INFINITY;
max.coord[3] = Float.POSITIVE_INFINITY;
} else {
min.coord[3] = z;
max.coord[3] = z;
}
}
@Deprecated
public long[] whenLong() {
double a = min.coord[0];
double b = max.coord[0];
return new long[]{(long) a, (long) b};
}
@Nullable
public <X> X remove(String key) {
if (data != null)
return (X) data.remove(key);
return null;
}
public Object /* previous */ putIfAbsent(String key, @NotNull Object value) {
return data.putIfAbsent(key, value);
}
public MutableNObject without(String k) {
remove(k);
return this;
}
public MutableNObject putAll(JsonNode x) {
x.fields().forEachRemaining(e -> {
JsonNode v = e.getValue();
Object s;
if (v.isBoolean())
s = v.booleanValue();
else if (v.isNumber()/* || v.canConvertToInt()*/)
s = v.numberValue();
else if (v.isTextual())
s = v.textValue();
else
s = v.toString();
put(e.getKey(), s);
});
return this;
}
// /** produces a "1-line" summar JSON object as a string */
// @JsonIgnore public String summary(StringBuilder sb) {
// sb.setLength(0);
//
// sb.append("{\"I\":\"").append(getId()).append('"');
//
// String name = getName();
// if (name!=null) {
// //TODO use nars Utf8
// sb.append(",\"N\":\"").append(name).append('"');
// }
//
//// if (isSpatial())
//// sb.append(",\"S\":").append(Arrays.toString(spacetime)); //TODO append
//// if (isTemporal())
//// sb.append(",\"T\":").append(Arrays.toString(time)); //TODO append
//
// sb.append('}');
//
// return sb.toString();
// }
}