package com.jscape.inet.util;

import java.net.MalformedURLException;
import java.util.Locale;
import java.util.Properties;


/**
 * This class enables URL parsing for non-registered protocols.
 * By default, many Java implementations would throw exception if one tries to
 * run the following code:<br>
 * <code>
 * URL url = new URL("https://www.jscape.com/");
 * </code>
 * In order to use the above code, a protocol handler must be installed
 * for protocol 'https'. This is a procedure that is normally not allowed to client
 * applet or application due to security restrictions.<br>
 * Hence, this class provides the code necessary to parse the common URL representations
 * regardless of the protocols installed on client computer. It is primarity oriented
 * towards http-like URL's.
 *
 * @see java.net.URL
 */

public class URLParser {

    String whole = null;
    String server = null;
    int port = -1;
    String path = null;
    String file = null;
    String authority = null;
    String protocol = null;
    String query = null;
    String reference = null;

    public static Properties protocols;

    static {
        protocols = new Properties();

        // load default protocols
        protocols.put("http", "80");
        protocols.put("https", "443");
        protocols.put("ftp", "21");
        protocols.put("file", "-1");
    }

    /**
     * Create URLParser and parse url fields.
     *
     * @param url String representation of the URL to parse
     *
     * @throws MalformedURLException if the URL could not be parsed
     */
    public URLParser(String url)
            throws MalformedURLException {
        parse(url);
    }

    /**
     * Parse url fields.
     *
     * @param url String representation of the URL to parse
     *
     * @throws MalformedURLException if the URL could not be parsed
     */
    public void parse(String url)
            throws MalformedURLException {
        whole = null;
        server = null;
        port = -1;
        path = null;
        file = null;
        authority = null;
        protocol = null;
        query = null;
        reference = null;
        try {
            whole = new String(url);
            String u = new String(url);

            // we hope for
            // <protocol>://<authority><server>[:<port>][ [/<file>] | [/<path>] | [/<path>/<file>] ]

            // parse protocol
            int at = u.indexOf(":/");

            if (at == -1) {
                throw new MalformedURLException("Missing protocol specification.\nURL should be specified as : <protocol>://<authority><server>[:<port>][ [/<file>] | [/<path>] | [/<path>/<file>] ]");
            }
            protocol = (u.substring(0, at)).toLowerCase(Locale.ENGLISH);

            u = u.substring(at + 1);
            while (u.startsWith("/")) {
                u = u.substring(1);
            }
            boolean nopath = false;

            // parse authority, server and port
            // <authority> ::= [<user>:<passwd>@]
            at = u.indexOf("/");
            String tmp = new String(u);

            if (at > 0) {
                // there is some path info after server, keep it for later parsing.
                tmp = u.substring(0, at);
                u = u.substring(at);
            } else {
                nopath = true;
            } // auth
            at = tmp.lastIndexOf("@");
            if (at > 0) {
                // auth info is supplied
                authority = tmp.substring(0, at);
                tmp = tmp.substring(at + 1);
            } // server
            at = tmp.indexOf(':');
            if (at > -1) {
                //port is supplied
                server = tmp.substring(0, at);
                port = Integer.parseInt(tmp.substring(at + 1));
            } else {
                // port is not supplied
                server = tmp;
                // try to find out which port it is
                Object p = protocols.get(protocol);

                if (p != null) {
                    port = Integer.parseInt((String) p);
                } else {
                    port = -1;
                }
            }
            if (nopath) {
                path = new String("/");
                return;
            } // parse path, file, query and reference
            // going backwards, removing reference, then query and then will
            // separate file and path
            // ref
            at = u.indexOf('#');
            if (at > -1) {
                reference = u.substring(at + 1);
                u = u.substring(0, at);
            } // query
            at = u.indexOf('?');
            if (at > -1) {
                query = u.substring(at + 1);
                u = u.substring(0, at);
            } // path
            at = u.lastIndexOf('/');
            if (at == 0) {
                path = new String(u);
                u = u.substring(at + 1);
                if (u.length() > 0) {
                    file = new String(u);
                }
            } else if (at != -1) {
                path = new String(u);
                u = u.substring(at + 1);
                if (u.length() > 0) {
                    file = new String(u);
                }
            } else {
                path = new String(u);
                // what???
                //throw new MalformedURLException("Error parsing! ");//URL should be specified as : <protocol>://<authority><server>[:<port>][ [/<file>] | [/<path>] | [/<path>/<file>] ]");
            }
        } catch (Exception e) {
            throw new MalformedURLException("URL should be specified as : <protocol>://<authority><server>[:<port>][ [/<file>] | [/<path>] | [/<path>/<file>] ]");
        }
    }

    /*
     public static void main(String []a){
     try{
     URLParser u = new URLParser("https://www.jscape.com/");
     debug(u);
     u.parse("http://www.jscape.com");
     debug(u);
     u.parse("https://lala:lala@lala.com@www.jscape.com:4567/test/test.jsp?cmd=test&who=lala#show");
     debug(u);
     u.parse("ftp://lala:lala@www.jscape.com/test.jsp");
     debug(u);
     }catch(Exception e){
     e.printStackTrace();
     }
     }
     */
    private static void debug(URLParser p) {
        System.out.println("==========================================");
        System.out.println("Parsed URL       :  " + p.getURLString());
        System.out.println("Parsed Protocol  :  " + p.getProtocol());
        System.out.println("Parsed Server    :  " + p.getHost());
        System.out.println("Parsed Port      :  " + p.getPort());
        System.out.println("Parsed Authority :  " + p.getAuthority());
        System.out.println("Parsed Path      :  " + p.getPath());
        System.out.println("Parsed File      :  " + p.getFile());
        System.out.println("Parsed Query     :  " + p.getQuery());
        System.out.println("Parsed Reference :  " + p.getReference());
        System.out.println("==========================================");
    }

    /**
     * Get the host name for this URL.
     *
     * @return The host name parsed from the URL string.
     *
     * @see java.net.URL#getHost
     */
    public String getHost() {
        return server;
    }

    /**
     * Get the port number for this URL.
     *
     * @return The port number parsed from the URL string.
     *
     * @see java.net.URL#getPort
     */
    public int getPort() {
        return port;
    }

    /**
     * Get the path for this URL.
     *
     * @return The path parsed from the URL string.
     *
     * @see java.net.URL#getPath
     */
    public String getPath() {
        return path;
    }

    /**
     * Get the file name for this URL.
     *
     * @return The file name parsed from the URL string.
     *
     * @see java.net.URL#getFile
     */
    public String getFile() {
        return file;
    }

    /**
     * Get the authentication information for this URL.
     *
     * @return The authentication info parsed from the URL string.
     *
     * @see java.net.URL#getAuthority
     */
    public String getAuthority() {
        return authority;
    }

    /**
     * Get the protocol for this URL.
     *
     * @return The protocol parsed from the URL string.
     *
     * @see java.net.URL#getProtocol
     */
    public String getProtocol() {
        return protocol;
    }

    /**
     * Get the query part of this URL.
     *
     * @return The query parsed from the URL string.
     *
     * @see java.net.URL#getQuery
     */
    public String getQuery() {
        return query;
    }

    /**
     * Get the reference part of this URL.
     *
     * @return The reference parsed from the URL string.
     */
    public String getReference() {
        return reference;
    }

    /**
     * Gets the original URL string.
     *
     * @return the original URL
     */
    public String getURLString() {
        return whole;
    }
}
