package io.spacedog.model;

import org.joda.time.DateTime;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import io.spacedog.http.SpaceFields;
import io.spacedog.utils.Json;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonAutoDetect(fieldVisibility = Visibility.ANY, //
		getterVisibility = Visibility.NONE, //
		isGetterVisibility = Visibility.NONE, //
		setterVisibility = Visibility.NONE)
public abstract class DataObjectAbstract<K> implements DataObject<K>, SpaceFields {

	private String id;
	private String type;
	private long version;
	private Object[] sort;
	private float score;
	private boolean justCreated;

	public DataObjectAbstract() {
	}

	@Override
	public String id() {
		return id;
	}

	@Override
	public DataObjectAbstract<K> id(String id) {
		this.id = id;
		return this;
	}

	@Override
	public String type() {
		return type == null ? sourceClass().getSimpleName().toLowerCase() : type;
	}

	@Override
	public DataObjectAbstract<K> type(String type) {
		this.type = type;
		return this;
	}

	@Override
	public long version() {
		return version;
	}

	@Override
	public DataObjectAbstract<K> version(long version) {
		this.version = version;
		return this;
	}

	@Override
	public float score() {
		return score;
	}

	@Override
	public Object[] sort() {
		return sort;
	}

	@Override
	public boolean justCreated() {
		return this.justCreated;
	}

	@Override
	public DataObjectAbstract<K> justCreated(boolean created) {
		this.justCreated = created;
		return this;
	}

	@Override
	public String owner() {
		K source = source();
		if (source instanceof Metadata)
			return ((Metadata) source).owner();
		if (source instanceof ObjectNode) {
			JsonNode node = ((ObjectNode) source).get(OWNER_FIELD);
			return Json.isNull(node) ? null : node.asText();
		}
		return null;
	}

	@Override
	public void owner(String owner) {
		K source = source();
		if (source instanceof Metadata)
			((Metadata) source).owner(owner);
		else if (source instanceof ObjectNode)
			((ObjectNode) source).put(OWNER_FIELD, owner);
	}

	@Override
	public String group() {
		K source = source();
		if (source instanceof Metadata)
			return ((Metadata) source).group();
		if (source instanceof ObjectNode) {
			JsonNode node = ((ObjectNode) source).get(GROUP_FIELD);
			return Json.isNull(node) ? null : node.asText();
		}
		return null;
	}

	@Override
	public void group(String group) {
		K source = source();
		if (source instanceof Metadata)
			((Metadata) source).group(group);
		else if (source instanceof ObjectNode)
			((ObjectNode) source).put(GROUP_FIELD, group);
	}

	@Override
	public DateTime createdAt() {
		K source = source();
		if (source instanceof Metadata)
			return ((Metadata) source).createdAt();
		if (source instanceof ObjectNode)
			return Json.toPojo(((ObjectNode) source).get(CREATED_AT_FIELD), DateTime.class);
		return null;
	}

	@Override
	public void createdAt(DateTime createdAt) {
		K source = source();
		if (source instanceof Metadata)
			((Metadata) source).createdAt(createdAt);
		else if (source instanceof ObjectNode)
			((ObjectNode) source).put(CREATED_AT_FIELD, createdAt.toString());
	}

	@Override
	public DateTime updatedAt() {
		K source = source();
		if (source instanceof Metadata)
			return ((Metadata) source).updatedAt();
		if (source instanceof ObjectNode)
			return Json.toPojo(((ObjectNode) source).get(UPDATED_AT_FIELD), DateTime.class);
		return null;
	}

	@Override
	public void updatedAt(DateTime updatedAt) {
		K source = source();
		if (source instanceof Metadata)
			((Metadata) source).updatedAt(updatedAt);
		else if (source instanceof ObjectNode)
			((ObjectNode) source).put(UPDATED_AT_FIELD, updatedAt.toString());
	}

}

@@ -379,16 +379,53 @@
 Fields;%0A
+import io.spacedog.utils.Exceptions;%0A
 import i
@@ -447,16 +447,16 @@
 s.Json;%0A
-
 %0A@JsonIg
@@ -2295,16 +2295,146 @@
 owner);%0A
+%09%09else%0A%09%09%09throw Exceptions.unsupported(%22class %5B%25s%5D doesn't implement this operation%22, //%0A%09%09%09%09%09source.getClass().getSimpleName());%0A
 %09%7D%0A%0A%09@Ov
@@ -2964,16 +2964,146 @@
 group);%0A
+%09%09else%0A%09%09%09throw Exceptions.unsupported(%22class %5B%25s%5D doesn't implement this operation%22, //%0A%09%09%09%09%09source.getClass().getSimpleName());%0A
 %09%7D%0A%0A%09@Ov
@@ -3644,24 +3644,154 @@
 oString());%0A
+%09%09else%0A%09%09%09throw Exceptions.unsupported(%22class %5B%25s%5D doesn't implement this operation%22, //%0A%09%09%09%09%09source.getClass().getSimpleName());%0A
 %09%7D%0A%0A%09@Overri
@@ -4254,32 +4254,32 @@
 eof ObjectNode)%0A
-
 %09%09%09((ObjectNode)
@@ -4328,18 +4328,148 @@
 oString());%0A
+%09%09else%0A%09%09%09throw Exceptions.unsupported(%22class %5B%25s%5D doesn't implement this operation%22, //%0A%09%09%09%09%09source.getClass().getSimpleName());%0A
 %09%7D%0A%0A%7D%0A

Throw unsupported exceptions when data class doesn't handle metadata update