package gr.uoa.di.resourcediscovery;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import org.apache.log4j.Logger;

public class Toolkit {
	
	static transient Logger logger = Logger.getLogger(Toolkit.class);
	static int timeout = 10000;

	static public String makeAbsolute(String url, URL connectionUrl) throws MalformedURLException {
		return new URL(connectionUrl, url).toString();
	}

	static public String makeRelative(URL connectionUrl) throws MalformedURLException {
		return connectionUrl.getPath();
	}
	
	static public String getRedirectedUrl(String resourceURL, long sleepMillis) throws IOException, MalformedURLException {
		URL url = null;

		try {
			url = new URL(resourceURL);
		} catch (MalformedURLException mue) {
			logger.error("Error opening first url", mue);
			throw mue;
		}

		HttpURLConnection.setFollowRedirects(false);

		HttpURLConnection conn = null;
		try {
			Thread.sleep(sleepMillis);
			conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(timeout);
			conn.setReadTimeout(timeout);
			conn.setAllowUserInteraction(false);         
			conn.setDoOutput(true);
		} catch (ClassCastException ex) {
			throw new MalformedURLException();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		conn.setRequestMethod("HEAD");

		try {
			conn = openConnectionCheckRedirects(conn, sleepMillis);
		} catch (Exception ex) {
			throw new MalformedURLException();
		}

		try {
			Thread.sleep(sleepMillis);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		int statusCode = conn.getResponseCode();
		if (statusCode == 503) {
			logger.error("Url " + conn.getURL() + " reported status code 503. Please increase the crawler's sleep time.");
			conn.disconnect();

			throw new IOException("Url " + conn.getURL() + " reported status code 503. Please increase the crawler's sleep time.");
		} else if (conn.getResponseCode() >= 400) {
			// Client or server error received
			logger.error("Url " + conn.getURL() + " seems to be unreachable (response code:"+statusCode+"). If this url is not of importance you can ignore this error.");
			conn.disconnect();

			throw new IOException("Url " + conn.getURL() + " seems to be unreachable (response code:"+statusCode+"). If this url is not of importance you can ignore this error.");
		} else {
			return conn.getURL().toString();
		}
	}

	static public String getMimeType(String resourceURL, long sleepMillis) throws IOException, MalformedURLException {
		URL url = null;

		try {
			url = new URL(resourceURL);
		} catch (MalformedURLException mue) {
			logger.debug("Error getting mime type" + mue);
			throw mue;
		}

		HttpURLConnection.setFollowRedirects(false);

		HttpURLConnection conn = null;
		try {
			Thread.sleep(sleepMillis);
			conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(timeout);
			conn.setReadTimeout(timeout);
			conn.setAllowUserInteraction(false);         
			conn.setDoOutput(true);
		} catch (ClassCastException ex) {
			throw new MalformedURLException();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		conn.setRequestMethod("HEAD");

		try {
			conn = openConnectionCheckRedirects(conn, sleepMillis);
		} catch (Exception ex) {
			throw new MalformedURLException();
		}

		try {
			Thread.sleep(sleepMillis);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		int statusCode = conn.getResponseCode();
		if (statusCode == 503) {
			logger.error("WARNING: Url " + conn.getURL() + " reported status code 503. Please increase the crawler's sleep time.");
			conn.disconnect();

			return "unknown";
		} else if (conn.getResponseCode() >= 400) {
			// Client or server error received
			logger.error("WARNING: Url " + conn.getURL() + " seems to be unreachable (response code:"+statusCode+"). If this url is not of importance you can ignore this error.");
			conn.disconnect();

			return "unknown";
		} else {
			String mimeType = conn.getContentType();

			logger.debug("mime type for " + conn.getURL() + ": " + mimeType);
			logger.debug("response code was: " + statusCode);
			conn.disconnect();
			if (mimeType == null)
				mimeType = "unknown";
			return mimeType.replaceAll(";.*", "").trim();
		}
	}

	static public HttpURLConnection openConnectionCheckRedirects(URLConnection c, long sleepMillis) throws IOException {
		boolean redir;
		int redirects = 0;

		do {
			redir = false;
			if (c instanceof HttpURLConnection) {
				HttpURLConnection http = (HttpURLConnection) c;
				try {
					Thread.sleep(sleepMillis);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				int stat = http.getResponseCode();

				if (stat >= 300 && stat <= 307 && stat != 306 && stat != HttpURLConnection.HTTP_NOT_MODIFIED) {
					URL base = http.getURL();
					String loc = http.getHeaderField("Location");
					URL target = null;
					if (loc != null) {
						target = new URL(base, loc);
					}
					http.disconnect();
					// Redirection should be allowed only for HTTP and HTTPS
					// and should be limited to 5 redirections at most.
					if (target == null || !(target.getProtocol().equals("http") || target.getProtocol().equals("https")) || redirects >= 5) {
						throw new IOException("Redirection should be allowed only for HTTP and HTTPS and should be limited to 5 redirections at most.");
					}
					redir = true;
					try {
						Thread.sleep(sleepMillis);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					c = target.openConnection();
					c.setConnectTimeout(timeout);
					c.setReadTimeout(timeout);
					c.setAllowUserInteraction(false);         
					c.setDoOutput(true);
					redirects++;
				}
			}
		} while (redir);

		return (HttpURLConnection) c;
	}
}
