/* global window, document, $, common, WinEnv, elManager, $id, $str */

(function() {
  /* global Components */
  const {utils: Cu} = Components;

  var {ScriptLoader: {importModule}} = Cu.import(
      "chrome://rpcontinued/content/lib/script-loader.jsm", {});
  var {Prefs} = importModule("models/prefs");
  var {PolicyManager} = importModule("lib/policy-manager");

  //============================================================================

  var PAGE_STRINGS = [
    "yourPolicy",
    "defaultPolicy",
    "subscriptions",
    "type",
    "origin",
    "destination",
    "allow",
    "block",
    "temporary",
    "addRule",
    "learnMoreAboutRules",
    "removeOldRules",
    "ruleSet",
    "filterRules",
    "policy"
  ];

  $(function() {
    common.localize(PAGE_STRINGS);
    // l10n for input placeholders.
    $id("rulesearch").placeholder = $str("search");
    $("[name=originscheme]").prop("placeholder", $str("scheme"));
    $("[name=destscheme]").prop("placeholder", $str("scheme"));
    $("[name=originhost]").prop("placeholder", $str("host"));
    $("[name=desthost]").prop("placeholder", $str("host"));
    $("[name=originport]").prop("placeholder", $str("port"));
    $("[name=destport]").prop("placeholder", $str("port"));
  });

  const SEARCH_DELAY = 100;

  var searchTimeoutId = null;

  function populateRuleTable(filter) {
    searchTimeoutId = null;

    var table = $id("rules");

    clearRulesTable(table);

    var entries;

    // Get and display user rules
    var user = PolicyManager.getUserRulesets().user;
    entries = user.rawRuleset.toJSON().entries;
    addRules(entries, "User", filter, false);

    // Get and display temorary rules
    var temp = PolicyManager.getUserRulesets().temp;
    entries = temp.rawRuleset.toJSON().entries;
    addRules(entries, "Temporary", filter, false);

    // Get and display subscription rules
    var subscriptionLists = PolicyManager.getSubscriptionRulesets();
    for (var subscriptionList in subscriptionLists) {
      for (var subscription in subscriptionLists[subscriptionList]) {
        entries = subscriptionLists[subscriptionList][subscription].rawRuleset.
            toJSON().entries;
        addRules(entries, subscription, filter, true);
      }
    }
  }

  function addRules(entries, source, filter, readOnly) {
    var table = $("#rules");
    for (var entryType in entries) {
      for (var i = 0; i < entries[entryType].length; i++) {
        var entry = entries[entryType][i];
        var origin = entry.o ? common.ruleDataPartToDisplayString(entry.o) : "";
        var dest = entry.d ? common.ruleDataPartToDisplayString(entry.d) : "";
        if (filter) {
          if (origin.indexOf(filter) === -1 && dest.indexOf(filter) === -1) {
            continue;
          }
        }
        addRulesTableRow(table, entryType, origin, dest, entry, source,
            readOnly);
      }
    }
  }

  function deleteRule(event) {
    // TODO: the rule should not be referenced by the rule data but
    //       by some unique identifier. Currently, if there's exactly
    //       the same rule twice, (one of them might be a temporary
    //       rule), both will get removed.
    var anchor = $(event.target);
    var ruleAction = anchor.data("requestpolicyRuleAction");
    var ruleData = anchor.data("requestpolicyRuleData");
    if (ruleAction === "allow") {
      PolicyManager.removeAllowRule(ruleData);
    } else {
      PolicyManager.removeDenyRule(ruleData);
    }
    anchor.closest("tr").remove();
  }

  function clearRulesTable(table) {
    var children = table.getElementsByTagName("tr");
    while (children.length) {
      var child = children.item(0);
      child.parentNode.removeChild(child);
    }
  }

  function addRulesTableRow(table, ruleAction, origin, dest, ruleData, source,
      readOnly) {

    if (ruleAction !== "allow") {
      ruleAction = "block";
    }
    ruleAction = ruleAction === "allow" ? "allow" : "block";
    var ruleActionString = ruleAction === "allow" ? $str("allow") :
        $str("block");

    var row = $("<tr>").addClass(ruleAction).appendTo(table);

    row.append(
      $("<td>").text(ruleActionString),
      $("<td>").text(origin),
      $("<td>").text(dest),
      $("<td>").text(source)
    );

    if (!readOnly) {
      var anchor = $("<a>");
      anchor.text("x").addClass("deleterule");
      anchor.data("requestpolicyRuleAction", ruleAction);
      anchor.data("requestpolicyRuleData", ruleData);
      anchor.click(deleteRule);
      row.append($("<td>").append(anchor));
    } else {
      row.append($("<td>"));
    }
  }

  window.addRule = function() {
    try {
      addRuleHelper();
    } catch (e) {
      window.alert("Unable to add rule: " + e.toString());
      return;
    }

    // the table is repopulated through the RulesChangedObserver
  };

  function addRuleHelper() {
    var form = document.forms.addruleform;
    var allow = form.elements.allowrule.checked ? true : false;
    var temporary = form.elements.temporary.checked ? true : false;
    var originScheme = form.elements.originscheme.value;
    var originHost = form.elements.originhost.value;
    var originPort = form.elements.originport.value;
    var destScheme = form.elements.destscheme.value;
    var destHost = form.elements.desthost.value;
    var destPort = form.elements.destport.value;
    // TODO: we either need to sanity check the ruleData here
    //       or the policy needs to do this when it is added.
    //       Probably better to do it in the policy code.
    function ruleInfoToRuleDataPart(scheme, host, port) {
      if (!scheme && !host && !port) {
        return null;
      }
      var part = {};
      if (scheme) {
        part.s = scheme;
      }
      if (host) {
        part.h = host;
      }
      if (port) {
        part.port = port;
      }
      return part;
    }
    var originPart = ruleInfoToRuleDataPart(originScheme, originHost,
        originPort);
    var destPart = ruleInfoToRuleDataPart(destScheme, destHost, destPort);
    if (!originPart && !destPart) {
      // TODO: don't throw, instead show message in form.
      throw "You must specify some rule information";
    }
    var ruleData = {};
    if (originPart) {
      ruleData.o = originPart;
    }
    if (destPart) {
      ruleData.d = destPart;
    }
    if (allow) {
      if (temporary) {
        PolicyManager.addTemporaryAllowRule(ruleData);
      } else {
        PolicyManager.addAllowRule(ruleData);
      }
    } else {
      if (temporary) {
        PolicyManager.addTemporaryDenyRule(ruleData);
      } else {
        PolicyManager.addDenyRule(ruleData);
      }
    }
  }

  window.onload = function() {
    var search = $id("rulesearch");
    elManager.addListener(search, "keyup", function(event) {
      if (searchTimeoutId !== null) {
        window.clearTimeout(searchTimeoutId);
      }
      searchTimeoutId = window.setTimeout(function() {
        populateRuleTable(event.target.value);
      }, SEARCH_DELAY);
    }, false);
    populateRuleTable(search.value);
    if (Prefs.oldRulesExist()) {
      $("#oldrulesexist").show();
    }

    // observe rule changes and update the table then
    WinEnv.obMan.observe(["rpcontinued-rules-changed"], function() {
      var search = $id("rulesearch");
      populateRuleTable(search.value);
    });
  };

}());
