/* 
 * Copyright 2015 by AVM GmbH <info@avm.de>
 *
 * This software contains free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License ("License") as 
 * published by the Free Software Foundation  (version 3 of the License). 
 * This software 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 copy of the 
 * License you received along with this software for more details.
 */

package de.avm.android.tr064.exceptions;

import java.util.Locale;

import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.http.HttpEntity;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import android.text.TextUtils;

import de.avm.android.tr064.Tr064Log;

public class SoapException extends BaseException
{
	private static final String TAG = "SoapException"; 
	private static final long serialVersionUID = -5776510833030353384L;

	private static final String NAMESPACE_ENVELOPE = "http://schemas.xmlsoap.org/soap/envelope/";
	private static final String NAMESPACE_SOAP = "urn:dslforum-org:control-1-0";
	
	public static final String SOAPERROR_NOT_AUTHORIZED = "606";
	
	private static class Result
	{
		String mCode = null;
		String mMessage = null;
	}
	private String mSoapError = null;
	
	public String getSoapError()
	{
		return mSoapError;
	}
	
	/**
	 * Instantiates a new SOAP exception.
	 * 
	 * @param code
	 * 			UPnPError code
	 * 
	 * @param string
	 *          the string
	 */
	public SoapException(String code, String string)
	{
		super(string);
		mSoapError = code;
	}
	
	/**
	 * Instantiates a new SOAP exception.
	 * 
	 * @param code
	 * 			UPnPError code
	 * 
	 * @param string
	 *            the string
	 * @param e
	 *            the e
	 */
	public SoapException(String code, String string, Exception e)
	{
		super(string, e);
		mSoapError = code;
	}
	
	/**
	 * Creates instance from SOAP respone.
	 * 
	 * @param soapResponse
	 *          the SOAP response envelope with error
	 *          
	 * @return
	 * 			null if no SOAP error code has been extracted from argument
	 */
	public static SoapException create(HttpEntity soapResponse)
	{
		Result result = new Result();
		try
		{
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
			Document document = factory.newDocumentBuilder()
					.parse(soapResponse.getContent());
			NodeList nodes = document.getElementsByTagNameNS(NAMESPACE_ENVELOPE, "Fault");
			for (int ii = 0; TextUtils.isEmpty(result.mCode) && (ii < nodes.getLength()); ii++)
				fromFault(nodes.item(ii), result);
		}
		catch (Exception e)
		{
			Tr064Log.d(TAG, "No SOAP error code in HttpEntity", e);
			return null;
		}
		
		if (!TextUtils.isEmpty(result.mCode))
		{
			if (TextUtils.isEmpty(result.mMessage))
				result.mMessage = "UPnPError: " + result.mCode;
			return new SoapException(result.mCode, result.mMessage);
		}
		else Tr064Log.d(TAG, "No SOAP error code in HttpEntity");

		return null;
	}

	private static void fromFault(Node nodeFault, Result result)
	{
		if (nodeFault == null) return;
		
		NodeList nodes = nodeFault.getChildNodes();
		boolean upnpError = false;
		Node nodeDetail = null;
		for (int ii = 0;
				(!upnpError || (nodeDetail == null)) && (ii < nodes.getLength());
				ii++)
		{
			Node node = nodes.item(ii);
			if (isNode(node, null, "faultstring"))
			{
				if ("UPnPError".equals(getTextContent(node))) upnpError = true;
			}
			else if (isNode(node, null, "detail"))
			{
				nodeDetail = node;
			}
		}
		
		if (upnpError && (nodeDetail != null))
		{
			nodes = nodeDetail.getChildNodes();
			for (int ii = 0;  ii < nodes.getLength(); ii++)
			{
				Node node = nodes.item(ii);
				if (isNode(node, NAMESPACE_SOAP,"upnperror"))
				{
					NodeList nodesError = node.getChildNodes();
					for (int jj = 0;  jj < nodesError.getLength(); jj++)
					{
						Node nodeError = nodesError.item(jj);
						if (isNode(nodeError, NAMESPACE_SOAP, "errorCode"))
							result.mCode = getTextContent(nodeError);
						else if (isNode(nodeError, NAMESPACE_SOAP, "errorDescription"))
							result.mMessage = getTextContent(nodeError);
					}
					if (TextUtils.isEmpty(result.mCode))
						result.mMessage = null;
					else
						break;
				}
			}
		}
	}
	
	private static boolean isNode(Node node, String namespace, String name)
	{
		if (node.getNodeType() != Node.ELEMENT_NODE)
			return false;
		
		if (!TextUtils.isEmpty(namespace) &&
				!namespace.equals(node.getNamespaceURI()))
			return false;

		return (name.toLowerCase(Locale.US).equals(node.getLocalName()
						.toLowerCase(Locale.US)));
	}
	
	private static String getTextContent(Node node)
	{
		// first text value
		NodeList nodes = node.getChildNodes();
		for (int ii = 0; ii < nodes.getLength(); ii++)
		{
			Node childNode = nodes.item(ii);
			if (childNode.getNodeType() == Node.TEXT_NODE)
				return childNode.getNodeValue();
		}
		return "";
	}
}