/*
 * This file is part of the DITA Open Toolkit project hosted on
 * Sourceforge.net. See the accompanying license.txt file for 
 * applicable licenses.
 */

/*
 * (c) Copyright IBM Corp. 2010 All Rights Reserved.
 */
package org.dita.dost.util;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.StringTokenizer;

/**
 * Corrects the URLs.
 */
public final class URLUtils {
	/**
	 * Corrects the file to URL.
	 * 
	 * @param file 
	 *            The file to be corrected. If null will throw
	 *            MalformedURLException.
	 * @return a corrected URL. Never null.
	 * @exception MalformedURLException
	 *                when the argument is null.
	 */
	public static URL correct(final File file) throws MalformedURLException {
		if (file == null) {
			throw new MalformedURLException("The url is null");
		}
		return new URL(correct(file.toURL().toString(), true));
	}

	/**
	 * Corrects an URL.
	 * 
	 * @param url
	 *            The URL to be corrected. If null will throw
	 *            MalformedURLException.
	 * @return a corrected URL. Never null.
	 * @exception MalformedURLException
	 *                when the argument is null.
	 */
	public static URL correct(final URL url) throws MalformedURLException {
		if (url == null) {
			throw new MalformedURLException("The url is null");
		}
		return new URL(correct(url.toString(), false));
	}

	/**
	 * Decode UTF8/URL encoded strings.
	 * 
	 * @param s
	 *            the string to be decoded
	 * @return the decoded string
	 */
	public static String uncorrect(final String s) {
		if (s == null) {
			return null;
		}
		if (s.indexOf('%') == -1) {
			// Optimization, nothing to uncorrect here
			return s;
		}
		final StringBuffer sbuf = new StringBuffer();
		final int l = s.length();
		int ch = -1;
		int b = 0, sumb = 0;
		boolean applyUTF8dec = false;

		for (int i = 0, more = -1; i < l; i++) {
			// Get next byte b from URL segment s
			final char current = s.charAt(i);
			ch = current;
			switch (ch) {
			case '%':
				if (i + 2 < s.length()) {
					// Avoid java.lang.StringIndexOutOfBoundsException...
					ch = s.charAt(++i);
					final int hb = (Character.isDigit((char) ch) ? ch - '0'
							: 10 + Character.toLowerCase((char) ch) - 'a') & 0xF;
					ch = s.charAt(++i);
					final int lb = (Character.isDigit((char) ch) ? ch - '0'
							: 10 + Character.toLowerCase((char) ch) - 'a') & 0xF;
					b = (hb << 4) | lb;
					applyUTF8dec = true;
				}
				break;
			default:
				b = ch;
			applyUTF8dec = false;
			}
			// Decode byte b as UTF-8, sumb collects incomplete chars
			if (applyUTF8dec) {
				if ((b & 0xc0) == 0x80) {
					// 10xxxxxx (continuation byte)
					sumb = (sumb << 6) | (b & 0x3f); // Add 6 bits to sumb
					if (--more == 0) {
						sbuf.append((char) sumb); // Add char to sbuf
					}
				} else if ((b & 0x80) == 0x00) {
					// 0xxxxxxx (yields 7 bits)
					sbuf.append((char) b); // Store in sbuf
				} else if ((b & 0xe0) == 0xc0) {
					// 110xxxxx (yields 5 bits)
					sumb = b & 0x1f;
					more = 1; // Expect 1 more byte
				} else if ((b & 0xf0) == 0xe0) {
					// 1110xxxx (yields 4 bits)
					sumb = b & 0x0f;
					more = 2; // Expect 2 more bytes
				} else if ((b & 0xf8) == 0xf0) {
					// 11110xxx (yields 3 bits)
					sumb = b & 0x07;
					more = 3; // Expect 3 more bytes
				} else if ((b & 0xfc) == 0xf8) {
					// 111110xx (yields 2 bits)
					sumb = b & 0x03;
					more = 4; // Expect 4 more bytes
				} else {
					sumb = b & 0x01;
					more = 5; // Expect 5 more bytes
				}
			} else {
				sbuf.append(current);
				// Do not expect other continuation.
				more = -1;
			}
			// We don't test if the UTF-8 encoding is well-formed
		}
		return sbuf.toString();
	}

	/**
	 * On Windows names of files from network neighborhood must be corrected
	 * before open.
	 * 
	 * @param url
	 *            The file URL.
	 * @return The canonical or absolute file, or null if the protocol is not
	 *         file.
	 */
	public static File getCanonicalFileFromFileUrl(final URL url) {
		File file = null;
		if (url == null) {
			throw new NullPointerException("The URL cannot be null.");
		}
		if ("file".equals(url.getProtocol())) {
			final String fileName = url.getFile();
			final String path = URLUtils.uncorrect(fileName);
			file = new File(path);

			try {
				file = file.getCanonicalFile();
			} catch (final IOException e) {
				// Does not exist.
				file = file.getAbsoluteFile();
			}

		}
		return file;
	}

	/**
	 * Method introduced to correct the URLs in the default machine encoding.
	 * @param url
	 *            The URL to be corrected. If it contains a % char, it means it
	 *            already was corrected, so it will be returned. Take care at
	 *            composing URLs from a corrected part and an uncorrected part.
	 *            Correcting the result will not work. Try to correct first the
	 *            relative part.
	 * @param forceCorrection
	 *            True if the correction must be executed any way (for files
	 *            containing % for example - the % will be also corrected). Also
	 *            if <code>true</code> '#' and '?' will be corrected otherwise
	 *            will consider that is an URL that contains an anchor or a
	 *            query.
	 * @return The corrected URL.
	 */
	public static String correct(String url, final boolean forceCorrection) {
		if (url == null) {
			return null;
		}

		final String initialUrl = url;

		// If there is a % that means the URL was already corrected.
		if (!forceCorrection && url.indexOf("%") != -1) {
			return initialUrl;
		}

		// Extract the reference (anchor) part from the URL. The '#' char identifying the anchor
		// must not be corrected.
		String reference = null;
		if (!forceCorrection) {
			final int refIndex = url.lastIndexOf('#');
			if (refIndex != -1) {
				reference = FilePathToURI.filepath2URI(url.substring(refIndex + 1));
				url = url.substring(0, refIndex);
			}
		}

		// Buffer where eventual query string will be processed.
		StringBuffer queryBuffer = null;
		if (!forceCorrection) {
			final int queryIndex = url.indexOf('?');
			if (queryIndex != -1) {
				// We have a query
				final String query = url.substring(queryIndex + 1);
				url = url.substring(0, queryIndex);
				queryBuffer = new StringBuffer(query.length());
				// Tokenize by &
				final StringTokenizer st = new StringTokenizer(query, "&");
				while (st.hasMoreElements()) {
					String token = st.nextToken();
					token = FilePathToURI.filepath2URI(token);
					// Correct token
					queryBuffer.append(token);
					if (st.hasMoreElements()) {
						queryBuffer.append("&");
					}
				}
			}
		}
		String toReturn = FilePathToURI.filepath2URI(url);

		if (queryBuffer != null) {
			// Append to the end the corrected query.
			toReturn += "?" + queryBuffer.toString();
		}

		if (reference != null) {
			// Append the reference to the end the corrected query.
			toReturn += "#" + reference;
		}
		return toReturn;
	}
	
	/**
	 * Convert a file name to url.
	 * @param fileName -
	 * 				The file name string.
	 * @return string -
	 * 				URL
	 * @author Zhang Di Hua
	 */
	public static String getURL(final String fileName){
		
		if(fileName.startsWith("file:/")){
			return fileName;
		}else{
			final File file = new File(fileName);
			try {
				return file.toURI().toURL().toString();
			} catch (final MalformedURLException e) {
				// TODO Auto-generated catch block
				//e.printStackTrace();
				return "";
			}
		}
		
	}

}