package org.arefgard.flow.core;

import org.arefgard.flow.node.DecisionNode;
import org.arefgard.flow.node.IncludeNode;
import org.arefgard.flow.node.InvokeNode;
import org.arefgard.flow.node.ReceiveNode;
import org.arefgard.flow.node.ReplyNode;
import org.arefgard.flow.node.ThrowNode;
import org.arefgard.flow.node.WaitNode;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

/**
 *  をパースするためのContentHandler。
 * 
 * @author Takashi Yamashina
 * @since 1.0.0
 */
public class FlowHandler implements ContentHandler {

	private static final String NODE_USECASE = "usecase";
	private static final String NODE_VARIABLE = "var";
	private static final String NODE_RECEIVE = "receive";
	private static final String NODE_REPLY = "reply";
	private static final String NODE_INVOKE = "invoke";
	private static final String NODE_DECISION = "decision";
	private static final String NODE_WAIT = "wait";
	private static final String NODE_THROW = "throw";
	private static final String NODE_INCLUDE = "include";
	private static final String NODE_LOAD = "load";
	private static final String NODE_TRANSFORM = "transform";
	
	private static final String NODE_PARAMETER = "parameter";
	private static final String NODE_NAVIGATION = "navigation";
	private static final String NODE_RETURN = "return";
	
	private static final String ATTR_CONTAINER = "container";
	private static final String ATTR_EXTENDS = "extends";
	private static final String ATTR_NAME = "name";
	private static final String ATTR_CLASS = "class";
	private static final String ATTR_NEXTTO = "nextTo";
	private static final String ATTR_TYPE = "type";
	private static final String ATTR_METHOD = "method";
	private static final String ATTR_TIME = "time";
	private static final String ATTR_PATH = "path";
	private static final String ATTR_INHERIT = "inherit";
	
	private UsecaseFlow flow;
	private String nowReceive;
	private String nowReply;
	private String nowInvoke;
	private String nowDecision;
	private String nowNavigation;
	
	public FlowHandler(UsecaseFlow flow) {
		this.flow = flow;
	}
	
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		if(this.nowDecision != null && this.nowNavigation != null) {
			DecisionNode now = (DecisionNode)this.flow.getNode(nowDecision);
			now.addProperty(new String(ch, start, length).trim(), this.nowNavigation);
		}
	}

	public void endDocument() throws SAXException {

	}

	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		if(qName.endsWith(NODE_RECEIVE)) {
			// <bean>
			this.nowReceive = null;
		}else if(qName.endsWith(NODE_REPLY)) {
			// <bean>
			this.nowReply = null;
		}else if(qName.endsWith(NODE_INVOKE)) {
			// <invoke>
			this.nowInvoke = null;
		}else if(qName.endsWith(NODE_DECISION)) {
			// <decision>
			this.nowDecision = null;
		}else if(this.nowDecision != null && qName.endsWith(NODE_NAVIGATION)) {
			this.nowNavigation = null;
		}
	}

	public void endPrefixMapping(String prefix) throws SAXException {

	}

	public void ignorableWhitespace(char[] ch, int start, int length)
			throws SAXException {

	}

	public void processingInstruction(String target, String data)
			throws SAXException {

	}

	public void setDocumentLocator(Locator locator) {

	}

	public void skippedEntity(String name) throws SAXException {

	}

	public void startDocument() throws SAXException {

	}

	public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
		if(qName.endsWith(NODE_USECASE)) {
			this.flow.setExtends(atts.getValue(ATTR_EXTENDS));
			if(this.flow.getExtends() != null) {
				UsecaseFlowDefinitionReader reader = new UsecaseFlowDefinitionReader(this.flow);
				try {
					reader.parse(this.flow.getExtends());
				}catch(Exception e) {
					new SAXException("Super usecase flow parse fail.", e);
				}
			}
			this.flow.setContainerPath(atts.getValue(ATTR_CONTAINER));
		}else if(qName.endsWith(NODE_VARIABLE )) {
			this.flow.addVar(atts.getValue(ATTR_NAME), atts.getValue(ATTR_CLASS));
		}else if(qName.endsWith(NODE_RECEIVE)) {
			// <receive>
			ReceiveNode node = new ReceiveNode();
			node.setName(atts.getValue(ATTR_NAME));
			node.setNextTo(atts.getValue(ATTR_NEXTTO));
			this.nowReceive = node.getName();
			this.flow.addNode(node);
		}else if(this.nowReceive != null && qName.endsWith(NODE_PARAMETER)) {
			ReceiveNode node = (ReceiveNode)this.flow.getNode(nowReceive);
			node.addProperty(atts.getValue(ATTR_NAME));
		}else if(qName.endsWith(NODE_REPLY)) {
			// <reply>
			ReplyNode node = new ReplyNode();
			node.setName(atts.getValue(ATTR_NAME));
			this.nowReply = node.getName();
			this.flow.addNode(node);
		}else if(this.nowReply != null && qName.endsWith(NODE_PARAMETER)) {
			ReplyNode node = (ReplyNode)this.flow.getNode(nowReply);
			node.addProperty(atts.getValue(ATTR_NAME));
		}else if(qName.endsWith(NODE_INVOKE)) {
			// <invoke>
			InvokeNode node = new InvokeNode();
			node.setName(atts.getValue(ATTR_NAME));
			node.setType(atts.getValue(ATTR_TYPE));
			node.setMethod(atts.getValue(ATTR_METHOD));
			node.setNextTo(atts.getValue(ATTR_NEXTTO));
			this.nowInvoke = node.getName();
			this.flow.addNode(node);
		}else if(this.nowInvoke != null && qName.endsWith(NODE_PARAMETER)) {
			InvokeNode node = (InvokeNode)this.flow.getNode(nowInvoke);
			node.addProperty(atts.getValue(ATTR_NAME));
		}else if(this.nowInvoke != null && qName.endsWith(NODE_RETURN)) {
			InvokeNode node = (InvokeNode)this.flow.getNode(nowInvoke);
			node.setReturn(atts.getValue(ATTR_NAME));
		}else if(qName.endsWith(NODE_DECISION)) {
			// <decision>
			DecisionNode node = new DecisionNode();
			node.setName(atts.getValue(ATTR_NAME));
			this.nowDecision = node.getName();
			this.flow.addNode(node);
		}else if(this.nowDecision != null && qName.endsWith(NODE_NAVIGATION)) {
			this.nowNavigation = atts.getValue(ATTR_NEXTTO);
		}else if(qName.endsWith(NODE_WAIT)) {
			// <wait>
			WaitNode node = new WaitNode();
			node.setName(atts.getValue(ATTR_NAME));
			node.setTime(Long.parseLong(atts.getValue(ATTR_TIME)));
			node.setNextTo(atts.getValue(ATTR_NEXTTO));
			this.flow.addNode(node);
		}else if(qName.endsWith(NODE_THROW)) {
			// <throw>
			ThrowNode node = new ThrowNode();
			node.setName(atts.getValue(ATTR_NAME));
			node.setClassName(atts.getValue(ATTR_CLASS));
		}else if(qName.endsWith(NODE_INCLUDE)) {
			// <include>
			IncludeNode node = new IncludeNode();
			node.setName(atts.getValue(ATTR_NAME));
			node.setPath(atts.getValue(ATTR_PATH));
			node.setInherit(Boolean.parseBoolean(atts.getValue(ATTR_INHERIT)));
			node.setNextTo(atts.getValue(ATTR_NEXTTO));
			this.flow.addNode(node);
		}else if(qName.endsWith(NODE_LOAD)) {
			
		}else if(qName.endsWith(NODE_TRANSFORM)) {
			
		}
	}

	public void startPrefixMapping(String prefix, String uri)
			throws SAXException {

	}

}
