package org.w3c.addressing.sax;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.HashSet;

import org.biomoby.shared.MobyException;
import org.biomoby.w3c.addressing.EndpointReference;
import org.tulsoft.shared.GException;
import org.tulsoft.tools.debug.DGUtils;
import org.tulsoft.tools.xml.XMLUtils2;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

public class EndpointReferenceParser extends DefaultHandler implements EprTags {

	
	private static String[] eventNamesArray = {
		ADDR,
		REF_PARAMETERS
	};

	private static HashSet<String> eventNames = new HashSet<String>();
	static {
		for (int i = 0; i < eventNamesArray.length; i++) {
			eventNames.add(eventNamesArray[i]);
		}
	}

	// parsing helpers
	private boolean parsingEpr = false;
	private boolean parsingAddress = false;
	private boolean parsingReferenceParameters = false;
	private boolean parsingSvcId = false;
	private StringBuffer addressBuf, idBuf;
	private Locator locator;

	private XMLReader parser = null;

	// the EPR object
	private EndpointReference result = null;
	
	public EndpointReferenceParser() {
		parsingEpr = false;
		parsingAddress = false;
		parsingReferenceParameters = false;
		parsingSvcId = false;
	}

	/***************************************************************************
	 * 
	 * XML-SAX 2.0 handler routines.
	 * 
	 **************************************************************************/

	/***************************************************************************
	 * Called at the beginning of the parsed document.
	 **************************************************************************/
	public void startDocument() throws SAXException {
		addressBuf = new StringBuffer();
		idBuf= new StringBuffer();
	}

	/***************************************************************************
	 * Called at the end of the parsed document.
	 **************************************************************************/
	public void endDocument() throws SAXException {
		parsingEpr = false;
		parsingAddress = false;
		parsingReferenceParameters = false;
		parsingSvcId = false;
		
		if (this.result != null) {
			this.result.setAddress(addressBuf.toString());
			this.result.setServiceInvocationId(idBuf.toString());
		}
		idBuf = null;
		addressBuf = null;
	}

	/***************************************************************************
	 * Called when an element starts.
	 **************************************************************************/
	public void startElement(String namespaceURI, String name, String qName,
			Attributes attrs) throws SAXException {
		if (name.equals(EPR)) {
			parsingEpr = true;
			result = new EndpointReference();
			return;
		}
		
		if (parsingEpr) {
			if (eventNames.contains(name)) {
				if (name.equals(ADDR)) {
					parsingAddress = true;
					return;
				}
				if (name.equals(REF_PARAMETERS)) {
					parsingReferenceParameters = true;
					return;
				}
			}
			
			if (parsingReferenceParameters) {
				if (name.equals(SVC_ID)) {
					parsingSvcId = true;
					return;
				}
			}
			
		}
	}

	/***************************************************************************
	 * Called when an element ends.
	 **************************************************************************/
	public void endElement(String namespaceURI, String name, String qName)
			throws SAXException {
		if (name.equals(ADDR)) {
			parsingAddress = false;
			parsingSvcId = false;
			return;
		}
		if (name.equals(SVC_ID)) {
			parsingSvcId = false;
			parsingAddress = false;
			return;
		}
		if (name.equals(REF_PARAMETERS)) {
			parsingSvcId = false;
			parsingAddress = false;
			parsingReferenceParameters = false;
			return;
		}
		if (name.equals(EPR)) {
			parsingSvcId = false;
			parsingAddress = false;
			parsingReferenceParameters = false;
			parsingEpr = false;
			return;
		}
	
	}

	/***************************************************************************
	 * Set document locator. There is usually no need to use it from outside the
	 * parser.
	 **************************************************************************/
	public void setDocumentLocator(Locator l) {
		this.locator = l;
	}

	/***************************************************************************
	 * Called for #PCDATA.
	 **************************************************************************/
	public void characters(char[] ch, int start, int length) {
		StringBuffer sb = new StringBuffer();
		sb.append(ch,start, length);
		if (parsingAddress) {
			addressBuf.append(sb.toString().trim());
			return;
		}
		if (parsingSvcId) {
			idBuf.append(sb.toString().trim());
			return;
		}
	}

	/***************************************************************************
	 * Called when an error occurs.
	 **************************************************************************/
	protected SAXParseException error(String message) {
		return new SAXParseException("", locator, new MobyException(message));
	}

	/***************************************************************************
	 * Parse the contents of the given file.
	 **************************************************************************/
	public EndpointReference parse(String xmlFilename) throws MobyException {
		return _parse(new InputSource(xmlFilename));
	}

	/***************************************************************************
	 * Parse the contents coming from the given input stream.
	 **************************************************************************/
	public EndpointReference parse(InputStream xml) throws MobyException {
		return _parse(new InputSource(xml));
	}

	/***************************************************************************
	 * Parse the contents coming from the given reader.
	 **************************************************************************/
	public EndpointReference parse(Reader xmlReader) throws MobyException {
		return _parse(new InputSource(xmlReader));
	}

	private synchronized EndpointReference _parse(InputSource xmlSource)
			throws MobyException {

		try {
			// Create a parser and register handlers (do it only once)
			if (parser == null)
				parser = XMLUtils2.makeXMLParser(this);

			// Parse it!
			parser.parse(xmlSource);
			if (result == null) {
				throw new MobyException(
						"Parsing XML failed, and I do not know why. \n"
								+ "Panic... (or send the XML to a jMoby developer)\n");
			}
			return result;

		} catch (GException e) {
			throw new MobyException("Error in creating XML parser "
					+ e.getMessage());

		} catch (SAXException e) {
			throw new MobyException("Error in the XML input.\n"
					+ XMLUtils2.getFormattedError(e));
		} catch (IOException e) {
			throw new MobyException("Error by reading XML input: "
					+ e.toString());

		} catch (Error e) {
			throw new MobyException("Serious or unexpected error!\n"
					+ e.toString() + "\n" + DGUtils.stackTraceToString(e));
		}
	}
}
