/*
 *  Copyright (C) 2006  Takashi Kasuya <kasuya@sfc.keio.ac.jp>
 *
 * This library is free software; you can redistribute it and/or
 *　modify it under the terms of the GNU Lesser General Public
 *　License as published by the Free Software Foundation; either
 *　version 2.1 of the License, or (at your option) any later version.
 *　This library is distributed in the hope that it will be useful,
 *　but WITHOUT ANY WARRANTY; without even the implied warranty of
 *　MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *　Lesser General Public License for more details.
 *
 *　You should have received a copy of the GNU Lesser General Public
 *　License along with this library; if not, write to the Free Software
 *　Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package jp.ac.naka.ec.sip.pidf;

import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.parser.StringMsgParser;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.ParseException;

import javax.sip.address.SipURI;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import jp.ac.naka.ec.Location;
import jp.ac.naka.ec.entity.EntityImpl;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * 
 * @author Takashi Kasuya
 * 
 */
public class PIDFParserImpl implements ErrorHandler {

	private static DocumentBuilder db;

	private static PIDFParserImpl instance = new PIDFParserImpl();

	/**
	 * 
	 * 
	 */
	private PIDFParserImpl() {

	}

	/**
	 * 
	 * @return
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 */
	public static PIDFParserImpl getInatance() throws ParserConfigurationException,
			SAXException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		// 検証を行う
		// dbf.setValidating(true);
		// ドキュメントビルダーを生成
		db = dbf.newDocumentBuilder();
		db.setErrorHandler(instance);
		return instance;
	}

	
	public PIDFData parse(String pidf) throws SAXException, ParseException, IOException {
		return parse(pidf.getBytes());
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see jp.ac.naka.ec.sip.pidf.PIDFParser#parse(byte[])
	 */
	public PIDFData parse(byte[] content) throws SAXException, ParseException,
			IOException {
		//System.out.println(new String(content,"UTF-8"));
		Document doc = db.parse(new ByteArrayInputStream(content), "UTF-8");
		Element root = doc.getDocumentElement();
		NamedNodeMap oAttrs = root.getAttributes();
		String entity = oAttrs.getNamedItem("entity").getNodeValue();
		if (entity == null || entity.equals("null")) {
			throw new NullPointerException("Entity attribute is null.");
		}

		SipURI uri = createSipURI(entity);
		PIDFData data = new PIDFData(uri);
		// tupleタグの処理
		NodeList list = root.getElementsByTagName("tuple");
		procTuple(list, data);

		// noteタグの処理
		Node node = root.getLastChild();

		while (node.getNodeType() == Node.ELEMENT_NODE
				&& node.getNodeName().equals("note")) {
			data.addNote(getNote(node));
			node = node.getPreviousSibling();
		}
		

		// 他Extensionsの処理
		
		// messageタグ
		NodeList nodes = root.getElementsByTagName("message");
		node = nodes.item(0);
		if (node != null) {
			Node t = node.getFirstChild();
			String msg = t.getNodeValue();
			data.setMessage(msg);
		}
		// locationタグ
		nodes = root.getElementsByTagName("location");
		node = nodes.item(0);
		if (node != null) {
			Node t = node.getFirstChild();
			String name = t.getFirstChild().getNodeValue();
			t = t.getNextSibling();
			String lat = t.getFirstChild().getNodeValue();
			t = t.getNextSibling();
			String lon = t.getFirstChild().getNodeValue();
			Location location = new Location(name, lat, lon);
			data.setLocation(location);
			if (nodes.item(1) != null) {
				System.err.println("Invalid Location tag, Ignored.");
			}
		}
		return data;
	}

	public static javax.sip.address.SipURI createSipURI(String uri)
	//  throws java.net.URISyntaxException {
	throws ParseException {
		if (uri == null)
			throw new NullPointerException("null URI");
		try {
			StringMsgParser smp = new StringMsgParser();
			SipUri sipUri = smp.parseSIPUrl(uri);
			return (SipURI) sipUri;
		} catch (ParseException ex) {
			//  throw new java.net.URISyntaxException(uri, ex.getMessage());
			throw new ParseException(ex.getMessage(), 0);
		}

	}

	/**
	 * 
	 * @param list
	 * @param data
	 * @throws SAXException
	 * @throws ParseException
	 */
	private void procTuple(NodeList list, PIDFData data) throws SAXException,
			ParseException {
		NodeList tList, eList;
		Node element1, element2;

		for (int i = 0; i < list.getLength(); i++) {
			Tuple tuple = new Tuple();
			Element element = (Element) list.item(i);

			// tuple idの取得
			NamedNodeMap oAttrs = element.getAttributes();
			String tupleId = oAttrs.getNamedItem("id").getNodeValue();

			if (tupleId == null) {
				throw new IllegalArgumentException("No tuple id.");
			}

			tuple.id = tupleId;
			tList = element.getChildNodes();
			// tupleタグの処理
			for (int j = 0; j < tList.getLength(); j++) {
				element1 = tList.item(j);
				if (!(element1.getNodeType() == Node.ELEMENT_NODE)) {
					continue;
				}

				// statusタグの処理
				if (element1.getNodeName().equals("status")) {
					eList = element1.getChildNodes();
					for (int k = 0; k < eList.getLength(); k++) {
						element2 = eList.item(k);
						if (!(element2.getNodeType() == Node.ELEMENT_NODE)) {
							continue;
						}
						String nodeName = element2.getNodeName();
						if (nodeName.equals("basic")) {
							String basic = element2.getFirstChild()
									.getNodeValue();
							if (basic.equals("OPEN")) {
								tuple.status.basic = Tuple.Basic.OPEN;
							} else {
								tuple.status.basic = Tuple.Basic.CLOSE;
							}
						} else if (nodeName.equals("alive")) {
							String alive = element2.getFirstChild()
									.getNodeValue();
							if (alive.indexOf("YES") != -1) {
								tuple.setAlive(true);
							} else {
								tuple.setAlive(false);
							}
						}

						// 自己定義タグ
						else {
							// tuple.(nodeName,
							// element2.getFirstChild().getNodeValue());
						}
					}
				}

				// contactタグの処理
				else if (element1.getNodeName().equals("contact")) {
					SipURI uri = new SipUri();
					String temp = element1.getFirstChild().getNodeValue();
					java.util.StringTokenizer stToken = new java.util.StringTokenizer(
							temp, "@:");
					String scheme = stToken.nextToken();
					if (!scheme.equals("sip") && !scheme.equals("pres")) {
						throw new IllegalArgumentException("Bad uri scheme :"
								+ scheme);
					}
					String user = stToken.nextToken();
					String host = stToken.nextToken();
					int port = Integer.parseInt(stToken.nextToken());
					uri.setUser(user);
					uri.setHost(host);
					uri.setPort(port);
					// tuple.name = user;
					tuple.contact = uri;
				}

				// noteの処理
				else if (element1.getNodeName().equals("note")) {
					tuple.addNote(element1.getFirstChild().getNodeValue());
				}
				// timestampの処理
				else if (element1.getNodeName().equals("timestamp")) {
					tuple.timestamp = element1.getFirstChild().getNodeValue();
				}
				// 自己定義タグの処理
				else {
					tuple.putExtension(element1.getNodeName(), element1
							.getFirstChild().getNodeValue());
				}
			}
			data.addTuple(tuple);
		}
	}

	private String getNote(Node node) {
		return node.getFirstChild().getNodeValue();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
	 */
	public void error(SAXParseException e) throws SAXException {
		e.printStackTrace();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
	 */
	public void fatalError(SAXParseException e) throws SAXException {
		e.printStackTrace();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
	 */
	public void warning(SAXParseException e) throws SAXException {
		e.printStackTrace();
	}
	
	public static void main (String[] args) throws ParserConfigurationException, SAXException {
		String pidf = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><presence xmlns=\"urn:ietf:params:xml:ns:pidf\" entity=\"sip:1kasuya@localhost:5060\"><tuple id=\"1kasuya0000\"><status><alive>YES</alive></status><keyword>button,hoge</keyword><name>1kasuya</name><type>SENSOR</type><contact>sip:1kasuya@localhost:5060</contact><timestamp>2007-04-108T13:46:08Z</timestamp></tuple><message>test</message></presence>";
		PIDFParserImpl parse = PIDFParserImpl.getInatance();
		try {
			PIDFData data = parse.parse(pidf);
			System.out.println(new EntityImpl(data));
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	

}
