package info.bukova.isspst.services.addressbook;

import info.bukova.isspst.data.Address;
import info.bukova.isspst.services.IsspstException;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.springframework.util.xml.SimpleNamespaceContext;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class AddressFinderTaxID implements AddressFinder {
	
	private String aresDicUrl;
	private Address resAddr;

	@Override
	public synchronized List<Address> findAddress(Address condAddress) throws IsspstException {
		ArrayList<Address> resList = new ArrayList<Address>();
		resAddr = new Address();
		resAddr.setIc(condAddress.getIc());
		InputStream is = null;
		try {
			is	= getAresResponse(condAddress);
			parseDic(is);
			is.close();
		} catch (HttpException e) {
			throw new IsspstException();
		} catch (IOException e) {
			throw new IsspstException();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				throw new IsspstException("Cannot close stream", e.getCause());
			}
		}
		resList.add(resAddr);
		return resList;
	}
	
	private InputStream getAresResponse(Address condAddress) throws IsspstException, HttpException, IOException {
		GetMethod httpGet = new GetMethod(aresDicUrl);
		HttpClient httpCli = new HttpClient();
		
		httpGet.setQueryString("ico=" + String.valueOf(condAddress.getIc()));
		int res = httpCli.executeMethod(httpGet);
				
		if (res != 200) {
			throw new IsspstException("Server returned error code " + String.valueOf(res));
		}
		
		return httpGet.getResponseBodyAsStream();
	}
	
	private void parseDic(InputStream is) throws IsspstException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);
		try {
			DocumentBuilder docBuilder = dbf.newDocumentBuilder();
			Document doc = docBuilder.parse(is);
			
			Map<String, String> xmlNs = new HashMap<String, String>();
			xmlNs.put("are", "http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_answer_basic/v_1.0.3");
			xmlNs.put("D", "http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_datatypes/v_1.0.3");
			xmlNs.put("U", "http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/uvis_datatypes/v_1.0.3");
			SimpleNamespaceContext nsCtx = new SimpleNamespaceContext();
			nsCtx.setBindings(xmlNs);
			
			XPathFactory factory = XPathFactory.newInstance();
			XPath xpath = factory.newXPath();
			xpath.setNamespaceContext(nsCtx);
			XPathExpression exp = xpath.compile("/are:Ares_odpovedi/are:Odpoved/D:VBAS/D:DIC");
			String dic = (String) exp.evaluate(doc, XPathConstants.STRING);
			exp = xpath.compile("/are:Ares_odpovedi/are:Odpoved/D:VBAS/D:OF");
			String company = (String) exp.evaluate(doc, XPathConstants.STRING);
			exp = xpath.compile("/are:Ares_odpovedi/are:Odpoved/D:VBAS/D:AA/D:NU");
			String street = (String) exp.evaluate(doc, XPathConstants.STRING);
			exp = xpath.compile("/are:Ares_odpovedi/are:Odpoved/D:VBAS/D:AA/D:CD");
			String num = (String) exp.evaluate(doc, XPathConstants.STRING);
			exp = xpath.compile("/are:Ares_odpovedi/are:Odpoved/D:VBAS/D:AA/D:CO");
			String numOr = (String) exp.evaluate(doc, XPathConstants.STRING);
			exp = xpath.compile("/are:Ares_odpovedi/are:Odpoved/D:VBAS/D:AA/D:N");
			String city = (String) exp.evaluate(doc, XPathConstants.STRING);
			exp = xpath.compile("/are:Ares_odpovedi/are:Odpoved/D:VBAS/D:AA/D:PSC");
			String zip = (String) exp.evaluate(doc, XPathConstants.STRING);
			exp = xpath.compile("/are:Ares_odpovedi/are:Odpoved/D:VBAS/D:AA/D:NS");
			String state = (String) exp.evaluate(doc, XPathConstants.STRING);
						
			resAddr.setDic(dic);
			resAddr.setCompany(company);
			resAddr.setStreet(street);
			if (numOr != null && !numOr.isEmpty())
				num = num + "/" + numOr;
			resAddr.setHouseNumber(num);
			resAddr.setCity(city);
			resAddr.setZipCode(zip);
			resAddr.setState(state);
			
		} catch (ParserConfigurationException e) {
			throw new IsspstException("ARES: parese error while finding Tax ID", e.getCause());
		} catch (SAXException e) {
			throw new IsspstException("ARES: SAX error while finding Tax ID", e.getCause());
		} catch (IOException e) {
			throw new IsspstException("ARES: IO error while finding Tax ID", e.getCause());
		} catch (XPathExpressionException e) {
			throw new IsspstException("ARES: xpath compile error", e.getCause());
		}
		
	}

	public String getAresDicUrl() {
		return aresDicUrl;
	}

	public void setAresDicUrl(String aresDicUrl) {
		this.aresDicUrl = aresDicUrl;
	}

}
