/*
 * This file is part of SpyBlock <https://www.gnu.org/software/gnuzilla/>,
 * Copyright (C) 2006-2015 Eyeo GmbH (Adblock Plus) 
 * Copyright (C) 2015 Ruben Rodriguez <ruben@gnu.org>
 *
 * SpyBlock is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * SpyBlock is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SpyBlock.  If not, see <http://www.gnu.org/licenses/>.
 */

let nodes = null;
let item = null;
let advancedMode = false;

function init()
{
  [nodes, item] = window.arguments;

  E("filterType").value = (!item.filter || item.filter.disabled || item.filter instanceof WhitelistFilter ? "filterlist" : "whitelist");
  E("customPattern").value = item.location;

  let insertionPoint = E("customPatternBox");
  let addSuggestion = function(address)
  {
    // Always drop protocol and www. from the suggestion
    address = address.replace(/^[\w\-]+:\/+(?:www\.)?/, "");

    let suggestion = document.createElement("radio");
    suggestion.setAttribute("value", address);
    suggestion.setAttribute("label", address);
    suggestion.setAttribute("crop", "center");
    suggestion.setAttribute("class", "suggestion");
    insertionPoint.parentNode.insertBefore(suggestion, insertionPoint);

    return address;
  }

  let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  try
  {
    let suggestions = [""];

    let url = ioService.newURI(item.location, null, null)
                       .QueryInterface(Ci.nsIURL);
    let suffix = (url.query ? "?*" : "");
    url.query = "";
    url.ref = "";
    suggestions[1] = addSuggestion(url.spec + suffix);

    let parentURL = ioService.newURI(url.fileName == "" ? ".." : ".", null, url);
    if (!parentURL.equals(url))
      suggestions[2] = addSuggestion(parentURL.spec + "*");
    else
      suggestions[2] = suggestions[1];

    let rootURL = ioService.newURI("/", null, url);
    if (!rootURL.equals(parentURL) && !rootURL.equals(url))
      suggestions[3] = addSuggestion(rootURL.spec + "*");
    else
      suggestions[3] = suggestions[2];

    try
    {
      suggestions[4] = addSuggestion(url.host.replace(/^www\./, "") + "^");

      // Prefer example.com^ to example.com/*
      let undesired = suggestions[4].replace(/\^$/, "/*");
      for (let i = 0; i < suggestions.length - 1; i++)
        if (suggestions[i] == undesired)
          suggestions[i] = suggestions[4];

      for (let child = insertionPoint.parentNode.firstChild; child; child = child.nextSibling)
      {
        if (child.localName == "radio" && child.getAttribute("value") == undesired)
        {
          child.parentNode.removeChild(child);
          break;
        }
      }
    }
    catch (e)
    {
      suggestions[4] = suggestions[3];
    }

    try
    {
      let effectiveTLD = Cc["@mozilla.org/network/effective-tld-service;1"].getService(Ci.nsIEffectiveTLDService);
      let host = url.host;
      let baseDomain = effectiveTLD.getBaseDomainFromHost(host);
      if (baseDomain != host.replace(/^www\./, ""))
        suggestions[5] = addSuggestion(baseDomain + "^");
      else
        suggestions[5] = suggestions[4];
    }
    catch (e)
    {
      suggestions[5] = suggestions[4];
    }

    E("patternGroup").value = (Prefs.composer_default in suggestions ? suggestions[Prefs.composer_default] : suggestions[1]);
  }
  catch (e)
  {
    // IOService returned nsIURI - not much we can do with it
    addSuggestion(item.location);
    E("patternGroup").value = "";
  }
  if (Prefs.composer_default == 0)
    E("customPattern").focus();
  else
    E("patternGroup").focus();

  let types = [];
  for (let type in Policy.localizedDescr)
  {
    types.push(parseInt(type));
  }
  types.sort(function(a, b) {
    if (a < b)
      return -1;
    else if (a > b)
      return 1;
    else
      return 0;
  });

  let docDomain = item.docDomain;
  let thirdParty = item.thirdParty;

  if (docDomain)
    docDomain = docDomain.replace(/^www\./i, "").replace(/\.+$/, "");
  if (docDomain)
    E("domainRestriction").value = docDomain;

  E("thirdParty").hidden = !thirdParty;
  E("firstParty").hidden = thirdParty;

  let typeGroup = E("typeGroup");
  let defaultTypes = RegExpFilter.prototype.contentType & ~RegExpFilter.typeMap.DOCUMENT;
  let isDefaultType = (RegExpFilter.typeMap[item.typeDescr] & defaultTypes) != 0;
  for (let type of types)
  {
    if (type == Policy.type.ELEMHIDE)
      continue;

    let typeNode = document.createElement("checkbox");
    typeNode.setAttribute("value", Policy.typeDescr[type].toLowerCase().replace(/\_/g, "-"));
    typeNode.setAttribute("label", Policy.localizedDescr[type].toLowerCase());

    let typeMask = RegExpFilter.typeMap[Policy.typeDescr[type]];
    typeNode._defaultType = (typeMask & defaultTypes) != 0;
    if ((isDefaultType && typeNode._defaultType) || (!isDefaultType && item.type == type))
      typeNode.setAttribute("checked", "true");

    if (item.type == type)
      typeNode.setAttribute("disabled", "true");
    typeNode.addEventListener("command", function() checkboxUpdated(this), false);
    typeGroup.appendChild(typeNode);
  }

  let collapseDefault = E("collapseDefault");
  collapseDefault.label = collapseDefault.getAttribute(Prefs.fastcollapse ? "label_no" : "label_yes");
  E("collapse").value = "";
  E("collapse").setAttribute("label", collapseDefault.label);

  let warning = E("disabledWarning");
  generateLinkText(warning);
  warning.hidden = Prefs.enabled;

  updatePatternSelection();
}

function checkboxUpdated(checkbox)
{
  checkbox._lastChange = Date.now();
  updateFilter();
}

function updateFilter()
{
  let filter = "";

  let type = E("filterType").value
  if (type == "whitelist")
    filter += "@@";

  let pattern = E("patternGroup").value;
  if (pattern == "")
    pattern = E("customPattern").value;

  if (E("anchorStart").checked)
    filter += E("anchorStart").flexibleAnchor ? "||" : "|";

  filter += pattern;

  if (E("anchorEnd").checked)
    filter += "|";

  if (advancedMode)
  {
    let options = [];

    if (E("domainRestrictionEnabled").checked)
    {
      let domainRestriction = E("domainRestriction").value.replace(/[,\s]/g, "").replace(/\.+$/, "");
      if (domainRestriction)
        options.push([E("domainRestrictionEnabled")._lastChange || 0, "domain=" + domainRestriction]);
    }

    if (E("firstParty").checked)
      options.push([E("firstParty")._lastChange || 0, "~third-party"]);
    if (E("thirdParty").checked)
      options.push([E("thirdParty")._lastChange || 0, "third-party"]);

    if (E("matchCase").checked)
      options.push([E("matchCase")._lastChange || 0, "match-case"]);

    let collapse = E("collapse");
    disableElement(collapse, type == "whitelist", "value", "");
    if (collapse.value != "")
      options.push([collapse._lastChange, collapse.value]);

    let enabledTypes = [];
    let disabledTypes = [];
    let forceEnabledTypes = [];
    for (let typeNode = E("typeGroup").firstChild; typeNode; typeNode = typeNode.nextSibling)
    {
      let value = typeNode.getAttribute("value");
      if (value == "document")
        disableElement(typeNode, type != "whitelist", "checked", false);

      if (!typeNode._defaultType)
      {
        if (typeNode.getAttribute("checked") == "true")
          forceEnabledTypes.push([typeNode._lastChange || 0, value]);
      }
      else if (typeNode.getAttribute("checked") == "true")
        enabledTypes.push([typeNode._lastChange || 0, value]);
      else
        disabledTypes.push([typeNode._lastChange || 0, "~" + value]);
    }
    if (!forceEnabledTypes.length && disabledTypes.length < enabledTypes.length)
      options.push.apply(options, disabledTypes);
    else
      options.push.apply(options, enabledTypes);
    options.push.apply(options, forceEnabledTypes);

    if (options.length)
    {
      options.sort(function(a, b) a[0] - b[0]);
      filter += "$" + options.map(function(o) o[1]).join(",");
    }
  }
  else
  {
    let defaultTypes = RegExpFilter.prototype.contentType & ~RegExpFilter.typeMap.DOCUMENT;
    let isDefaultType = (RegExpFilter.typeMap[item.typeDescr] & defaultTypes) != 0;
    if (!isDefaultType)
      filter += "$" + item.typeDescr.toLowerCase().replace(/\_/g, "-");
  }

  filter = Filter.normalize(filter);
  E("regexpWarning").hidden = !Filter.regexpRegExp.test(filter);

  let isSlow = false;
  let compiledFilter = Filter.fromText(filter);
  if (E("regexpWarning").hidden)
  {
    if (compiledFilter instanceof RegExpFilter && defaultMatcher.isSlowFilter(compiledFilter))
      isSlow = true;
  }
  E("shortpatternWarning").hidden = !isSlow;

  E("matchWarning").hidden = compiledFilter instanceof RegExpFilter && compiledFilter.matches(item.location, item.typeDescr, item.docDomain, item.thirdParty);

  E("filter").value = filter;
}

function generateLinkText(element, replacement)
{
  let template = element.getAttribute("textTemplate");
  if (typeof replacement != "undefined")
    template = template.replace(/\?1\?/g, replacement)

  let [, beforeLink, linkText, afterLink] = /(.*)\[link\](.*)\[\/link\](.*)/.exec(template) || [null, "", template, ""];
  while (element.firstChild && element.firstChild.nodeType != Node.ELEMENT_NODE)
    element.removeChild(element.firstChild);
  while (element.lastChild && element.lastChild.nodeType != Node.ELEMENT_NODE)
    element.removeChild(element.lastChild);
  if (!element.firstChild)
    return;

  element.firstChild.textContent = linkText;
  element.insertBefore(document.createTextNode(beforeLink), element.firstChild);
  element.appendChild(document.createTextNode(afterLink));
}

function updatePatternSelection()
{
  let pattern = E("patternGroup").value;
  if (pattern == "")
  {
    pattern = E("customPattern").value;
  }
  else
  {
    E("anchorStart").checked = true;
    E("anchorEnd").checked = false;
  }

  function testFilter(/**String*/ filter) /**Boolean*/
  {
    return RegExpFilter.fromText(filter + "$" + item.typeDescr).matches(item.location, item.typeDescr, item.docDomain, item.thirdParty);
  }

  let anchorStartCheckbox = E("anchorStart");
  if (!/^\*/.test(pattern) && testFilter("||" + pattern))
  {
    disableElement(anchorStartCheckbox, false, "checked", false);
    [anchorStartCheckbox.label, anchorStartCheckbox.accessKey] = Utils.splitLabel(anchorStartCheckbox.getAttribute("labelFlexible"));
    anchorStartCheckbox.flexibleAnchor = true;
  }
  else
  {
    disableElement(anchorStartCheckbox, /^\*/.test(pattern) || !testFilter("|" + pattern), "checked", false);
    [anchorStartCheckbox.label, anchorStartCheckbox.accessKey] = Utils.splitLabel(anchorStartCheckbox.getAttribute("labelRegular"));
    anchorStartCheckbox.flexibleAnchor = false;
  }
  disableElement(E("anchorEnd"), /[\*\^]$/.test(pattern) || !testFilter(pattern + "|"), "checked", false);

  updateFilter();
  setAdvancedMode(document.documentElement.getAttribute("advancedMode") == "true");
}

function updateCustomPattern()
{
  E("patternGroup").value = "";
  updatePatternSelection();
}

function addFilter() {
  let filter = Filter.fromText(document.getElementById("filter").value);
  filter.disabled = false;

  FilterStorage.addFilter(filter);

  if (nodes)
    Policy.refilterNodes(nodes, item);

  return true;
}

function setAdvancedMode(mode) {
  advancedMode = mode;

  var dialog = document.documentElement;
  dialog.setAttribute("advancedMode", advancedMode);

  var button = dialog.getButton("disclosure");
  button.setAttribute("label", dialog.getAttribute(advancedMode ? "buttonlabeldisclosure_off" : "buttonlabeldisclosure_on"));

  updateFilter();
}

function disableElement(element, disable, valueProperty, disabledValue) {
  if ((element.getAttribute("disabled") == "true") == disable)
    return;

  if (disable)
  {
    element.setAttribute("disabled", "true");
    element._abpStoredValue = element[valueProperty];
    element[valueProperty] = disabledValue;
  }
  else
  {
    element.removeAttribute("disabled");
    if ("_abpStoredValue" in element)
      element[valueProperty] = element._abpStoredValue;
    delete element._abpStoredValue;
  }
}

function openPreferences()
{
  UI.openFiltersDialog(Filter.fromText(E("filter").value));
}

function doEnable() {
  Prefs.enabled = true;
  E("disabledWarning").hidden = true;
}

/**
 * Selects or unselects all type checkboxes except those
 * that are disabled.
 */
function selectAllTypes(/**Boolean*/ select)
{
  for (let typeNode = E("typeGroup").firstChild; typeNode; typeNode = typeNode.nextSibling)
    if (typeNode.getAttribute("disabled") != "true")
      typeNode.checked = select;
  updateFilter();
}
