--
-- (C) 2014 - ntop.org
--

-- This file contains the description of all functions
-- used to interact with sqlite

dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path

require "lua_utils"
require "template"
local j = require ("dkjson")

local SqliteClass = {} -- the table representing the class, which will double as the metatable for the instances
SqliteClass.__index = SqliteClass -- failed table lookups on the instances should fallback to the class table, to get methods

-- syntax equivalent to "SqliteClass.new = function..."
function SqliteClass.new(init)
  local self = setmetatable({}, SqliteClass)

  self.debug = false
  self.class_benchmark = os.clock()
  self.query_benchmark = os.clock()
  self.flows_benchmark = os.clock()

  -- Template
  self.flow_template = {
  ["IPV4_SRC_ADDR"]   = function (table,val) table["cli.ip"]          = val           end,
  ["L4_SRC_PORT"]     = function (table,val) table["cli.port"]        = tonumber(val) end,
  ["IPV4_DST_ADDR"]   = function (table,val) table["srv.ip"]          = val           end,
  ["L4_DST_PORT"]     = function (table,val) table["srv.port"]        = tonumber(val) end,
  ["PROTOCOL"]        = function (table,val)
  if (l4_template[tonumber(val)] ~= nil ) then
    table["proto.l4"]        = l4_template[tonumber(val)]
  else
    table["proto.l4"]        = val
  end
  end,
    -- ["SRC_VLAN"]        = function (table,val) table["vlan"]            = tonumber(val) end,
    -- ["DST_VLAN"]        = function (table,val) table["vlan"]            = tonumber(val) end,
    ["L7_PROTO_NAME"]   = function (table,val) table["proto.ndpi"]      = val           end,
    ["TCP_FLAGS"]       = function (table,val) table["tcp_flags"]       = tonumber(val) end,
    ["OUT_PKTS"]        = function (table,val) table["cli2srv.packets"] = tonumber(val) end,
    ["OUT_BYTES"]       = function (table,val) table["cli2srv.bytes"]   = tonumber(val) end,
    ["IN_PKTS"]         = function (table,val) table["srv2cli.packets"] = tonumber(val) end,
    ["IN_BYTES"]        = function (table,val) table["srv2cli.bytes"]   = tonumber(val) end,
  }

  -- Query
  self.response = nil
  self.db = nil;
  self.query = nil;
  self.number_rows = 0;

  -- Flows
  self.flows = nil
  self.flows_num = 0


  return self
end

-- ###################################
-- Getter and setter

function SqliteClass.setDebug(self, bool)
  if (bool == nil) then bool = false end
  self.debug = bool
  return self.debug
end

function SqliteClass.getDebug(self)     return self.debug end
function SqliteClass.getFlows(self)     return self.flows end
function SqliteClass.getFlowsNum(self)  return self.flows_num end
function SqliteClass.getResponse(self)  return self.response end
function SqliteClass.getDB(self)        return self.db end
function SqliteClass.getQuery(self)     return self.query end


-- ###################################

function SqliteClass.execQuery(self, db ,query)
  if (db == nil) then return -1 end
  if (query == nil) then return -2 end

  self.db = db;
  self.query = query;

  self.query_benchmark = os.clock()
  -- io.write(dirs.workingdir ..db..'\n')
  self.response = ntop.execQuery(dirs.workingdir ..db , query)
  n_rows = ntop.execQuery(dirs.workingdir ..db , "SELECT COUNT (*) as rows_number FROM flows")

  if (n_rows ~= nil) then
    self.number_rows = n_rows[1]["rows_number"]
  end

  Sqlite:benchmark("query",os.clock())

  return self.response
end

-- ###################################

function SqliteClass.getFlows(self)
  if (self.response == nil) then return nil end
  -- Init some parameters
  self.flows_benchmark = os.clock()
  self.flows = {}
  num = 0

  for _k,_v in pairs(self.response) do

       -- init table of table
       self.flows[num] = {}
       self.flows[num]["ID"] = tonumber(self.response[_k]["ID"])
       self.flows[num]["vlan"] = tonumber(self.response[_k]["vlan_id"])
       self.flows[num]["bytes"] = tonumber(self.response[_k]["bytes"])
       self.flows[num]["duration"] = tonumber(self.response[_k]["duration"])

       -- Throughput
       -- io.write((self.flows[num]["bytes"] / self.flows[num]["duration"])..'\n')
       self.flows[num]["throughput_bps"] = (self.flows[num]["bytes"] / 8 ) / (self.flows[num]["duration"])
       self.flows[num]["throughput_trend_bps"] = 3


       local info, pos, err = j.decode(self.response[_k]["json"], 1, nil)

       if (info == nil) then
        traceError(TRACE_ERROR,TRACE_CONSOLE,"Impossible read json form sqlite: ".. err)
      else

        for key,val in pairs(info) do

          label_key = nil

          -- Check if the option --jsonlabes is active
          if (rtemplate[tonumber(key)] ~= nil) then label_key = rtemplate[tonumber(key)] end
          if (template[key] ~= nil) then label_key = key end

          -- Convert template id into template name
          if (label_key ~= nil) then

            if (self.flow_template[label_key] ~= nil) then
              -- Call conversion function in order to convert some value to number if it is necessary
              self.flow_template[label_key](self.flows[num],val)
            else
              -- Leave the default key and value
              self.flows[num][key] = val
            end

          end
        end -- info loop

        -- Custom parameters
        if (self.flows[num]["proto.ndpi"] == nil) then
          self.flows[num]["proto.ndpi"] = "Unknown"
        end
      end -- info == nil

      num = num + 1
    end -- for


    -- self.writeFlows(self)
    self.flows_num = num
    Sqlite:benchmark("flows",os.clock())

    return self.flows
  end


-- ###################################

function SqliteClass.getRowsNumber(self)
  return (self.number_rows);
end

-- ###################################
-- Utils

function SqliteClass.benchmark(self,type,time)
  if ((time ~= nil) and (self.debug))then
    bk_text = "class"
    bk = self.class_benchmark

    if (type == "query") then
     bk = self.query_benchmark
     bk_text = type
     elseif (type == "flows") then
      bk = self.flows_benchmark
      bk_text = type
    end

    traceError(TRACE_DEBUG,TRACE_CONSOLE,string.format(bk_text .. ": elapsed time: %.4f", time - bk))
  end
end

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

function SqliteClass.writeFlows(self)
  if (self.flows ~= nil) then
    for key,val in pairs(self.flows) do
      traceError(TRACE_DEBUG,TRACE_CONSOLE,'Flow: '..key)
      for k,v in pairs(self.flows[key]) do
        traceError(TRACE_DEBUG,TRACE_CONSOLE,'\t'..k..' - '..v)
      end
    end
  end
end


function getParameters(datetime,action)

  datetime_tbl = cleanDateTime(datetime, action)

  datetime_tbl["displayed"] = os.date("%m/%d/%Y %I:%M %p",datetime_tbl["epoch"])
  traceError(TRACE_DEBUG,TRACE_CONSOLE,'Displayed: ['..datetime_tbl["displayed"] .. ']')
  datetime_tbl["query"] = os.date("/%Y/%m/%d/%H/%M",datetime_tbl["epoch"])
  traceError(TRACE_DEBUG,TRACE_CONSOLE,'query: ['..datetime_tbl["query"] .. ']')

  return datetime_tbl
end



function cleanDateTime(datetime,action)

  if (datetime == nil) then return {} end
  traceError(TRACE_DEBUG,TRACE_CONSOLE,'Initial date and time: '..datetime)

  tbl = split(datetime," ")
  q_date = tbl[1]
  q_time = tbl[2]
  q_type = tbl[3]

  traceError(TRACE_DEBUG,TRACE_CONSOLE,'Default value: ['..q_date..']['..q_time .. '][' .. q_type..']')

  date_tbl  = split(q_date,"/")
  q_month    = tonumber(date_tbl[1])
  q_day      = tonumber(date_tbl[2])
  q_year     = tonumber(date_tbl[3])

  traceError(TRACE_DEBUG,TRACE_CONSOLE,'Default value: ['..q_day..']['..q_month .. '][' .. q_year..']')

  time_tbl = split(q_time,":")
  q_hour   = tonumber(time_tbl[1])
  q_min  = tonumber(time_tbl[2])

  if (q_type == "PM") then q_hour = q_hour + 12 end

  traceError(TRACE_DEBUG,TRACE_CONSOLE,'Default value: ['..q_hour..']['..q_min .. ']')

  q_epoch = os.time({year=q_year, month=q_month, day=q_day, hour=q_hour, min=q_min})

  traceError(TRACE_DEBUG,TRACE_CONSOLE,'epoch: ['.. q_epoch ..']')

  if (action ~= nil) then
    if (action == "newer") then
      q_epoch = q_epoch + 300 -- plus 5 min
    else
      q_epoch = q_epoch - 300 -- min 5 min
    end
  end

  mktime = os.date("%m/%d/%Y %I:%M %p", q_epoch)

  traceError(TRACE_DEBUG,TRACE_CONSOLE,'time: ['.. mktime ..']')

  ret_tbl = {}
  ret_tbl["year"] = q_year
  ret_tbl["month"] = month
  ret_tbl["day"] = q_day

  ret_tbl["q_hour"] = q_q_hour
  ret_tbl["q_min"] = q_q_min

  ret_tbl["epoch"] = q_epoch

  return ret_tbl

end

-- ###################################

-- Don't remove below line
Sqlite = SqliteClass.new()
-- Sqlite:benchmark(os.clock())
Sqlite:setDebug(false)
