package com.sissi.protocol.message;

import java.util.UUID;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import com.sissi.io.read.Collector;
import com.sissi.io.read.Metadata;
import com.sissi.protocol.Protocol;
import com.sissi.protocol.ProtocolType;
import com.sissi.protocol.error.ServerError;
import com.sissi.protocol.iq.data.XData;
import com.sissi.protocol.iq.data.XDataType;
import com.sissi.protocol.iq.data.XField;
import com.sissi.protocol.muc.XUser;
import com.sissi.protocol.offline.Delay;
import com.sissi.protocol.offline.History;

/**
 * @author kim 2013-10-27
 */
@Metadata(uri = Message.XMLNS, localName = Message.NAME)
@XmlRootElement
public class Message extends Protocol implements Collector {

	public final static String XMLNS = "jabber:client";

	public final static String NAME = "message";

	private boolean trace;

	private Body body;

	private XUser user;

	private XData data;

	private Delay delay;

	private Thread thread;

	private Subject subject;

	private History history;

	private AckRequest request;

	private AckReceived received;

	public Message() {
		this.trace = true;
	}

	private Message history(History history) {
		this.history = history;
		return this;
	}

	private Message request(AckRequest request) {
		this.request = request;
		return this;
	}

	private Message received(AckReceived received) {
		this.received = received;
		return this;
	}

	private Message x(Object x) {
		return XData.class == x.getClass() ? this.data(XData.class.cast(x)) : this.muc(XUser.class.cast(x));
	}

	public Message setType(MessageType type) {
		super.setType(type.toString());
		return this;
	}

	public String getType() {
		return super.getType() != null ? super.getType() : MessageType.NONE.toString();
	}

	public boolean type(MessageType... types) {
		for (MessageType type : types) {
			if (type.equals(this.getType())) {
				return true;
			}
		}
		return false;
	}

	public String getId() {
		return super.getId() != null || this.type(ProtocolType.ERROR, ProtocolType.RESULT) ? super.getId() : super.setId(UUID.randomUUID().toString()).getId();
	}

	public boolean delay() {
		return this.getDelay() != null;
	}

	@XmlElement
	public Delay getDelay() {
		return this.delay;
	}

	public Message delay(Delay delay) {
		this.delay = delay;
		return this;
	}

	public boolean body() {
		return this.getBody() != null;
	}

	public Message body(String body) {
		this.body = body != null ? new Body(body) : null;
		return this;
	}

	public Message body(Body body) {
		this.body = body;
		return this;
	}

	@XmlElement
	public Body getBody() {
		return this.body;
	}

	public String thread() {
		return this.getThread() != null ? this.getThread().getText() : null;
	}

	public Message noneThread() {
		this.trace = false;
		return this;
	}

	public Message thread(Thread thread) {
		this.thread = thread;
		return this;
	}

	public Message setThread(String thread) {
		this.thread = thread != null ? new Thread(thread) : null;
		return this;
	}

	@XmlElement
	public Thread getThread() {
		return this.thread != null && this.thread.content() ? this.thread : (this.trace ? new Thread(UUID.randomUUID().toString()) : null);
	}

	public boolean subject() {
		return this.getSubject() != null;
	}

	public Message subject(String subject) {
		this.subject = subject != null ? new Subject(subject) : null;
		return this;
	}

	public Message subject(Subject subject) {
		this.subject = subject;
		return this;
	}

	@XmlElement
	public Subject getSubject() {
		return this.subject != null && this.subject.content() ? this.subject : null;
	}

	@XmlElement
	public ServerError getError() {
		return super.getError();
	}

	public Message muc(XUser x) {
		this.user = x;
		return this;
	}

	@XmlElement(name = XUser.NAME)
	public XUser getMuc() {
		return this.user;
	}

	public boolean data(String name) {
		return this.getData() != null && this.getData().findField(name, XField.class) != null;
	}

	public boolean dataType(String type) {
		return this.getData() != null && this.getData().type(XDataType.parse(type));
	}

	public Message data(XData x) {
		this.data = x;
		return this;
	}

	@XmlElement(name = XData.NAME)
	public XData getData() {
		return this.data;
	}

	public boolean invite() {
		return this.getMuc() != null && this.getMuc().invite();
	}

	public boolean decline() {
		return this.getMuc() != null && this.getMuc().decline();
	}

	public boolean request() {
		return this.getRequest() != null;
	}

	public Message request(boolean request) {
		return this.request(request ? AckRequest.REQUEST : null);
	}

	@XmlElement(name = AckRequest.NAME)
	public AckRequest getRequest() {
		return this.request;
	}

	public boolean received() {
		return this.getReceived() != null;
	}

	@XmlElement(name = AckReceived.NAME)
	public AckReceived getReceived() {
		return this.received;
	}

	public boolean history() {
		return this.getHistory() != null;
	}

	public History getHistory() {
		return this.history;
	}

	/**
	 * 是否同时存在Received/Request(冲突)
	 * 
	 * @return
	 */
	public boolean noneConflict() {
		return !this.received() || !this.request();
	}

	/**
	 * 是否同时存在Received/Body且存在Received.id
	 * 
	 * @return
	 */
	public boolean validReceived() {
		return this.received() ? (this.getBody() == null && this.getReceived().valid()) : true;
	}

	public boolean content() {
		return this.body != null && this.body.content();
	}

	@Override
	public void set(String localName, Object ob) {
		switch (localName) {
		case XData.NAME:
			this.x(ob);
			return;
		case Body.NAME:
			this.body(Body.class.cast(ob));
			return;
		case Delay.NAME:
			this.delay(Delay.class.cast(ob));
			return;
		case Thread.NAME:
			this.thread(Thread.class.cast(ob));
			return;
		case Subject.NAME:
			this.subject(Subject.class.cast(ob));
			return;
		case History.NAME:
			this.history(History.class.cast(ob));
			return;
		case AckRequest.NAME:
			this.request(AckRequest.class.cast(ob));
			return;
		case AckReceived.NAME:
			this.received(AckReceived.class.cast(ob));
			return;
		}
	}
}

@@ -2313,16 +2313,50 @@
 .delay =
+ this.delay != null ? this.delay :
  delay;%0A

修改Message.delay策略 防止已存在的Delay被强制覆盖