package com.rosaloves.bitlyj;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.rosaloves.bitlyj.Bitly.Provider;
import com.rosaloves.bitlyj.data.Pair;
import com.rosaloves.bitlyj.utils.Dom;

/**
 *
 * $Id$
 *
 * @author clewis Jul 17, 2010
 *
 */
class SimpleProvider implements Provider {

	private final String url;

	private final String user;

	private final String apiKey;

	private final String endPoint;

	SimpleProvider(String url, String user, String apiKey, String endPoint) {
		super();
		this.url = url;
		this.user = user;
		this.apiKey = apiKey;
		this.endPoint = endPoint;
	}

	public <A> A call(BitlyMethod<A> m) {
		String url = getUrlForCall(m);
		Document response = filterErrorResponse(fetchUrl(url));
		return m.apply(this, response);
	}

	public String getUrl() {
		return this.url;
	}

	@Override
	public String toString() {
		return "SimpleProvider [apiKey=" + apiKey + ", endPoint=" + endPoint
				+ ", url=" + url + ", user=" + user + "]";
	}

	protected String getUrlForCall(BitlyMethod<?> m) {
		StringBuilder sb = new StringBuilder(endPoint)
			.append(m.getName() + "?")
			.append("&login=").append(user)
			.append("&apiKey=").append(apiKey)
			.append("&format=xml");

			try {
				for(Pair<String, String> p : m.getParameters()) {
					sb.append("&" + p.getOne() + "=" + URLEncoder.encode(p.getTwo(), "UTF-8"));
				}
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException(e);
			}

		return sb.toString();
	}

	private Document filterErrorResponse(Document doc) {
		Node statusCode = doc.getElementsByTagName("status_code").item(0);
		Node statusText = doc.getElementsByTagName("status_txt").item(0);

		if(statusCode == null || statusText == null) {
			throw new BitlyException("Unexpected response (no status and/or message)!");
		}

		int code = Integer.parseInt(Dom.getTextContent(statusCode));
		if(code == 200)
			return doc;
		else {
			throw new BitlyException(Dom.getTextContent(statusText));
		}
	}

	private Document fetchUrl(String url) {

	    HttpURLConnection openConnection = null;

		try {
			openConnection = (HttpURLConnection) new URL(url).openConnection();
			if(openConnection.getResponseCode() == 200)
				return DocumentBuilderFactory.newInstance()
					.newDocumentBuilder().parse(openConnection.getInputStream());
			else
				throw new BitlyException("Transport error! "
						+ openConnection.getResponseCode() + " "
						+ openConnection.getResponseMessage());
		} catch (IOException e) {
			throw new BitlyException("Transport I/O error!", e);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {

		    if (openConnection != null) {

		        openConnection.disconnect();
		    }
		}
	}

}
