/*
 * Copyright 2006 Maskat Project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.maskat.framework.eventdef.xml;

import java.io.Writer;
import java.util.Iterator;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.maskat.framework.BasicDef;
import org.maskat.framework.IBasicDef;
import org.maskat.framework.eventdef.Bind;
import org.maskat.framework.eventdef.Component;
import org.maskat.framework.eventdef.Event;
import org.maskat.framework.eventdef.EventRef;
import org.maskat.framework.eventdef.Header;
import org.maskat.framework.eventdef.Layout;
import org.maskat.framework.eventdef.Param;
import org.maskat.framework.eventdef.RemoteUrl;
import org.maskat.framework.eventdef.Result;
import org.maskat.framework.eventdef.Source;
import org.maskat.framework.eventdef.Target;
import org.maskat.framework.screendef.DescDef;
import org.maskat.xml.DefXmlSerializer;

public class EventDefXmlSerializer extends DefXmlSerializer {

	public EventDefXmlSerializer(Object def, Writer writer) {
		super(def, writer);
	}

	protected void customizeWrite(XMLStreamWriter writer) throws XMLStreamException {
		writer.writeDTD("<!DOCTYPE eventDef SYSTEM \"eventDef.dtd\">");
	}

	protected String getElementName(Object def) {
		if (def instanceof DescDef) {
			return "desc";
		}
		if (def instanceof Layout) {
			return "eventDef";
		}
		if (def instanceof RemoteUrl) {
			return "remoteUrl";
		}
		if (def instanceof Header) {
			return "header";
		}
		if (def instanceof Component) {
			return "component";
		}
		if (def instanceof EventRef) {
			return "eventRef";
		}
		if (def instanceof Event) {
			return "event";
		}
		if (def instanceof Param) {
			return "param";
		}
		if (def instanceof Source) {
			return "source";
		}
		if (def instanceof Bind) {
			return "bind";
		}
		if (def instanceof Result) {
			return "result";
		}
		if (def instanceof Target) {
			return "target";
		}
		return null;
	}

	protected void writeDefAttribute(XMLStreamWriter writer, Object def)
			throws XMLStreamException {
		if (def instanceof Layout) {
			Layout theDef = (Layout) def;
			writeOneAttribute(writer, "id", theDef.getId());
		}
		if (def instanceof RemoteUrl) {
			RemoteUrl theDef = (RemoteUrl) def;
			writeOneAttribute(writer, "url", theDef.getUrl());
		}
		if (def instanceof Header) {
			Header theDef = (Header) def;
			writeOneAttribute(writer, "name", theDef.getName());
			writeOneAttribute(writer, "value", theDef.getValue());
		}
		if (def instanceof Component) {
			Component theDef = (Component) def;
			writeOneAttribute(writer, "id", theDef.getId());
		}
		if (def instanceof EventRef) {
			EventRef theDef = (EventRef) def;
			writeOneAttribute(writer, "id", theDef.getRefid());
		}
		if (def instanceof Event) {
			Event theDef = (Event) def;
			writeOneAttribute(writer, "id", theDef.getId());
			writeOneAttribute(writer, "ref", theDef.getRef());
			writeOneAttribute(writer, "start", theDef.getStart());
			writeOneAttribute(writer, "before", theDef.getBefore());
			writeOneAttribute(writer, "after", theDef.getAfter());
			writeOneAttribute(writer, "finish", theDef.getFinish());
			writeOneAttribute(writer, "type", theDef.getType());
			writeOneAttribute(writer, "async", theDef.getAsync());
			writeOneAttribute(writer, "remoteUrl", theDef.getRemoteUrl());
			writeOneAttribute(writer, "timeout", theDef.getTimeout());
			writeOneAttribute(writer, "onTimeoutError", theDef.getOnTimeoutError());
		}
		if (def instanceof Param) {
			Param theDef = (Param) def;
			writeOneAttribute(writer, "rootNode", theDef.getRootNode());
			writeOneAttribute(writer, "ns", theDef.getNs());
			writeOneAttribute(writer, "soap", theDef.getSoap());
		}
		if (def instanceof Source) {
			Source theDef = (Source) def;
			writeOneAttribute(writer, "obj", theDef.getObj());
			writeOneAttribute(writer, "node", theDef.getNode());
			writeOneAttribute(writer, "idxRef", theDef.getIdxRef());
			writeOneAttribute(writer, "childNode", theDef.getChildNode());
			writeOneAttribute(writer, "fromkey", theDef.getFromkey());
			writeOneAttribute(writer, "type", theDef.getType());
			writeOneAttribute(writer, "desc", theDef.getDesc());
			writeOneAttribute(writer, "min", theDef.getMin());
			writeOneAttribute(writer, "max", theDef.getMax());
			writeOneAttribute(writer, "regexp", theDef.getRegexp());
			writeOneAttribute(writer, "teleType", theDef.getTeleType());
			writeOneAttribute(writer, "sendBlankElement", theDef.getSendBlankElement());
		}
		if (def instanceof Bind) {
			Bind theDef = (Bind) def;
			writeOneAttribute(writer, "fromkey", theDef.getFromkey());
			writeOneAttribute(writer, "tokey", theDef.getTokey());
			writeOneAttribute(writer, "node", theDef.getNode());
			writeOneAttribute(writer, "sendBlankElement", theDef.getSendBlankElement());
		}
		if (def instanceof Result) {
			Result theDef = (Result) def;
			writeOneAttribute(writer, "rootNode", theDef.getRootNode());
			writeOneAttribute(writer, "onErrorTele", theDef.getOnErrorTele());
			writeOneAttribute(writer, "soap", theDef.getSoap());
		}
		if (def instanceof Target) {
			Target theDef = (Target) def;
			writeOneAttribute(writer, "out", theDef.getOut());
			writeOneAttribute(writer, "in", theDef.getIn());
			writeOneAttribute(writer, "inkey", theDef.getInkey());
			writeOneAttribute(writer, "type", theDef.getType());
			writeOneAttribute(writer, "teleType", theDef.getTeleType());
			writeOneAttribute(writer, "workType", theDef.getWorkType());
		}

	}

	private boolean isNotEmpty(String str) {
		return str == null || str.trim().equals("") ? false : true;
	}

	private boolean isBindEmpty(Bind bind) {
		if (isNotEmpty(bind.getSendBlankElement()) || isNotEmpty(bind.getNode())
				|| isNotEmpty(bind.getFromkey()) || isNotEmpty(bind.getTokey())) {
			return false;
		}
		return true;
	}

	protected void writeObj(XMLStreamWriter writer, Object def, int hierarchyLvl)
			throws XMLStreamException {

		if (def instanceof Header) {
			Header header = (Header) def;
			if (!(isNotEmpty(header.getName()) || isNotEmpty(header.getValue()))) {
				return;
			}
		}
		if (def instanceof Bind) {
			Bind bind = (Bind) def;
			if (isBindEmpty(bind)) {
				return;
			}
		}
		if (def instanceof Source) {
			Source source = (Source) def;
			boolean hasAttributes = false;
			if (isNotEmpty(source.getObj()) || isNotEmpty(source.getNode())
					|| isNotEmpty(source.getIdxRef())
					|| isNotEmpty(source.getChildNode())
					|| isNotEmpty(source.getFromkey()) || isNotEmpty(source.getType())
					|| isNotEmpty(source.getDesc()) || isNotEmpty(source.getMin())
					|| isNotEmpty(source.getMax()) || isNotEmpty(source.getRegexp())
					|| isNotEmpty(source.getTeleType())
					|| isNotEmpty(source.getSendBlankElement())) {
				hasAttributes = true;
			}
			boolean hasChildren = source.getAllChildren() == null ? false : true;
			boolean hasChildToOutput = false;
			if (hasChildren) {
				// check if the source has bind child
				// which is not empty or other children
				for (Iterator it = source.getAllChildren(); it.hasNext();) {
					Object obj = it.next();
					if (obj instanceof Bind) {
						if (!isBindEmpty((Bind) obj)) {
							hasChildToOutput = true;
							break;
						}
					} else {
						hasChildToOutput = true;
						break;
					}
				}
			}
			if ((!hasAttributes) && (!hasChildToOutput)) {
				// if this source is empty don't output it.
				return;
			}
		}
		if (def instanceof Target) {
			Target target = (Target) def;
			boolean hasAttributes = false;
			if (isNotEmpty(target.getOut()) || isNotEmpty(target.getIn())
					|| isNotEmpty(target.getInkey()) || isNotEmpty(target.getType())
					|| isNotEmpty(target.getTeleType())
					|| isNotEmpty(target.getWorkType())) {
				hasAttributes = true;
			}
			boolean hasChildren = target.getAllChildren() == null ? false : true;
			boolean hasChildToOutput = false;
			if (hasChildren) {
				// check if the target has bind child
				// which is not empty or other children
				for (Iterator it = target.getAllChildren(); it.hasNext();) {
					Object obj = it.next();
					if (obj instanceof Bind) {
						if (!isBindEmpty((Bind) obj)) {
							hasChildToOutput = true;
							break;
						}
					} else {
						hasChildToOutput = true;
						break;
					}
				}
			}
			if ((!hasAttributes) && (!hasChildToOutput)) {
				// if this target is empty don't output it.
				return;
			}
		}

		writer.writeCharacters("\n");
		writeIndent(writer, hierarchyLvl);
		writer.writeStartElement(getElementName(def));

		// write attribute
		writeDefAttribute(writer, def);

		if (def instanceof DescDef) {
			writer.writeCData(((DescDef) def).getComment());
		}

		boolean hasChildren = false;
		if (def instanceof BasicDef) {
			for (Iterator it = ((IBasicDef) def).getAllChildren(); it != null
					&& it.hasNext();) {
				hasChildren = true;
				Object next = it.next();
				writeObj(writer, next, hierarchyLvl + 1);
			}
		}

		if (hasChildren) {
			writer.writeCharacters("\n");
			writeIndent(writer, hierarchyLvl);
		}

		writer.writeEndElement();
	}
}