"use strict";
var URL = require('./URL');

module.exports = URLUtils;

// Allow the `x == null` pattern.  This is eslint's "null: 'ignore'" option,
// but jshint doesn't support this.
/* jshint eqeqeq: false */

// This is an abstract superclass for Location, HTMLAnchorElement and
// other types that have the standard complement of "URL decomposition
// IDL attributes".  This is now standardized as URLUtils, see:
// https://url.spec.whatwg.org/#urlutils
// Subclasses must define a getter/setter on href.
// The getter and setter methods parse and rebuild the URL on each
// invocation; there is no attempt to cache the value and be more efficient
function URLUtils() {}
URLUtils.prototype = Object.create(Object.prototype, {

  _url: { get: function() {
    // XXX: this should do the "Reinitialize url" steps, and "null" should
    // be a valid return value.
    return new URL(this.href);
  } },

  protocol: {
    get: function() {
      var url = this._url;
      if (url && url.scheme) return url.scheme + ":";
      else return ":";
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);
      if (url.isAbsolute()) {
        v = v.replace(/:+$/, "");
        v = v.replace(/[^-+\.a-zA-Z0-9]/g, URL.percentEncode);
        if (v.length > 0) {
          url.scheme = v;
          output = url.toString();
        }
      }
      this.href = output;
    },
  },

  host: {
    get: function() {
      var url = this._url;
      if (url.isAbsolute() && url.isAuthorityBased())
        return url.host + (url.port ? (":" + url.port) : "");
      else
        return "";
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);
      if (url.isAbsolute() && url.isAuthorityBased()) {
        v = v.replace(/[^-+\._~!$&'()*,;:=a-zA-Z0-9]/g, URL.percentEncode);
        if (v.length > 0) {
          url.host = v;
          delete url.port;
          output = url.toString();
        }
      }
      this.href = output;
    },
  },

  hostname: {
    get: function() {
      var url = this._url;
      if (url.isAbsolute() && url.isAuthorityBased())
        return url.host;
      else
        return "";
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);
      if (url.isAbsolute() && url.isAuthorityBased()) {
        v = v.replace(/^\/+/, "");
        v = v.replace(/[^-+\._~!$&'()*,;:=a-zA-Z0-9]/g, URL.percentEncode);
        if (v.length > 0) {
          url.host = v;
          output = url.toString();
        }
      }
      this.href = output;
    },
  },

  port: {
    get: function() {
      var url = this._url;
      if (url.isAbsolute() && url.isAuthorityBased() && url.port!==undefined)
        return url.port;
      else
        return "";
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);
      if (url.isAbsolute() && url.isAuthorityBased()) {
        v = '' + v;
        v = v.replace(/[^0-9].*$/, "");
        v = v.replace(/^0+/, "");
        if (v.length === 0) v = "0";
        if (parseInt(v, 10) <= 65535) {
          url.port = v;
          output = url.toString();
        }
      }
      this.href = output;
    },
  },

  pathname: {
    get: function() {
      var url = this._url;
      if (url.isAbsolute() && url.isHierarchical())
        return url.path;
      else
        return "";
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);
      if (url.isAbsolute() && url.isHierarchical()) {
        if (v.charAt(0) !== "/")
          v = "/" + v;
        v = v.replace(/[^-+\._~!$&'()*,;:=@\/a-zA-Z0-9]/g, URL.percentEncode);
        url.path = v;
        output = url.toString();
      }
      this.href = output;
    },
  },

  search: {
    get: function() {
      var url = this._url;
      if (url.isAbsolute() && url.isHierarchical() && url.query!==undefined)
        return "?" + url.query;
      else
        return "";
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);
      if (url.isAbsolute() && url.isHierarchical()) {
        if (v.charAt(0) === "?") v = v.substring(1);
        v = v.replace(/[^-+\._~!$&'()*,;:=@\/?a-zA-Z0-9]/g, URL.percentEncode);
        url.query = v;
        output = url.toString();
      }
      this.href = output;
    },
  },

  hash: {
    get: function() {
      var url = this._url;
      if (url == null || url.fragment == null || url.fragment === '') {
        return "";
      } else {
        return "#" + url.fragment;
      }
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);

      if (v.charAt(0) === "#") v = v.substring(1);
      v = v.replace(/[^-+\._~!$&'()*,;:=@\/?a-zA-Z0-9]/g, URL.percentEncode);
      url.fragment = v;
      output = url.toString();

      this.href = output;
    },
  },

  username: {
    get: function() {
      var url = this._url;
      return url.username || '';
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);
      if (url.isAbsolute()) {
        v = v.replace(/[\x00-\x1F\x7F-\uFFFF "#<>?`\/@\\:]/g, URL.percentEncode);
        url.username = v;
        output = url.toString();
      }
      this.href = output;
    },
  },

  password: {
    get: function() {
      var url = this._url;
      return url.password || '';
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);
      if (url.isAbsolute()) {
        if (v==='') {
          url.password = null;
        } else {
          v = v.replace(/[\x00-\x1F\x7F-\uFFFF "#<>?`\/@\\]/g, URL.percentEncode);
          url.password = v;
        }
        output = url.toString();
      }
      this.href = output;
    },
  },

  origin: { get: function() {
    var url = this._url;
    if (url == null) { return ''; }
    var originForPort = function(defaultPort) {
      var origin = [url.scheme, url.host, +url.port || defaultPort];
      // XXX should be "unicode serialization"
      return origin[0] + '://' + origin[1] +
        (origin[2] === defaultPort ? '' : (':' + origin[2]));
    };
    switch (url.scheme) {
    case 'ftp':
      return originForPort(21);
    case 'gopher':
      return originForPort(70);
    case 'http':
    case 'ws':
      return originForPort(80);
    case 'https':
    case 'wss':
      return originForPort(443);
    default:
      // this is what chrome does
      return url.scheme + '://';
    }
  } },

  /*
  searchParams: {
    get: function() {
      var url = this._url;
      // XXX
    },
    set: function(v) {
      var output = this.href;
      var url = new URL(output);
      // XXX
      this.href = output;
    },
  },
  */
});

URLUtils._inherit = function(proto) {
  // copy getters/setters from URLUtils to o.
  Object.getOwnPropertyNames(URLUtils.prototype).forEach(function(p) {
    if (p==='constructor' || p==='href') { return; }
    var desc = Object.getOwnPropertyDescriptor(URLUtils.prototype, p);
    Object.defineProperty(proto, p, desc);
  });
};
