package biotree;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

//Need to add library

import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.json.simple.JSONObject;

public class WormsAPI {

	public static void main(String[] args) throws IOException, ParseException {
		// small test
		System.out.println(nameToID("Neogobius melanostomus"));
		/*
		 * TaxonNode[] taxnodes = idToClassification(126916);
		 * 
		 * for (int i =0; i < taxnodes.length;i++) {
		 * System.out.println(taxnodes[i].getTaxonId());
		 * System.out.println(taxnodes[i].getTaxonType());
		 * System.out.println(taxnodes[i].getName()); }
		 */

	}

	/**
	 * Search the WORMS database by scientific name to return Aphia (taxon) ID. This
	 * must be an exact name or it will fail. Use fuzzyNameToID if the name is not
	 * exact.
	 * 
	 * @param scientificName
	 *            Scientific name of taxon (family, genus, species, etc)
	 * @return Aphia (taxon) ID of given scientific name.
	 * @throws IOException
	 */
	public static Integer nameToID(String scientificName) throws IOException {
		scientificName = repSpaces(scientificName);
		String resp = makeRequest(
				String.format("http://marinespecies.org/rest/AphiaIDByName/%s?marine_only=false", scientificName));
		if (resp.length() == 0) return null;
		return Integer.parseInt(resp);
	}

	/**
	 * Search the WORMS database by fuzzy scientific name (a slightly misspelled
	 * name). This has the advantage of being more flexible but it can be less
	 * accurate and it is slower. If you have the actual scientific name, use
	 * nameToID() instead.
	 * 
	 * @param fuzzyName
	 *            Fuzzy scientific name of taxon (family, genus, species, etc)
	 * @return Aphia (taxon) ID of given scientific name.
	 * @throws IOException
	 */
	public static int fuzzyNameToID(String fuzzyName) throws IOException {
		fuzzyName = repSpaces(fuzzyName);
		String resp = makeRequest(String.format(
				"http://marinespecies.org/rest/AphiaRecordsByMatchNames?scientificnames%5B%5D=%s&marine_only=true",
				fuzzyName));

		// TODO: finish this function.
		return 123123;
	}

	/**
	 * Search by taxonId (AphiaID) and return bioclassification of that and above.
	 * 
	 * /AphiaClassificationByAphiaID/{ID}
	 * 
	 * @throws IOException
	 * @throws ParseException
	 */

	public static TaxonNode[] idToClassification(int taxonId) throws IOException, ParseException {
		String resp = makeRequest(
				String.format("http://marinespecies.org/rest/AphiaClassificationByAphiaID/%d", taxonId));
		if (resp.length() == 0) return null;
		JSONParser parser = new JSONParser();
		JSONObject json = (JSONObject) parser.parse(resp);

		// Assume length of 8 based on number of taxontypes
		TaxonNode[] taxnodes = new TaxonNode[8];
		int arraysize = parseIdCall(taxnodes, json, 0);
		TaxonNode[] copiedArray = new TaxonNode[arraysize];

		System.arraycopy(taxnodes, 0, copiedArray, 0, arraysize);

		return copiedArray;

	}

	/**
	 * Perform a GET request to the given URL and return the content of the
	 * response.
	 * 
	 * @param url
	 *            The URL to which to make a request.
	 * @return The content returned by the server (if successful).
	 * @throws IOException
	 */
	private static String makeRequest(String url) throws IOException {
		// Request method adapted from http://www.baeldung.com/java-http-request
		// create new URL instance
		URL urll = new URL(url);
		// create and set up connection
		HttpURLConnection con = (HttpURLConnection) urll.openConnection();
		con.setRequestMethod("GET");

		int status = con.getResponseCode();

		BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
		String inputLine;
		StringBuffer content = new StringBuffer();
		while ((inputLine = in.readLine()) != null) {
			content.append(inputLine);
		}
		in.close();
		con.disconnect();

		return content.toString();
	}

	/**
	 * Helper to replace a space with the correct one for URLs.
	 * 
	 * @param in
	 *            Input string
	 * @return String with spaces replaced by "%20" for spaces in URLs.
	 */
	private static String repSpaces(String in) {
		return in.replaceAll(" ", "%20");
	}

	/**
	 * Parses the json request recursively and also keeping track of the array
	 * length
	 * 
	 * @param nodes
	 *            the TaxonNode array
	 * @param current
	 *            current rank level of JSON file
	 * @param n
	 *            index of where the TaxonNode is being stored at
	 * @return arraysize number of elements added within array
	 */
	private static int parseIdCall(TaxonNode[] nodes, JSONObject current, int n) {
		boolean checktype = false;
		TaxonNode curNode = null;
		int arraysize = n;
		// Checks if rank matches the TaxonType Enum
		for (TaxonType c : TaxonType.values()) {

			if (c.name().equals((String) current.get("rank"))) {
				checktype = true;
				break;
			} else
				checktype = false;
		}
		JSONObject child = (JSONObject) current.get("child");

		if (checktype == true) {
			curNode = new TaxonNode((int) (long) current.get("AphiaID"), TaxonType.valueOf((String) current.get("rank")),
					(String) current.get("scientificname"));
			nodes[n] = curNode;
			n++;
		}
		// If child is null, return
		if ((JSONObject) current.get("child") == null)
			return n;
		arraysize = parseIdCall(nodes, child, n);
		return arraysize;
	}

}