/*
 * Copyright 2014 A.C.R. Development
 */
package acr.browser.lightning;

import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static android.util.Patterns.GOOD_IRI_CHAR;

/**
 * Web Address Parser
 * 
 * This is called WebAddress, rather than URL or URI, because it attempts to
 * parse the stuff that a user will actually type into a browser address widget.
 * 
 * Unlike java.net.uri, this parser will not choke on URIs missing schemes. It
 * will only throw a ParseException if the input is really hosed.
 * 
 * If given an https scheme but no port, fills in port
 */
public class WebAddress {

	private String mScheme;
	private String mHost;
	private int mPort;
	private String mPath;
	private String mAuthInfo;
	static final int MATCH_GROUP_SCHEME = 1;
	static final int MATCH_GROUP_AUTHORITY = 2;
	static final int MATCH_GROUP_HOST = 3;
	static final int MATCH_GROUP_PORT = 4;
	static final int MATCH_GROUP_PATH = 5;
	static Pattern sAddressPattern = Pattern.compile(
	/* scheme */"(?:(http|https|file)\\:\\/\\/)?" +
	/* authority */"(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
	/* host */"([" + GOOD_IRI_CHAR + "%_-][" + GOOD_IRI_CHAR + "%_\\.-]*|\\[[0-9a-fA-F:\\.]+\\])?" +
	/* port */"(?:\\:([0-9]*))?" +
	/* path */"(\\/?[^#]*)?" +
	/* anchor */".*", Pattern.CASE_INSENSITIVE);

	/**
	 * Parses given URI-like string.
	 */
	public WebAddress(String address) {

		if (address == null) {
			throw new IllegalArgumentException("address can't be null");
		}

		mScheme = "";
		mHost = "";
		mPort = -1;
		mPath = "/";
		mAuthInfo = "";

		Matcher m = sAddressPattern.matcher(address);
		String t;
		if (!m.matches()) {
			throw new IllegalArgumentException("Parsing of address '" + address + "' failed");
		}

		t = m.group(MATCH_GROUP_SCHEME);
		if (t != null) {
			mScheme = t.toLowerCase(Locale.ROOT);
		}
		t = m.group(MATCH_GROUP_AUTHORITY);
		if (t != null) {
			mAuthInfo = t;
		}
		t = m.group(MATCH_GROUP_HOST);
		if (t != null) {
			mHost = t;
		}
		t = m.group(MATCH_GROUP_PORT);
		if (t != null && !t.isEmpty()) {
			// The ':' character is not returned by the regex.
			try {
				mPort = Integer.parseInt(t);
			} catch (NumberFormatException ex) {
				throw new RuntimeException("Parsing of port number failed", ex);
			}
		}
		t = m.group(MATCH_GROUP_PATH);
		if (t != null && !t.isEmpty()) {
			/*
			 * handle busted myspace frontpage redirect with missing initial "/"
			 */
			if (t.charAt(0) == '/') {
				mPath = t;
			} else {
				mPath = '/' + t;
			}
		}

		/*
		 * Get port from scheme or scheme from port, if necessary and possible
		 */
		if (mPort == 443 && "".equals(mScheme)) {
			mScheme = "https";
		} else if (mPort == -1) {
			if ("https".equals(mScheme)) {
				mPort = 443;
			} else {
				mPort = 80; // default
			}
		}
		if ("".equals(mScheme)) {
			mScheme = "http";
		}
	}

	@Override
	public String toString() {

		String port = "";
		if ((mPort != 443 && "https".equals(mScheme)) || (mPort != 80 && "http".equals(mScheme))) {
			port = ':' + Integer.toString(mPort);
		}
		String authInfo = "";
		if (!mAuthInfo.isEmpty()) {
			authInfo = mAuthInfo + '@';
		}

		return mScheme + "://" + authInfo + mHost + port + mPath;
	}

	public void setScheme(String scheme) {
		mScheme = scheme;
	}

	public String getScheme() {
		return mScheme;
	}

	public void setHost(String host) {
		mHost = host;
	}

	public String getHost() {
		return mHost;
	}

	public void setPort(int port) {
		mPort = port;
	}

	public int getPort() {
		return mPort;
	}

	public void setPath(String path) {
		mPath = path;
	}

	public String getPath() {
		return mPath;
	}

	public void setAuthInfo(String authInfo) {
		mAuthInfo = authInfo;
	}

	public String getAuthInfo() {
		return mAuthInfo;
	}
}
