/**
 * @since 150316 11:09
 * @author vivaxy
 */

/**
 * usage:
 *
 * // parse some link
 * var url = new Url(link);
 *
 * // parse current location link
 * var url = new Url();
 *
 * methods:
 *
 * // get query string by name
 * url.parameter(key);
 *
 * // set query string by name, and update url properties
 * url.parameter(key, value);
 *
 * // remove query string by name
 * url.removeParameter(key);
 *
 * // to string, returns url.href
 * url.toString();
 *
 * // get property
 * url.get(property);
 *
 * // set property
 * url.set(property, value);
 *
 * get and set properties:
 *
 * // href: fill link, like `https://www.test.com:8080/vivaxy/test/url?name=vivaxy&year=2014&month=6#some`
 * href
 *
 * // protocol: like `https:`
 * protocol
 *
 * // host: hostname + ':' + port, like `www.test.com:8080`
 * host
 *
 * // hostname: like `www.test.com`
 * hostname
 *
 * // port: like `8080`
 * port
 *
 * // pathname: full path from some host, like `/vivaxy/test/url`
 * pathname
 *
 * // search: queryString, like `?name=vivaxy&year=2014&month=6`
 * search
 *
 * // path: pathname + search, like `/vivaxy/test/url?name=vivaxy&year=2014&month=6`
 * path
 *
 * // query: search without `?`, like `name=vivaxy&year=2014&month=6`
 * query
 *
 * // hash: like `#some`
 * hash
 *
 */

/**
 *
 * @param href
 * @constructor
 */
var Url = function (href) {

    this._href = href || global.location.href;
    this._parameters = {};
    this._parse();
    this._parseQuery();
    this._formatQuery();

}, p = Url.prototype;

/**
 * parse url string to url object, and save to Url
 * @returns {Url}
 * @private
 */
p._parse = function () {

    var a = global.document.createElement('a');
    a.href = this._href;

    this._protocol = a.protocol;
    this._host = a.host;
    this._hostname = a.hostname;
    this._port = a.port;
    this._pathname = a.pathname;
    this._search = a.search;
    this._hash = a.hash;

    if (this._host === '') {
        throw new Error('Url: host not found');
    }

    this._path = this._pathname + this._search;
    this._query = this._search.slice(1);

    return this;
};

/**
 * parse query string to query object, and save to _parameters
 * @returns {Url}
 * @private
 */
p._parseQuery = function () {

    var qs = this._query.split('&'), l = qs.length, decode = global.decodeURIComponent;

    for (var i = 0; i < l; i++) {
        var split = qs[i].split('=');
        if (split.length === 2) {
            this._parameters[decode(split[0])] = decode(split[1]);
        }
    }

    return this;
};

/**
 * format parameters to query string, and update the url object
 * @returns {Url}
 * @private
 */
p._formatQuery = function () {
    var query = '', obj = this._parameters, encode = global.encodeURIComponent;
    for (var i in obj) {
        if (obj.hasOwnProperty(i)) {
            query += encode(i) + '=' + encode(obj[i]) + '&';
        }
    }
    query = query.slice(0, -1);
    this._query = query;
    if (query !== '') {
        this._search = '?' + query;
    }
    this._path = this._pathname + this._search;
    this._href = this._protocol + '//' + this._host + this._path + this._hash;

    return this;
};

/**
 * get or set some parameter in query string
 * @param key
 * @param [value]
 * @returns {*}
 */
p.parameter = function (key, value) {
    if (value === undefined) {
        // get
        return this._parameters[key];
    } else {
        // set
        this._parameters[key] = value;
        this._formatQuery();
        return this;
    }
};

/**
 * remove parameter in query string
 * @param key
 * @returns {Url}
 */
p.removeParameter = function (key) {
    delete this._parameters[key];
    this._formatQuery();
    return this;
};

/**
 * to string
 * @returns {*|string}
 */
p.toString = function () {
    return this._href;
};

/**
 * get property
 * @param prop
 * @returns {*}
 */
p.get = function (prop) {
    return this['_' + prop];
};

/**
 * set property
 * @param prop
 * @param value
 * @returns {Url}
 */
p.set = function (prop, value) {

    this['_' + prop] = value;

    /**
     * href |- protocol
     *      |- host     |- hostname
     *      |           |- port
     *      |- path     |- pathname
     *      |           |- search    |- query |- parameters
     *      |- hash
     */
    switch (prop) {
        case 'parameters':
            throw new Error('Url: use `query` instead');
            break;
        case 'query':
            this._path = this._pathname + (value === '' ? '' : '?' + this._query);
            break;
        case 'search':
            if (value === '' || value.indexOf('?') === 0) {
                this._path = this._pathname + value;
            } else {
                throw new Error('Url: `search` starts with `?`');
            }
            break;
        case 'pathname':
            this._path = value + this._search;
            break;
        case 'port':
            this._host = this._hostname + (value === '' ? '' : ':' + value);
            break;
        case 'hostname':
            this._host = value + this._port === '' ? '' : ':' + this._port;
            break;
        case 'hash':
            if (value !== '' && value.indexOf('#') !== 0) {
                throw new Error('Url: `hash` starts with `#`');
            }
            break;
        default:
            break;
    }

    this._href = this._protocol + '//' + this._host + this._path + this._hash;
    this._parameters = {};
    this._parse();
    this._parseQuery();
    this._formatQuery();
    return this;
};

@@ -1378,23 +1378,16 @@
 href %7C%7C 
-global.
 location
@@ -1639,23 +1639,16 @@
 var a = 
-global.
 document
@@ -2279,44 +2279,8 @@
 ngth
-, decode = global.decodeURIComponent
 ;%0A%0A 
@@ -2415,24 +2415,36 @@
 eters%5Bdecode
+URIComponent
 (split%5B0%5D)%5D 
@@ -2451,16 +2451,28 @@
 = decode
+URIComponent
 (split%5B1
@@ -2692,44 +2692,8 @@
 ters
-, encode = global.encodeURIComponent
 ;%0A  
@@ -2779,16 +2779,28 @@
 = encode
+URIComponent
 (i) + '=
@@ -2809,16 +2809,28 @@
 + encode
+URIComponent
 (obj%5Bi%5D)

remove global