--
-- (C) 2013-24 - ntop.org
--
-- trace_script_duration = true
local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path

local ifstats = interface.getStats()
if (ifstats.has_seen_pods or ifstats.has_seen_containers) then
    -- Use a different flows page
    dofile(dirs.installdir .. "/scripts/lua/inc/ebpf_flows_stats.lua")
    return
end

require "lua_utils"
require "flow_utils"
local page_utils = require("page_utils")
local template = require "template_utils"
local have_nedge = ntop.isnEdge()

sendHTTPContentTypeHeader('text/html')

page_utils.print_header_and_set_active_menu_entry(ternary(have_nedge, page_utils.menu_entries.nedge_flows,
    page_utils.menu_entries.active_flows))

dofile(dirs.installdir .. "/scripts/lua/inc/menu.lua")

local draw = _GET["draw"] or 0
local sort = _GET["sort"] or "flows"
local order = _GET["order"] or "desc"
local start = _GET["start"] or 0
local length = _GET["length"] or 10
local page = _GET["page"]
local client = _GET["client"]
local server = _GET["server"]
local flow_info = _GET["flow_info"]
local aggregation_criteria = _GET["aggregation_criteria"] or "client_server_srv_port_app_proto"
local base_url = ntop.getHttpPrefix() .. "/lua/flows_stats.lua"
local page_params = {
    ifid = interface.getId(),
    client = client,
    server = server,
    flow_info = flow_info
}

page_utils.print_navbar(i18n('graphs.active_flows'), base_url .. "?", {{
    active = page == "flows" or page == nil,
    url = base_url .. "?page=flows&traffic_type=unicast",
    page_name = "flows",
    label = "<i class=\"fas fa-lg fa-home\"></i>"
}, {
    url = base_url .. "?page=analysis&aggregation_criteria=" .. aggregation_criteria .. "&draw=" .. draw .. "&sort=" ..
        sort .. "&order=" .. order .. "&start=" .. start .. "&length=" .. length,
    active = page == "analysis",
    page_name = "analysis",
    label = i18n("analysis")
}})

if ((page == "flows" or page == nil) and ntop.isnEdge()) then
    -- nDPI application and category
    local application = _GET["application"]
    if (application ~= nil) then
        local application_split = string.split(application, "%.")
        if application_split and #application_split == 2 then
            -- 5.26
        else
            local _application = tonumber(application)
            if (_application) then
                application = interface.getnDPIProtoName(_application)
            end
        end
    end
    local category = _GET["category"]
    local hosts = _GET["hosts"]
    local host = _GET["host"]
    local talking_with = _GET["talkingWith"]
    local vhost = _GET["vhost"]
    local flowhosts_type = _GET["flowhosts_type"]
    local ipversion = _GET["version"]
    local l4proto = _GET["l4proto"]
    local vlan = _GET["vlan"]
    local icmp_type = _GET["icmp_type"]
    local icmp_code = _GET["icmp_cod"]
    local dscp_filter = _GET["dscp"]
    local host_pool = _GET["host_pool_id"]
    local traffic_profile = _GET["traffic_profile"]
    -- remote exporters address and interfaces
    local deviceIP = _GET["deviceIP"]
    local inIfIdx = _GET["inIfIdx"]
    local outIfIdx = _GET["outIfIdx"]
    local traffic_type = _GET["traffic_type"] or "unicast"
    local alert_type = _GET["alert_type"]
    local alert_type_severity = _GET["alert_type_severity"]
    local tcp_state = _GET["tcp_flow_state"]
    local port = _GET["port"]
    local network_id = _GET["network"]
    
    local prefs = ntop.getPrefs()
    local duration_or_last_seen = prefs.flow_table_time
    local begin_epoch_set = (ntop.getPref("ntopng.prefs.first_seen_set") == "1")
    local flows_filter = getFlowsFilter()
    flows_filter.statusFilter = nil -- remove the filter, otherwise no menu entries will be shown
    local flowstats = interface.getActiveFlowsStats(host, flows_filter, false, talking_with, client, server, flow_info)
    local active_msg = getFlowsTableTitle(ntop.getHttpPrefix())
    local mini_title = i18n("flow_details.purge_time", {
        purge_time = ntop.getPref("ntopng.prefs.flow_max_idle"),
        prefs_url = ntop.getHttpPrefix() .. '/lua/admin/prefs.lua?tab=in_memory'
    })
    if (category ~= nil) then
        page_params["category"] = category
    end

    if (application ~= nil) then
        page_params["application"] = application
    end

    if (host ~= nil) then
        page_params["host"] = host
    end

    if (port ~= nil) then
        page_params["port"] = port
    end

    if (vhost ~= nil) then
        page_params["vhost"] = vhost
    end

    if (hosts ~= nil) then
        page_params["hosts"] = hosts
    end

    if (port ~= nil) then
        page_params["port"] = port
    end

    if (ipversion ~= nil) then
        page_params["version"] = ipversion
    end

    if (l4proto ~= nil) then
        page_params["l4proto"] = l4proto
    end

    if (deviceIP ~= nil) then
        page_params["deviceIP"] = deviceIP
    end

    if (inIfIdx ~= nil) then
        page_params["inIfIdx"] = inIfIdx
    end

    if (outIfIdx ~= nil) then
        page_params["outIfIdx"] = outIfIdx
    end

    if (vlan ~= nil) then
        page_params["vlan"] = vlan
    end

    if (traffic_type ~= nil) then
        page_params["traffic_type"] = traffic_type
    end

    if (alert_type ~= nil) then
        page_params["alert_type"] = alert_type
    end

    if (alert_type_severity ~= nil) then
        page_params["alert_type_severity"] = alert_type_severity
    end

    if (tcp_state ~= nil) then
        page_params["tcp_flow_state"] = tcp_state
    end

    if (network_id ~= nil) then
        page_params["network"] = network_id
    end

    if (flowhosts_type ~= nil) then
        page_params["flowhosts_type"] = flowhosts_type
    end

    if ((icmp_type ~= nil) and (icmp_code ~= nil)) then
        page_params["icmp_type"] = icmp_type
        page_params["icmp_cod"] = icmp_code
    end

    if (dscp_filter ~= nil) then
        page_params["dscp"] = dscp_filter
    end

    if (host_pool ~= nil) then
        page_params["host_pool_id"] = host_pool
    end

    if (traffic_profile ~= nil) then
        page_params["traffic_profile"] = traffic_profile
    end
    local is_chart_printed = false

    print [[<div class="d-flex m-1">]]

    if is_chart_printed then
        print [[<h3 class="m-auto">]]
    else
        print [[<h3 class="me-auto mt-auto mb-auto">]]
    end

    print(active_msg)
    print [[</h3>]]

    if (table.len(page_params) > 0) and (not isEmptyString(page_params["application"])) then
        is_chart_printed = true
        print [[
         <div class="col-5">
            <div class="info-stats">
               <ul class="nav-side m-0 ps-5 ms-1" style="list-style-type: none;">
                  <li class="nav-item">
                     <div class="up">
                        <i class="fas fa-arrow-up" data-original-title="" title=""></i>
                        <span id="upload-filter-traffic-chart" class="line">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</span>
                        <span class='d-inline-block text-end' style='width: 13ch'>
                           <span id="upload-filter-traffic-value">0 kbit/s</span> |
                        </span>
                        <span id="filtered-flows-tot-bytes">]]
        print(i18n("flows_page.tot_bytes"))
        print [[</span>
                        <span id="filtered-flows-tot-bytes-value">0 B</span>
                     </div>
                  </li>
                  <li class="nav-item">
                     <div class="down">
                        <i class="fas fa-arrow-down" data-original-title="" title=""></i>
                        <span id="download-filter-traffic-chart" class="line">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</span>
                        <span class='d-inline-block text-end' style='width: 13ch'>
                           <span id="download-filter-traffic-value">0 kbit/s</span> |
                        </span>
                        <span id="filtered-flows-tot-throughput">]]
        print(i18n("flows_page.tot_throughput"))
        print [[</span>
                        <span id="filtered-flows-tot-throughput-value">0 kbit/s</span>
                     </div>
                  </li>
               </ul>
            </div>
         </div>
   ]]
    end

    print [[<h6 class="ms-auto mt-auto mb-auto">]]
    print(mini_title)
    print [[</h6>]]
    print [[</div>]]

    print [[
         <div id="table-flows"></div>
           <script>
      var url_update = "]]
    print(getPageUrl(ntop.getHttpPrefix() .. "/lua/get_flows_data.lua", page_params))

    print('";')

    print [[
   
   	 var table = $("#table-flows").datatable({
   			url: url_update ,
            tableCallback: function(test_tmp)  {
               ]]
    initFlowsRefreshRows()
    print [[
            },
   ]]

    preference = tablePreferences("rows_number", _GET["perPage"])
    if (preference ~= "") then
        print('perPage: ' .. preference .. ",\n")
    end

    print(" title: \"")

    print [[",
            showFilter: true,
            showPagination: true,
   ]]

    -- Automatic default sorted. NB: the column must be exists.
    print('sort: [ ["' .. getDefaultTableSort("flows") .. '","' .. getDefaultTableSortOrder("flows") .. '"] ],\n')

    print('buttons: [')

    printActiveFlowsDropdown(base_url, page_params, ifstats, flowstats)

    print(" ],\n")

    print [[
      columns: [
         {
            title: "",
            field: "key",
            hidden: true,
         }, {
            title: "",
            field: "hash_id",
            hidden: true,
         }, {
            title: "]]
    print(i18n("actions"))
    print [[",
            field: "column_key", /* This is the serial number but called key for placing the flow button pointing to the flow key */
            sortable: true,
            css: {
               textAlign: 'center',
               whiteSpace: 'nowrap'

            }
         }, {
            title: "]]
    print(i18n("application"))
    print [[",
            field: "column_ndpi",
            sortable: true,
            css: {
               textAlign: 'left',
               whiteSpace: 'nowrap'
            }
         }, {
            title: "]]
    print(i18n("proto"))
    print [[",
            field: "column_proto_l4",
            sortable: true,
            css: {
               textAlign: 'left',
               whiteSpace: 'nowrap'
            }
         },
   ]]

    print [[
         {
            title: "]]
    print(i18n("client"))
    print [[",
            field: "column_client",
            sortable: true,
            css: {
               whiteSpace: 'nowrap'
            }
         }, {
            title: "]]
    print(i18n("server"))
    print [[",
            field: "column_server",
            sortable: true,
            css: {
               whiteSpace: 'nowrap'
            }
   
         },
   ]]

    if begin_epoch_set == true then
        print [[
         {
            title: "]]
        print(i18n("first_seen"))
        print [[",
            field: "column_first_seen",
            sortable: true,
            css: {
               whiteSpace: 'nowrap',
               textAlign: 'center',
            }
         },
      ]]
    end

    if duration_or_last_seen == false then
        print [[
         {
            title: "]]
        print(i18n("duration"))
        print [[",
            field: "column_duration",
            sortable: true,
            css: {
               whiteSpace: 'nowrap',
               textAlign: 'center',
            }
         },
      ]]
    else
        print [[
         {
            title: "]]
        print(i18n("last_seen"))
        print [[",
            field: "column_last_seen",
            sortable: true,
            css: {
               whiteSpace: 'nowrap',
               textAlign: 'center'
            }
         },
      ]]
    end

    print [[{
            title: "]]
    print(i18n("score"))
    print [[",
            field: "column_score",
            hidden: ]]
    print(ternary(isScoreEnabled(), "false", "true"))
    print [[,
            sortable: true,
            css: {
               textAlign: 'center',
               whiteSpace: 'nowrap'
            }
         }, {
            title: "]]
    print(i18n("breakdown"))
    print [[",
            field: "column_breakdown",
            sortable: false,
            css: {
               textAlign: 'center',
               whiteSpace: 'nowrap'
            }
         }, {
            title: "]]
    print(i18n("flows_page.actual_throughput"))
    print [[",
            field: "column_thpt",
            sortable: true,
            css: {
               textAlign: 'right',
               whiteSpace: 'nowrap'
            }
         }, {
            title: "]]
    print(i18n("flows_page.total_bytes"))
    print [[",
            field: "column_bytes",
            sortable: true,
            css: {
               textAlign: 'right',
               whiteSpace: 'nowrap'
            }
         }, {
            title: "]]
    print(i18n("info"))
    print [[",
            field: "column_info",
            sortable: false,
            css: {
               textAlign: 'left',
               whiteSpace: 'nowrap'
            }
         },]]
    if interface.isPacketInterface() == false then
        print [[
         {
            title: "]]
        print(i18n('flow_devices.exporter_ip'))
        print [[",
            field: "column_device_ip",
            sortable: true,
            css: {
               textAlign: 'left',
               whiteSpace: 'nowrap'
            }
         }, {
            title: "]]
        print(i18n('flows_page.inIfIdx'))
        print [[",
            field: "column_in_index",
            sortable: true,
            css: {
               textAlign: 'left',
               whiteSpace: 'nowrap'
            }
         }, {
            title: "]]
        print(i18n('flows_page.outIfIdx'))
        print [[",
            field: "column_out_index",
            sortable: true,
            css: {
               textAlign: 'left',
               whiteSpace: 'nowrap'
            }
         },
      ]]
    end
    print [[
         ]
      });
   ]]

    if (have_nedge) then
        printBlockFlowJs()
    end
    print [[
   </script>
   ]]
    if (table.len(page_params) > 0) and (not isEmptyString(page_params["application"])) then
        print([[
         <script type='text/javascript'>
   
            let old_totBytesSent = 0;
            let old_totBytesRcvd = 0;
            let refresh_rate     = 5; /* seconds */
   
            $(document).ready(function() {
               const downloadChart = $("#download-filter-traffic-chart").peity("line", { width: 64, fill: "lightgreen" });
               const uploadChart = $("#upload-filter-traffic-chart").peity("line", { width: 64 });
               
               function pushNewValue(chart, newValue) {
                  const values = chart.text().split(",");
                  values.shift();
                  values.push(newValue);
   
                  chart
                    .text(values.join(","))
                    .change()
               }
   
               function updateChart() {
   
                  const request = $.get("]] ..
                  getPageUrl(ntop.getHttpPrefix() .. "/lua/rest/v2/get/flow/traffic_stats.lua", page_params) .. "&ifid=" ..
                  interface.getId() .. [[");
                  request.then((data) => {
                     let throughput_bps_sent = (8 * (data.rsp.totBytesSent - old_totBytesSent)) / refresh_rate;
                     let throughput_bps_rcvd = (8 * (data.rsp.totBytesRcvd - old_totBytesRcvd)) / refresh_rate;
                     
                     if (throughput_bps_sent < 0) throughput_bps_sent = 0;
                     if (throughput_bps_rcvd < 0) throughput_bps_rcvd = 0;
                     
                     let tot_throughput = (throughput_bps_sent + throughput_bps_rcvd) || 0;
   
                     if ((old_totBytesSent > 0) || (old_totBytesRcvd > 0)) {
                       /* Second iteration or later */
                       pushNewValue(downloadChart, -throughput_bps_rcvd);
                       pushNewValue(uploadChart, throughput_bps_sent);
                       $('#download-filter-traffic-value').html(NtopUtils.bitsToSize(throughput_bps_rcvd, 1000));                  
                       $('#upload-filter-traffic-value').html(NtopUtils.bitsToSize(throughput_bps_sent, 1000));
                       $('#filtered-flows-tot-throughput-value').html(NtopUtils.bitsToSize(tot_throughput, 1000));
                    }
   
                     /* Keep the old value for computing the differnce at the next round */
                     old_totBytesSent = data.rsp.totBytesSent || 0;
                     old_totBytesRcvd = data.rsp.totBytesRcvd || 0;
                     $('#filtered-flows-tot-bytes-value').html(NtopUtils.bytesToSize(old_totBytesSent + old_totBytesRcvd));
                  })
               }
   
               setInterval(() => { updateChart() }, refresh_rate*1000);
   
               updateChart();
            })
         </script>
      ]])
    end
elseif (page == "flows" or page == nil) then
    local has_exporters = false
    if ntop.isPro() and interface.isPacketInterface() == false then
        local flowdevs = interface.getFlowDevices() or {}
        if table.len(flowdevs) > 0 then
            has_exporters = true
        end
    end
    local json = require "dkjson" 
    local json_context = json.encode({
        ifid = ifstats.id,
        has_exporters = has_exporters,
        is_viewed = interface.isViewed(),
        is_clickhouse_enabled = hasClickHouseSupport(),
        is_pcap = interface.isPcapDumpInterface(),
        csrf = ntop.getRandomCSRFValue(),
        is_enterprise_l = ntop.isEnterpriseL()
    })
    template.render("pages/vue_page.template", { vue_page_name = "PageFlowsList", page_context = json_context })
else
    -- Analysis

    local json = require 'dkjson'
    -- Format VLANs dropdown
    local tmp_vlans = {}
    local vlans = {}
    local vlan_list = interface.getVLANsList() or {}

    if table.len(vlan_list) > 0 then
        vlan_list = vlan_list.VLANs
    end

    for _, vlan_info in pairsByField(vlan_list or {}, 'vlan_id', asc) do
        local label = i18n("hosts_stats.vlan_title", {
            vlan = getFullVlanName(vlan_info.vlan_id)
        })
        local currently_active = false

        if vlan_info.vlan_id == 0 then
            label = i18n('no_vlan')
        end

        tmp_vlans[#tmp_vlans + 1] = {
            label = label,
            id = vlan_info.vlan_id,
            countable = false,
            key = vlan_info.vlan_id,
            currently_active = (vlan == vlan_info.vlan_id or currently_active)
        }
    end
    if (#tmp_vlans > 1) then
        local currently_active = false

        tmp_vlans[#tmp_vlans + 1] = {
            label = i18n("flows_page.all_vlan_ids"),
            id = -1,
            countable = false,
            key = -1,
            currently_active = (vlan == -1 or currently_active)
        }
    end

    -- Order again by name
    for _, vlan in pairsByField(tmp_vlans or {}, 'label', asc_insensitive) do
        vlans[#vlans + 1] = vlan
    end

    local context = {
        ifid = ifId,
        vlans = json.encode(vlans),
        aggregation_criteria = aggregation_criteria,
        is_ntop_enterprise_m = ntop.isEnterpriseM(),
        draw = draw,
        sort = sort,
        order = order,
        start = start,
        length = length,
        host = "",
        csrf = ntop.getRandomCSRFValue()
    }

    local json_context = json.encode(context)
    template.render("pages/vue_page.template", { vue_page_name = "PageAggregatedLiveFlows", page_context = json_context })
 
end

dofile(dirs.installdir .. "/scripts/lua/inc/footer.lua")
