--
-- (C) 2017-21 - ntop.org
--

dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
require "lua_utils"
local host_pools_utils = require "host_pools_utils"
local discover = require "discover_utils"
local template = require "template_utils"
local graph_utils = require "graph_utils"

if(ntop.isPro()) then
  package.path = dirs.installdir .. "/pro/scripts/lua/modules/?.lua;" .. package.path
  shaper_utils = require "shaper_utils"
end

-- Administrator check
if not isAdministrator() then
  return
end

local pool_add_warnings = {}

if _POST["edit_pools"] ~= nil then
  local config = paramsPairsDecode(_POST, true)

  for pool_id, pool_name in pairs(config) do
    -- Filter pool ids only
    if tonumber(pool_id) ~= nil then
      host_pools_utils.createPool(pool_id, pool_name,
				  nil --[[ children_safe ]],
				  nil --[[ enforce_quotas_per_pool_member ]],
				  nil --[[ enforce_shapers_per_pool_member ]],
				  true --[[ create or rename ]])
    end
  end

  -- Reload is required here to load the new metadata
  ntop.reloadHostPools()
elseif _POST["pool_to_delete"] ~= nil then
  local pool_id = _POST["pool_to_delete"]
  host_pools_utils.deletePool(pool_id)

  -- Note: this will also reload the shaping rules
  ntop.reloadHostPools()
elseif (_POST["edit_members"] ~= nil) then
  local pool_to_edit = _POST["pool"]
  local config = paramsPairsDecode(_POST, true)

  local sanitized = {}

  -- Sanitize parameters
  for new_member, value in pairs(config) do
    if not isValidPoolMember(new_member) then
      http_lint.validationError(_POST, "new_member", new_member, "Invalid pool member")
    end

    local parts = split(value, "|")

    if #parts ~= 3 then
      http_lint.validationError(_POST, "member_values", parts, "Invalid member values")
    end

    local assembled = {
      old_member = parts[1],
      alias = parts[2],
      icon = parts[3],
    }

    -- Convention: use uppercase letters for mac, lowercase for ip
    if isMacAddress(new_member) then
      new_member = string.upper(new_member)
    else
      new_member = string.lower(new_member)
    end

    if (not isEmptyString(old_member)) and (not isValidPoolMember(assembled.old_member)) then
      http_lint.validationError(_POST, "old_member", new_member, "Invalid pool member")
    end

    if not http_lint.validateUnchecked(assembled.alias) then
      http_lint.validationError(_POST, "alias", assembled.alias, "Invalid member alias")
    end

    if not http_lint.validateSingleWord(assembled.icon) then
      http_lint.validationError(_POST, "icon", assembled.icon, "Invalid member icon")
    end

    local hostinfo = hostkey2hostinfo(new_member)
    local network, prefix = splitNetworkPrefix(hostinfo.host)
    local already_added = false

    if prefix ~= nil then
      local masked = ntop.networkPrefix(network, prefix)
      if masked ~= network then
        -- Normalize new networks (extract their prefix)
        local new_key = host2member(masked, hostinfo.vlan, prefix)

        pool_add_warnings[#pool_add_warnings + 1] = i18n("host_pools.network_normalized", {
          network = hostinfo2hostkey(hostkey2hostinfo(new_member)),
          network_normalized = hostinfo2hostkey(hostkey2hostinfo(new_key))
        })

        sanitized[new_key] = assembled
        already_added = true
      end
    end

    if not already_added then
      sanitized[new_member] = assembled
    end
  end

  config = sanitized

  -- This code handles member address changes

  -- delete old addresses
  for k,value in pairs(table.clone(config) --[[ Work on a copy to modify the original while iterating ]]) do
    local old_member = value.old_member

    if((not isEmptyString(old_member)) and (k ~= old_member)) then
      if config[old_member] then
        -- Do not delete and re-add members which have only changed their list key
        config[old_member].old_member = old_member
      else
        host_pools_utils.deletePoolMember(pool_to_edit, old_member)
      end
    end
  end

  -- add new addresses
  for new_member,value in pairs(config) do
    local k = value.old_member

    local is_new_member = (k ~= new_member)

    if is_new_member then
      local res, info = host_pools_utils.addPoolMember(pool_to_edit, new_member)

      if (res == false) and (info.existing_member_pool ~= nil) then
        -- remove @0
        local member_to_print = hostinfo2hostkey(hostkey2hostinfo(new_member))
        pool_add_warnings[#pool_add_warnings + 1] = i18n("host_pools.member_exists", {
          member_name = member_to_print,
          member_pool = host_pools_utils.getPoolName(info.existing_member_pool)
        })
      end
    end

    local host_key, is_network = host_pools_utils.getMemberKey(new_member)

    if not is_network then
      local alias = value.alias
      local skip_alias = false

      if isMacAddress(new_member) then
        local manuf = ntop.getMacManufacturer(new_member)
        if (manuf ~= nil) and (manuf.extended == alias) then
          -- this is not the alias, it is the manufacturer
          skip_alias = true
        end
      end

      if(((not is_new_member) or (not isEmptyString(alias))) and (not skip_alias)) then
        setHostAltName(host_key, alias)
      end

      local icon = tonumber(value.icon)

      if (not isEmptyString(icon))
            and ((not is_new_member) or (icon ~= 0))
            and isMacAddress(new_member)
            and new_member ~= "00:00:00:00:00:00" then
        setCustomDeviceType(new_member, icon)
        ntop.setMacDeviceType(new_member, icon, true --[[ overwrite ]])
      end
    end
  end

  ntop.reloadHostPools()
elseif _POST["member_to_delete"] ~= nil then
  local pool_to_edit = _POST["pool"]

  host_pools_utils.deletePoolMember(pool_to_edit, _POST["member_to_delete"])
  ntop.reloadHostPools()
elseif _POST["empty_pool"] ~= nil then
  host_pools_utils.emptyPool(_POST["empty_pool"])
  ntop.reloadHostPools()
elseif (_POST["member"] ~= nil) and (_POST["pool"] ~= nil) then
  -- change member pool
  host_pools_utils.changeMemberPool(_POST["member"], _POST["pool"], nil, true --[[do not consider host MAC]])
  ntop.reloadHostPools()
end

function printPoolNameField(pool_id_str)
  print[[<div class="form-group has-feedback" style="margin-bottom:0;">]]
  print[[<input name="pool_' + ]] print(pool_id_str) print[[ + '" class="form-control" spellcheck="false" data-unique="unique" placeholder="]] print(i18n("host_pools.specify_pool_name")) print[[" required/>]]
  print[[<div class="help-block with-errors" style="margin-bottom:0;"></div>]]
  print[[</div>]]
end

function printMemberAddressField(member_str, origin_value_str)
  print[[<div class="form-group has-feedback" style="margin-bottom:0;">]]
  print[[<input name="member_' + ]] print(member_str) print[[ + '" class="form-control" spellcheck="false" data-address="address" data-member="member" placeholder="]] print(i18n("host_pools.specify_member_address")) print[["]]
  if not isEmptyString(origin_value_str) then
    print[[ data-origin-value="' + ]] print(origin_value_str) print[[ + '"]]
  end
  print[[ required/>]]
  print[[<div class="help-block with-errors" style="margin-bottom:0;"></div>]]
  print[[</div>]]
end

function printMemberVlanField(member_str)
  print[[<div class="form-group has-feedback" style="margin-bottom:0;">]]
  print[[<input name="member_' + ]] print(member_str) print[[ + '_vlan" class="form-control text-right" data-member="member" style="width:5em; padding-right:1em; margin: 0 auto;" type="number" min="0" value="0" required/>]]
  print[[<div class="help-block with-errors" style="margin-bottom:0;"></div>]]
  print[[</div>]]
end

function printIconField(member_str)
  discover.printDeviceTypeSelector("", "icon_member_' + " .. member_str .. " + '")
end

function printAliasField(member_str)
  print[[<input name="alias_member_' + ]] print(member_str) print[[ + '" class="form-control" />]]
end

--------------------------------------------------------------------------------

local selected_pool_id = _GET["pool"]

local selected_pool = nil
local available_pools = host_pools_utils.getPoolsList()

for _, pool in ipairs(available_pools) do
  if pool.id == selected_pool_id then
    selected_pool = pool
  end
end

if selected_pool == nil then
  if #available_pools == 1 then
    -- only the default pool is available
    selected_pool = available_pools[1]
  else
    selected_pool = available_pools[2]
  end
end

-- We are passing too much _POST data, no more than 5 members allowed
local perPageMembers = "5"
local perPagePools = "10"

local members_filtering = _GET["members_filter"]
local manage_url = "?ifid="..ifId.."&page=pools&pool="..selected_pool.id.."#manage"
local ifstats = interface.getStats()

--------------------------------------------------------------------------------

print [[

  <ul id="hostPoolsNav" class="nav nav-tabs" role="tablist">
    <li class="nav-item"><a class="nav-link" data-toggle="tab" role="tab" href="#manage">]] print(i18n("host_pools.manage_pools")) print[[</a></li>
    <li class="nav-item"><a class="nav-link" data-toggle="tab" role="tab" href="#create">]] print(i18n("host_pools.create_pools")) print[[</a></li>
]]

if ifstats.has_macs then
  print[[<li class="nav-item"><a class="nav-link" data-toggle="tab" role="tab" href="#unassigned">]] print(i18n("unknown_devices.unassigned_devices")) print[[</a></li>]]
end

print[[
  </ul>
  <div class="tab-content">
    <div id="manage" class="tab-pane">
<br/><table><tbody><tr>
]]

print('<td style="white-space:nowrap; padding-right:1em;">') print(i18n("host_pools.pool")) print(': <select id="pool_selector" class="form-control pool-selector" style="display:inline; width:14em;" onchange="document.location.href=\'?ifid=') print(ifId.."") print('&page=pools&pool=\' + $(this).val() + \'#manage\';">')
print(graph_utils.poolDropdown(ifId, selected_pool.id, {[host_pools_utils.DEFAULT_POOL_ID]=true}))
print('</select>')

local no_pools = (#available_pools <= 1)

if selected_pool.id ~= host_pools_utils.DEFAULT_POOL_ID then
    if areHostPoolsTimeseriesEnabled(ifid) then
      print("&nbsp; <a href='"..ntop.getHttpPrefix().."/lua/pool_details.lua?pool="..selected_pool.id.."&page=historical' title='Chart'><i class='fas fa-chart-area'></i></a>")
    end
end

print('</td>\n')

local function formatMemberFilter()
  if starts(members_filtering, "manuf:") then
    return i18n("host_pools.manufacturer_filter", {manufacturer=string.sub(members_filtering, string.len("manuf:")+1)})
  else
    return i18n("host_pools.member_filter", {member=split(members_filtering, "/")[1]})
  end
end

if members_filtering ~= nil then
  print[[
  <td>
    <form action="#manage">
      <input type="hidden" name="ifid" value="]] print(ifId.."") print[[" />
      <input type="hidden" name="page" value="pools" />
      <input type="hidden" name="pool" value="]] print(selected_pool.id) print[[" />
      <button type="button" class="btn btn-secondary btn-sm" onclick="$(this).closest('form').submit();">
        <i class="fas fa-times fa-lg" aria-hidden="true" data-original-title="" title=""></i> ]] print(formatMemberFilter()) print[[
      </button>
    </form>
  </td>
  ]]
end
print('<td style="width:100%"></td><td>')
print(
  template.gen("typeahead_input.html", {
    typeahead={
      base_id     = "t_member",
      action      = ntop.getHttpPrefix() .. "/lua/if_stats.lua#manage",
      parameters  = {
                      pool = selected_pool.id,
                      ifid = tostring(ifId),
                      page = "pools",
                    },
      json_key    = "key",
      query_field = "members_filter",
      query_url   = ntop.getHttpPrefix() .. "/lua/find_member.lua",
      query_title = i18n("host_pools.search_member"),
      style       = "margin-left:1em; width:25em;",
    }
  })
)
print('</td>')
print('</tr></tbody></table>')

if no_pools then
  print[[<script>$("#pool_selector").attr("disabled", "disabled");</script>]]
end

for _, msg in ipairs(pool_add_warnings) do
  print([[
  <div class="alert alert-warning alert-dismissible" style="margin-top:2em; margin-bottom:0em;">
    <button type="button" class="close" data-dismiss="alert" aria-label="]]..i18n("close")..[[">
      <span aria-hidden="true">&times;</span>
    </button><b>]]..i18n("warning")..[[</b>: ]]..msg..[[
  </div>]])
end

  print[[
      <form id="table-manage-form">
        <br/><div id="table-manage"></div>
        <button class="btn btn-primary" style="float:right; margin-right:1em;" disabled="disabled" type="submit">]] print(i18n("save_settings")) print[[</button>
      </form>
      ]]

  if not ntop.isEnterpriseM() and not ntop.isnEdgeEnterprise() then
     print[[<span style="float:left;">]]
     print(i18n("notes"))
     print[[<ul>
      <li>]] print(i18n("host_pools.max_members_message", {maxnum = host_pools_utils.LIMITED_NUMBER_POOL_MEMBERS})) print[[</li>
    </ul>
  </span>]]
  end

print[[
      <button id="emptyPoolButton" class="btn btn-secondary" onclick="$('#empty_pool_dialog').modal('show');" style="float:right; margin-right:1em;"><i class="fas fa-trash" aria-hidden="true"></i> ]] print(i18n("host_pools.empty_pool")) print[[</button>
]]

print[[
      <br/><br/>
    </div>
    <div id="create" class="tab-pane">
      <form id="table-create-form">
        <br/><div id="table-create"></div>
        <button class="btn btn-primary" style="float:right; margin-right:1em;" disabled="disabled" type="submit">]] print(i18n("save_settings")) print[[</button>
      </form>

      <div style="float:left">
        <form action="/lua/rest/v1/get/pool/config.lua" class="form-inline" method="GET" data-ays-ignore="true">
          <input type="hidden" name="ifid" value="]] print(tostring(ifId)) print[[" />
          <input type="hidden" name="download" value="true" />
          <button type="submit" class="btn btn-secondary"><span>]] print(i18n("host_pools.config_export")) print[[</span></button>
        </form>
        <button id="import-modal-btn" data-toggle="modal" data-target="#import-modal" class="btn btn-secondary"><span>]] print(i18n("host_pools.config_import")) print[[</span></button>
      </div>

      <br/><br/>
      ]] print(i18n("notes")) print[[
      <ul>
        <li>]] print(i18n("host_pools.cannot_delete_cp")) print[[.</li>]]

if not ntop.isEnterpriseM() and not ntop.isnEdgeEnterprise() then
  print("<li>"..i18n("host_pools.max_pools_message", {maxnum=host_pools_utils.LIMITED_NUMBER_USER_HOST_POOLS}).."</li>")
end

print[[
      </ul>
    </div>
    <div id="unassigned" class="tab-pane">
]]

dofile(dirs.installdir .. "/scripts/lua/unknown_devices.lua")

print [[
    </div>
  </div>
]]

-- Create delete dialogs

print(
  template.gen("modal_confirm_dialog.html", {
    dialog={
      id      = "delete_pool_dialog",
      action  = "deletePool(delete_pool_id)",
      title   = i18n("host_pools.delete_pool"),
      message = i18n("host_pools.confirm_delete_pool") .. ' "<span id=\"delete_pool_dialog_pool\"></span>", ' .. i18n("host_pools.and_associated_members").."?",
      confirm = i18n("delete"),
    }
  })
)

print(
  template.gen("modal_confirm_dialog.html", {
    dialog={
      id      = "delete_member_dialog",
      action  = "deletePoolMember(delete_member_id)",
      title   = i18n("host_pools.remove_member"),
      message = i18n("host_pools.confirm_remove_member") .. ' "<span id=\"delete_member_dialog_member\"></span>" ' .. i18n("host_pools.from_pool") .. ' "' .. selected_pool.name .. '"?',
      confirm = i18n("remove"),
    }
  })
)

print(
  template.gen("modal_confirm_dialog.html", {
    dialog={
      id      = "empty_pool_dialog",
      action  = "emptyCurrentPool()",
      title   = i18n("host_pools.empty_pool"),
      message = i18n("host_pools.confirm_empty_pool") .. " " .. selected_pool.name.."?",
      confirm = i18n("host_pools.empty_pool"),
    }
  })
)

print(
  template.gen("modal_confirm_dialog.html", {
    dialog={
      id      = "change_member_pool_dialog",
      action  = "changeMemberPool(change_member_id)",
      title   = i18n("host_pools.change_member_pool"),
      message = i18n("host_pools.select_new_pool", {member='<span id="change_member_pool_dialog_member"></span>'}) ..
        '<br><br><select class="form-control" id="changed_host_pool" style="width:15em;">'..
        graph_utils.poolDropdown(ifId, "", {[selected_pool.id]=true, [host_pools_utils.DEFAULT_POOL_ID]=true})..
        '</select>',
      custom_alert_class = "",
      confirm = i18n("host_pools.change_pool"),
    }
  })
)

-- Create import dialog

print(
  template.gen("import_modal.html", {
    dialog={
      title   = i18n("host_pools.config_import"),
      label   = "",
      message = i18n("host_pools.config_import_message"),
      cancel  = i18n("cancel"),
      apply   = i18n("apply"),
    }
  })
)

print[[
  <script>
    const import_csrf = ']] print(ntop.getRandomCSRFValue()) print[[';

    $('#import-modal-btn').on("click", function(e) {

        // hide previous errors
        $("#import-error").hide();

        $("#import-modal form").off("submit");

        $('#btn-confirm-import').off('click').click(function(e) {
            const $button = $(this);

            let applied_value = null;

            $button.attr("disabled", "");

            // Read configuration file file
            var file = $('#import-input')[0].files[0];

            if (!file) {
                 $("#import-error").text(`${i18n.no_file}`).show();

                 // re-enable button
                 $button.removeAttr("disabled");
             } else {
                var reader = new FileReader();
                reader.onload = function () {
                    // Client-side configuration file format check
                    let json_conf = null
                    try { json_conf = JSON.parse(reader.result); } catch (e) {}

                    if (!json_conf || !json_conf['0']) {
                        $("#import-error").text(`${i18n.invalid_file}`).show();
                        // re-enable button
                        $button.removeAttr("disabled");
                    } else {
                        // Submit configuration file
                        $.post(`${http_prefix}/lua/rest/v1/set/pool/config.lua`, {
                            csrf: import_csrf,
                            JSON: JSON.stringify(json_conf)
                        })
                        .done((d, status, xhr) => {
                            if(xhr.status != 200) {
                              $("#import-error").text("]] print(i18n("request_failed_message")) print [[" + xhr.statusText).show();
                            } else if(d["rc_str"] != "OK") {
                                $("#import-error").text(d.rc_str).show();
                            } else {
                                location.reload();
                            }
                        })
                        .fail(({ status, statusText }) => {
                            $("#import-error").text(status + ": " + statusText).show();

                            // re-enable button
                            $button.removeAttr("disabled");
                        });

                     };
                 }
                 reader.readAsText(file, "UTF-8");
             }
        });

        $("#import-modal").on("submit", "form", function (e) {
            e.preventDefault();
            $("#btn-import").trigger("click");
        });
    });
  </script>
]]

--------------------------------------------------------------------------------

print[[
  <script>
    function recheckFields(input) {
      var member = input.val();
      var tr = input.closest("tr");
      var vlan_field = tr.find("td:nth-child(2) input");
      var icon_field = tr.find("td:nth-child(3) input");
      var select_field = tr.find("td:nth-child(4) select");
      var vlanicon_disabled = null;

      if (NtopUtils.is_mac_address(member)) {
        vlan_field.attr("disabled", true);
        vlanicon_disabled = false;
        select_field.attr("disabled", false);
      } else {
        var cidr = NtopUtils.is_network_mask(member, true);
        select_field.attr("disabled", true);

        if (cidr) {
          vlan_field.removeAttr("disabled");

          if((cidr.type == "ipv6" && cidr.mask == 128)
              || (cidr.type == "ipv4" && cidr.mask == 32)) {
            /* Custom alias only allowed for IP addresses */
            vlanicon_disabled = false;
          } else {
            vlanicon_disabled = true;
          }
        }
      }

      if (vlanicon_disabled != null) {
        icon_field.attr("disabled", vlanicon_disabled);
      }

      if (]] print(ternary(ifstats.has_macs, "false", "true")) print[[)
        select_field.attr("disabled", true);
    }

    /* Make the pair address,vlan unique */
    function addressValidator(input) {
      var member = input.val();

      /* this will be checked separately */
      if (! member)
        return true;

      recheckFields(input);
      return memberValueValidator(input);
    }

    function memberValidator(input) {
      var member = input.val();

      /* this will be checked separately */
      if (! member)
        return true;

      var is_mac = NtopUtils.is_mac_address(member);
      var identifier;

      if(is_mac) {
        identifier = member;
      } else {
        var address_value;
        var vlan_value;

        if (input.attr("name").endsWith("_vlan")) {
          var name = input.attr("name").split("_vlan")[0];
          address_value = $("input[name='" + name + "']", $("#table-manage-form")).val();
          vlan_value = member;
        } else {
          var name = input.attr("name") + "_vlan";
          address_value = member;
          vlan_value = $("input[name='" + name + "']", $("#table-manage-form")).val();
        }

        is_cidr = NtopUtils.is_network_mask(address_value, true);
        if (! is_cidr)
           /* this will be handled by addressValidator */
          return true;
        identifier = is_cidr.address + "/" + is_cidr.mask + "@" + vlan_value;
      }

      identifier = identifier.toLowerCase();
      var count = 0;

      $('input[name^="member_"]:not([name$="_vlan"])', $("#table-manage-form")).each(function() {
        var address_value = $(this).val();
        var is_cidr = NtopUtils.is_network_mask(address_value, true);

        var aggregated;
        if (! is_cidr) {
          aggregated = address_value;
        } else {
          var name = $(this).attr("name") + "_vlan";
          vlan_value = $("input[name='" + name + "']", $("#table-manage-form")).val();
          aggregated = is_cidr.address + "/" + is_cidr.mask + "@" + vlan_value;
        }
        aggregated = aggregated.toLowerCase()

        if (aggregated === identifier)
          count++;
      });

      return count == 1;
    }

    function poolNameUnique(input) {
      /* First update the host_pools names */
      datatableForEachRow("#table-create", function() {
        var pool_id = $("td:nth-child(1)", $(this)).html();
        var pool_name = $("td:nth-child(2) input", $(this)).val();

        for (var i=0; i<host_pools.length; i++)
          if (host_pools[i].id == pool_id)
            host_pools[i].name = pool_name;
      });

      var name = input.val();
      var count = 0;

      for (i=0; i<host_pools.length; i++) {
        if (host_pools[i].name === name)
          count++;
      }

      return count == 1;
    }
]]
if members_filtering ~= nil then
  print[[$("#pool_selector").attr("disabled", true);]]
end
print[[
  </script>
]]

-- ==== Manage tab ====

print [[

  <script>
    var addedMemberCtr = 0;
    var curDisplayedMembers = 0;
    var numPoolMembers = 0;

    var validator_options = {
      disable: true,
      custom: {
         member: memberValidator,
         address: addressValidator,
         unique: poolNameUnique,
      }, errors: {
         member: "]] print(i18n("host_pools.duplicate_member")) print[[.",
         address: "]] print(i18n("host_pools.invalid_member")) print[[.",
         unique: "]] print(i18n("host_pools.duplicate_pool")) print[[.",
      }
    }

    function decPoolMembers() { curDisplayedMembers--; numPoolMembers--; }

    function addPoolMember() {
      if($("#addPoolMemberBtn").attr("disabled"))
        return;

      if (datatableIsEmpty("#table-manage"))
         datatableRemoveEmptyRow("#table-manage");

      var member_id = addedMemberCtr++;
      var newid = "member_" + member_id;
      numPoolMembers++;

      var tr = $('<tr id=' + newid + '><td>]] printMemberAddressField('member_id') print[[</td><td class="text-center">]] printMemberVlanField('member_id') print[[</td><td>]] printAliasField('member_id') print[[</td><td>]] printIconField('member_id') print[[</td><td class="text-center"></td></tr>');
      datatableAddDeleteButtonCallback.bind(tr)(5, "datatableUndoAddRow('#" + newid + "', ']] print(i18n("host_pools.empty_pool")) print[[', '#addPoolMemberBtn', 'decPoolMembers')", "]] print("<i class='fas fa-undo-alt'></i>") print[[");
      $("#table-manage table").append(tr);
      $("input", tr).first().focus();

      var icon = $("td:nth-child(4)", tr);
      var icon_input = $("select", icon).first();
      curDisplayedMembers++;

      var is_disabled = ((curDisplayedMembers > ]] print(perPageMembers) print[[)
       || (numPoolMembers >= ]] print(host_pools_utils.LIMITED_NUMBER_POOL_MEMBERS.."") print[[));

      if(is_disabled)
          $("#addPoolMemberBtn").addClass("disabled").attr("disabled", "disabled");
        else
          $("#addPoolMemberBtn").removeClass("disabled").removeAttr("disabled");

      aysRecheckForm("#table-manage-form");
    }

    function deletePoolMember(member_id) {
      var form = $("#table-manage-form");
      var field = form.find("input[name='member_" + member_id + "']");

      if (field.attr("data-origin-value")) {
        var params = {};
        params.pool = ]] print(selected_pool.id) print[[;
        params.member_to_delete = field.attr("data-origin-value");
        params.csrf = "]] print(ntop.getRandomCSRFValue()) print[[";
        paramsToForm('<form method="post" action="]] print(manage_url) print[["></form>', params).appendTo('body').submit();
      }
    }

    function emptyCurrentPool() {
      var params = {};
      params.empty_pool = ]] print(selected_pool.id) print[[;
      params.csrf = "]] print(ntop.getRandomCSRFValue()) print[[";
      paramsToForm('<form method="post"></form>', params).appendTo('body').submit();
    }

    function changeMemberPool(member_id) {
      var form = $("#table-manage-form");
      var field = form.find("input[name='member_" + member_id + "']");

      if (field.attr("data-origin-value")) {
        var params = {};
        params.pool = $("#changed_host_pool").val();
        params.member = field.attr("data-origin-value");
        params.csrf = "]] print(ntop.getRandomCSRFValue()) print[[";
        paramsToForm('<form method="post" action="]] print(manage_url) print[["></form>', params).appendTo('body').submit();
      }
    }

    $("#table-manage").datatable({
      url: "]]
   print (ntop.getHttpPrefix())
   print [[/lua/get_host_pools.lua?ifid=]] print(ifId.."") print[[&pool=]] print(selected_pool.id) print[[&members_filter=]] print(members_filtering or "") print[[",
      title: "",
      perPage: ]] print(perPageMembers) print[[,
      forceTable: true,
      hidePerPage: true,

      buttons: [
         '<a id="addPoolMemberBtn" onclick="addPoolMember()" role="button" class="add-on btn" data-toggle="modal"><i class="fas fa-plus" aria-hidden="true"></i></a>'
      ], columns: [
         {
            title: "]] print(i18n("host_pools.member_address")) print[[",
            field: "column_member",
            css: {
               textAlign: 'left',
            }
         }, {
            title: "VLAN",
            field: "column_vlan",
            css: {
               width: '1%',
               textAlign: 'center',
            }
         }, {
            title: "]] print(i18n("host_pools.alias_or_manufacturer")) print[[",
            field: "column_alias",
            css: {
              width: '25%',
              textAlign: 'center',
              whiteSpace: 'nowrap',
            }
         }, {
            title: "Device Type",
            field: "column_icon",
            css: {
              width: '12%',
              textAlign: 'center',
              whiteSpace: 'nowrap',
            }
         }, {
            title: "]] print(i18n("actions")) print[[",
            css : {
               width: '20%',
               textAlign: 'center',
            }
         }, {
            field: "column_link",
            hidden: true,
         }, {
            field: "column_editable",
            hidden: true,
         }
      ], tableCallback: function() {
        var no_pools = false;
        curDisplayedMembers = 0;

        if (]] print(selected_pool.id) print[[ == ]] print(host_pools_utils.DEFAULT_POOL_ID) print[[) {
          datatableAddEmptyRow("#table-manage", "]] print(i18n("host_pools.no_pools_defined") .. " " .. i18n("host_pools.create_pool_hint")) print[[");
          no_pools = true;
        } else if(datatableIsEmpty("#table-manage")) {
          datatableAddEmptyRow("#table-manage", "]] print(i18n("host_pools.empty_pool")) print[[");
          $("#emptyPoolButton").attr("disabled", "disabled");
        } else {
          datatableForEachRow("#table-manage", function() {
            var member_address = $("td:nth-child(1)", $(this));
            var vlan = $("td:nth-child(2)", $(this));
            var alias = $("td:nth-child(3)", $(this));
            var icon = $("td:nth-child(4)", $(this));
            var link_value = $("td:nth-child(6)", $(this)).html().replace(/&nbsp;/gi,'');
            var editable = $("td:nth-child(7)", $(this)).html() == "true";

            var member_id = addedMemberCtr++;
            curDisplayedMembers++;

            /* Make member name editable */
            var value = member_address.html();
            var is_cidr = NtopUtils.is_network_mask(value);
            if (is_cidr) {
              old_value = member_address.html() + '@' + vlan.html();
              if (((is_cidr.type == "ipv4" && is_cidr.mask == 32)) ||
                 ((is_cidr.type == "ipv6" && is_cidr.mask == 128)))
                value = is_cidr.address;
            } else {
              old_value = member_address.html();
            }
            var input = $(']] printMemberAddressField('member_id', 'old_value') print[[');
            var address_input = $("input", input).first();
            address_input.val(value);
            member_address.html(input);

            /* Alias field */
            var input = $(']] printAliasField('member_id') print[[');
            input.val(alias.html().replace(/&nbsp;/gi,''));
            alias.html(input);

            /* Icon field */
            var div = $(']] printIconField('member_id') print[[');
            input = $("select", div);
            input.val(icon.html().replace(/&nbsp;/gi,''));
            icon.html(div);

            /* Make vlan editable */
            var input = $(']] printMemberVlanField('member_id') print[[');
            var vlan_value = parseInt(vlan.html());
            var vlan_input = $("input", input).first()
            vlan_input.val(vlan_value);
            vlan.html(input);

            recheckFields(address_input);

            if (! editable) {
              address_input.attr("disabled", "disabled");
              vlan_input.attr("disabled", "disabled");
            }

            if ((vlan_value > 0) && (is_cidr))
              value = value + " [VLAN " + vlan_value + "]";
]]

if #available_pools > 2 then
  print[[   datatableAddActionButtonCallback.bind(this)(5, "change_member_id ='" + member_id + "'; $('#change_member_pool_dialog_member').html('" + value +"'); $('#change_member_pool_dialog').modal('show');", "]] print(i18n("host_pools.change_pool")) print[[");]]
end

print[[
            datatableAddLinkButtonCallback.bind(this)(5, link_value, "]] print(i18n("host_pools.view")) print[[");
            if (!link_value) $("td:nth(5) a:nth(1)", this).css("visibility", "hidden");
            datatableAddDeleteButtonCallback.bind(this)(5, "delete_member_id ='" + member_id + "'; $('#delete_member_dialog_member').html('" + value +"'); $('#delete_member_dialog').modal('show');", "]] print("<i class='fas fa-trash'></i>") print[[");
          });

          if(numPoolMembers === 0)
            numPoolMembers = $("#table-manage").data("datatable").resultset.num_pool_members || 0;
          aysResetForm('#table-manage-form');
        }

        var is_disabled = (((! datatableIsLastPage("#table-manage-form"))
                                              || (no_pools))
                                              || (]] if members_filtering ~= nil then print("true") else print("false") end print[[)
                                              || (curDisplayedMembers > ]] print(perPageMembers) print[[)
                                              || (numPoolMembers >= ]] print(host_pools_utils.LIMITED_NUMBER_POOL_MEMBERS.."") print[[));

        if(is_disabled)
          $("#addPoolMemberBtn").addClass("disabled").attr("disabled", "disabled");
        else
          $("#addPoolMemberBtn").removeClass("disabled").removeAttr("disabled");

        $("#table-manage-form")
            .validator(validator_options)
            .on('submit', checkManagePoolForm);
      }
    });

    function checkManagePoolForm(event) {
      if (event.isDefaultPrevented())
        return false;

      var form = $("#table-manage-form");

      // build the settings object
      var settings = {};
      $('input[name^="member_"]:not([name$="_vlan"])', form).each(function() {
        var address = null;

        if((member = NtopUtils.is_network_mask($(this).val(), true))) {
          /* this is a network */
          var vlan_name = $(this).attr("name") + "_vlan";
          var vlan_field = $("input[name=" + vlan_name + "]", form);
          if (vlan_field.length == 1)
            address = member.address + "/" + member.mask + "@" + vlan_field.val();
        } else {
          /* this is a mac */
          address = $(this).val();
        }

        var original;
        if($(this).attr("data-origin-value"))
          original = $(this).attr("data-origin-value");
        else
          original = "";

        if (address !== null) {
          var alias_name = "alias_" + $(this).attr("name");
          var alias_field = $("input[name=" + alias_name + "]", form);
          var alias = alias_field.val();

          var icon_name = "icon_" + $(this).attr("name");
          var icon_field = $("select[name=" + icon_name + "]", form);
          var icon = icon_field.val();

          settings[address] = [original, alias, icon].join("|");
        }
      });

      // reset ays so that we can submit a custom form
      aysResetForm(form);

      // create a form with key-values encoded
      var params = paramsPairsEncode(settings);
      params.edit_members = "";
      params.pool = ]] print(selected_pool.id) print[[;
      params.csrf = "]] print(ntop.getRandomCSRFValue()) print[[";
      paramsToForm('<form method="post" action="]] print(manage_url) print[["></form>', params).appendTo('body').submit();
      return false;
    }
  </script>
]]

-- ==== Create tab ====

print [[
  <script>
    /* Assumption: this is sorted by pool id with possible gaps */
    var host_pools = ]] print(tableToJsObject(available_pools)) print[[;
    var maxPoolsNum = ]] print(tostring(host_pools_utils.LIMITED_NUMBER_TOTAL_HOST_POOLS)) print[[;

    function nextPoolId() {
      for (var i=0; i<host_pools.length-1; i++)
        if (parseInt(host_pools[i].id) + 1 < parseInt(host_pools[i+1].id))
          return parseInt(host_pools[i].id) + 1;

      return parseInt(host_pools[host_pools.length-1].id) + 1;
    }

    function deletePool(pool_id) {
      var params = {};
      params.pool_to_delete = pool_id;
      params.csrf = "]] print(ntop.getRandomCSRFValue()) print[[";

      var form = paramsToForm('<form method="post"></form>', params);
      if (pool_id == ]] print(selected_pool.id) print[[)
        form.attr("action", "?ifid=]] print(tostring(ifId)) print[[&page=pools#create");

      form.appendTo('body').submit();
    }

    function onPoolAddUndo(newid) {
      var pool_id = newid.split("added_pool_")[1];
      if (pool_id) {
        for (var i=0; i<host_pools.length; i++) {
          if (parseInt(host_pools[i].id) == pool_id) {
            /* Remove the element at index i */
            host_pools.splice(i, 1);
            break;
          }
        }
      }
    }

    function addPool() {
      if($("#addNewPoolBtn").attr("disabled"))
        return;

      var pool_id = nextPoolId();

      if (pool_id < maxPoolsNum) {
        if (datatableIsEmpty("#table-create"))
          datatableRemoveEmptyRow("#table-create");

        var newid = "added_pool_" + pool_id;

        /* Add at 'pool_id' position */
        host_pools.splice(pool_id, 0, {id:pool_id});

        var tr = $('<tr id=' + newid + '><td class="text-center" style="display:none;">' + pool_id + '</td><td>]]
printPoolNameField('pool_id') print[[</td><td align="center"></td></tr>');

        datatableAddDeleteButtonCallback.bind(tr)(3, "datatableUndoAddRow('#" + newid + "', ']] print(i18n("host_pools.no_pools_defined")) print[[', '#addNewPoolBtn', 'onPoolAddUndo')", "]] print("<i class='fas fa-undo-alt'></i>") print[[");
        $("#table-create table").append(tr);
        $("input", tr).focus();

        aysRecheckForm("#table-create-form");
      }

      recheckPoolAddButton();
    }

    function recheckPoolAddButton() {
      /* TODO use array size and perPage */
      var displayed_items = datatableGetNumDisplayedItems("#table-create");
      var numPools = host_pools.length;

      if ((numPools >= maxPoolsNum)
          || (displayed_items > ]] print(perPagePools) print[[)
          || (! datatableIsLastPage("#table-create-form")))
        $("#addNewPoolBtn").attr("disabled", "disabled").addClass("disabled");
      else
        $("#addNewPoolBtn").removeAttr("disabled").removeClass("disabled");
    }

    $("#table-create").datatable({
      url: "]]
   print (ntop.getHttpPrefix())
   print [[/lua/get_host_pools.lua?ifid=]] print(ifId.."") print[[",
      title: "",
      showPagination: true,
      forceTable: true,
      hidePerPage: true,
      perPage: ]] print(perPagePools) print[[,

      buttons: [
         '<a id="addNewPoolBtn" onclick="addPool()" role="button" class="add-on btn" data-toggle="modal"><i class="fas fa-plus" aria-hidden="true"></i></a>'
      ], columns: [
         {
            field: "column_pool_id",
            hidden: true,
         }, {
            title: "]] print(i18n("host_pools.pool_name")) print[[",
            field: "column_pool_name",
            css: {
               textAlign: 'left',
               width: '50%',
            }
         }, {
            field: "column_pool_undeletable",
            hidden: true,
         }, {
            title: "]] print(i18n("actions")) print[[",
            css : {
               width: '15%',
               textAlign: 'center',
            }
         }, {
           field: "column_pool_link",
           hidden: true,
         }
      ], tableCallback: function() {
        var pools_ctr = 0;

        if(datatableIsEmpty("#table-create")) {
          datatableAddEmptyRow("#table-create", "]] print(i18n("host_pools.no_pools_defined")) print[[");
        } else {
         datatableForEachRow("#table-create", function() {
            var pool_id = $("td:nth-child(1)", $(this)).html();
            var pool_name = $("td:nth-child(2)", $(this));
            var pool_undeletable = $("td:nth-child(3)", $(this)).html() === "true";
            var pool_link = $("td:nth-child(5)", $(this)).html();

            /* Make pool name editable */
            var input = $(']] printPoolNameField('pool_id') print[[');
            var value = pool_name.html();
            $("input", input).first().val(value);
            pool_name.html(input);

            if (pool_id == ]]  print(host_pools_utils.DEFAULT_POOL_ID) print[[) {
              $("input", input).first().attr("disabled", "disabled");
            } else {
              datatableAddLinkButtonCallback.bind(this)(4, pool_link, "View");
              value = value.replace("'", "\\'");
              datatableAddDeleteButtonCallback.bind(this)(4, "delete_pool_id ='" + pool_id + "'; $('#delete_pool_dialog_pool').html('" + value + "'); $('#delete_pool_dialog').modal('show');", "]] print("<i class='fas fa-trash'></i>") print[[");

              if (pool_undeletable)
                $("td:nth-child(4) a", $(this)).last().attr("disabled", "disabled");
            }

            pools_ctr++;
         });

         aysResetForm('#table-create-form');
        }

        recheckPoolAddButton();
        $("#table-create-form")
          .validator(validator_options)
          .on('submit', checkCreatePoolForm);
      }
    });

    function checkCreatePoolForm(event) {
      if (event.isDefaultPrevented())
        return false;

      var form = $("#table-create-form");

      // build the settings object
      var settings = {};
      $("input[name^='pool_']", form).each(function() {
        var pool_id = $(this).attr("name").split("pool_")[1];

        settings[pool_id] = $(this).val();
      });

      // reset ays so that we can submit a custom form
      aysResetForm(form);

      // create a form with key-values encoded
      var params = paramsPairsEncode(settings);
      params.edit_pools = "";
      params.csrf = "]] print(ntop.getRandomCSRFValue()) print[[";
      paramsToForm('<form method="post"></form>', params).appendTo('body').submit();

      return false;
    }
  </script>
]]

print[[
  <script>
    handle_tab_state($("#hostPoolsNav"), "manage");

    aysHandleForm("form", {
      handle_datatable: true,
      handle_tabs: true,
      ays_options: {addRemoveFieldsMarksDirty: true}
    });

    /* Retrigger the validation every second to clear outdated errors */
    setInterval(function() {
      $("form:data(bs.validator)").each(function(){
        $(this).data("bs.validator").validate();
      });
    }, 1000);
  </script>
]]

