------------------------------------------------------------------------
-- $Author: fpina $
-- $Revision: 161412 $  - $Date: 2015-01-29 11:09:11 -0200 (Thu, 29 Jan 2015) $
-------------------------------------------------------------------------
PROCESSES = {}

--..........................................................................
--..........................................................................

PROCESS_RUNNING = "RUNNING"
PROCESS_SLEEPING = "SLEEPING"
PROCESS_WAITING = "WAITING"
PROCESS_FINISHED = "FINISHED"

PROCESSES.ERROR_CODE = -1

--..........................................................................
--..........................................................................


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

--..........................................................................

function PROCESSES:init()
  servermanager.writeMsg("Detectado PID do processo SGA: "..tostring(SGA_PID))
  self:mountTableFields()
end

--..........................................................................

function PROCESSES:dumpPS()
  local processes_table = self.processes_table
  for i, v in ipairs(processes_table) do
    print( "" )
    local ptable = v
    for i,v in pairs(ptable) do
      io.write( i, ": ", v, "  " )
    end
  end
end

--..........................................................................

function PROCESSES:mountTableFields()
  self.ps_fields = {}
  self.ps_args = {}
  self.pattern = "%s*"
  self.arguments = ""

  if not PATTERNS.processes_formats or
     not type(PATTERNS.processes_formats) == type({}) then
     servermanager.writeError("PATTERNS.processes_formats mal formatado!!!")
     return
  end

  for i,v in pairs(PATTERNS.processes_formats) do
      table.insert( self.ps_fields, tostring(i) )
      table.insert( self.ps_args, tostring(v) )
  end

  local nfields = #self.ps_fields
  local f = 1
  while f <= nfields do
    self.arguments = self.arguments..self.ps_args[f]
    self.pattern = self.pattern.."(%S+)"
    if f ~= nfields then
       self.pattern = self.pattern.."%s+"
       self.arguments = self.arguments..","
    end
    f = f + 1
  end

  self.pattern = self.pattern.."%s*"
end

-- .......................................................................

function PROCESSES:getIndexesFromChildren(idx)
  local indexes = {}
  local procs = self.processes_table
  local nprocs = #procs
  local pid = self:getProcessId(idx)
  local i = 1
  while i <= nprocs do
    local proc = procs[i]
    local parent_pid = self:getParentProcessId(i)
    if parent_pid == pid then
       table.insert( indexes, i )
       local grand_indexes = self:getIndexesFromChildren(i)
       for i, v in ipairs(grand_indexes) do
         table.insert( indexes, v )
       end
    end
    i = i + 1
  end
  return indexes
end

-- .......................................................................

function PROCESSES:dumpStatus()
  if SGA_PID then return end                      -- XXX
  local myps = self:getAllInfoFromPid(SGA_PID)    -- XXX

  for i,v in ipairs(myps) do
    print( "--- ", i, v )
    for att,val in pairs(v) do
        print( "\t +)"..att.."\t\t"..tostring(val) )
    end
  end
end

-- .......................................................................

function PROCESSES:getAllInfoFromPid(pid)
  local idx = self:getIndex(pid)
  if not idx then return nil end
  local idx_children = self:getIndexesFromChildren(idx)
  local info = {}
  for i,v in ipairs(idx_children) do
      local tab = PROCESSES:getInfoFromIndex(v)
      table.insert( info, tab )
    end
  local mytab = PROCESSES:getInfoFromPid(pid)
  table.insert( info, mytab )
  return info
end

-- .......................................................................

function PROCESSES:getInfoFromIndex(idx)
  local errcode = self.ERROR_CODE
  local memorySize = self:getMemoryUsed(idx)
  local memoryRamSizeMb = (memorySize.ram or errcode)/ (1024 * 1024)  -- Rever!!!
  local memorySwapSizeMb = (memorySize.swap or errcode)/ (1024 * 1024)

  return {
    -- csbase_command_id = 0,
    csbase_command_pid = self:getProcessId(idx) or errcode,
    csbase_command_ppid = self:getParentProcessId(idx) or errcode,
    csbase_command_string = self:getCommandString(idx),
    csbase_command_exec_host = "unknown",
    csbase_command_state = self:getState(idx),
    processorId = self:getProcessorId(idx) or errcode,
    csbase_command_memory_ram_size_mb = memoryRamSizeMb,
    csbase_command_memory_swap_size_mb = memorySwapSizeMb,
    csbase_command_cpu_perc = self:getCPUPerc(idx) or errcode,
    csbase_command_cpu_time_sec = self:getCPUTime(idx) or errcode,
    csbase_command_wall_time_sec = self:getWallTime(idx) or errcode,
    csbase_command_user_time_sec = self:getUserTime(idx) or errcode,
    csbase_command_system_time_sec = self:getSystemTime(idx) or errcode,
    csbase_command_virtual_memory_size_mb = self:getVirtualMemory(idx) or errcode,
    csbase_command_bytes_in_kb = self:getBytesIn(idx) or errcode,
    csbase_command_bytes_out_kb = self:getBytesOut(idx) or errcode,
    csbase_command_disk_bytes_read_kb = self:getDiskBytesRead(idx) or errcode,
    csbase_command_disk_bytes_write_kb = self:getDiskBytesWrite(idx) or errcode,
  }
end

-- .......................................................................

function PROCESSES:getInfoFromPid(pid)
  local idx = self:getIndex(pid)
  if not idx then return nil end
  return self:getInfoFromIndex(idx)
end

-- .......................................................................

function PROCESSES:getProcessId(idx)
  return tonumber(self:getData(idx,"process_pid"))
end

-- .......................................................................

function PROCESSES:getParentProcessId(idx)
  return tonumber(self:getData(idx,"parent_process_pid"))
end

-- .......................................................................

function PROCESSES:getCommandString(idx)
  return tostring(self:getData(idx,"command"))
end

-- .......................................................................

function PROCESSES:getState(idx)
  return self:getData(idx,"state") or PROCESS_FINISHED
end

-- .......................................................................

function PROCESSES:getProcessorId(idx)
  return tonumber(self:getData(idx,"processor_id"))
end

-- .......................................................................

function PROCESSES:getMemoryUsed(idx)
  return {ram = tonumber(self:getData(idx,"ram_memory_used")),
          swap = tonumber(self:getData(idx,"swap_memory_used"))}
end

-- .......................................................................

function PROCESSES:getCPUPerc(idx)
  return tonumber(self:getData(idx,"cpu_percentage"))
end

-- .......................................................................

function PROCESSES:getCPUTime(idx)
  local str = self:getData(idx,"cpu_time")
  return self:convertStringTime(str)
end

-- .......................................................................

function PROCESSES:getWallTime(idx)
  local str = self:getData(idx,"wall_time")
  return self:convertStringTime(str)
end

-- .......................................................................

function PROCESSES:getUserTime(idx)
  return tonumber(self:getData(idx, "user_time"))
end

-- .......................................................................

function PROCESSES:getSystemTime(idx)
  return tonumber(self:getData(idx, "system_time"))
end

-- .......................................................................

function PROCESSES:getVirtualMemory(idx)
  return tonumber(self:getData(idx, "virtual_memory"))
end

-- .......................................................................

function PROCESSES:getBytesIn(idx)
  return tonumber(self:getData(idx, "bytes_in"))
end

-- .......................................................................

function PROCESSES:getBytesOut(idx)
  return tonumber(self:getData(idx, "bytes_out"))
end

-- .......................................................................

function PROCESSES:getDiskBytesRead(idx)
  return tonumber(self:getData(idx, "disk_bytes_read"))
end

-- .......................................................................

function PROCESSES:getDiskBytesWrite(idx)
  return tonumber(self:getData(idx, "disk_bytes_write"))
end

-- .......................................................................

function PROCESSES:getData(idx, attr)
  local ptable = self.processes_table
  idx = tonumber(idx)
  attr = tostring(attr)
  if not ptable or type(ptable) ~= type({}) then return nil end
  if not idx or idx <= 0 or idx > #ptable then return nil end
  if not ptable[idx] then return nil end
  return ptable[idx][attr]
end

-- .......................................................................

function PROCESSES:getIndex(pid)
   if PROCESSES.hash_table then
     return PROCESSES.hash_table[ pid ]
   else
     return nil
   end
end

-- .......................................................................

function PROCESSES:mountHashTable()
  self.hash_table = {}
  local processes_table = self.processes_table
  for idx, proc in ipairs(processes_table) do
     local pid = tonumber(proc.process_pid)
     if not pid then
       servermanager.writeError("Falha na leitura de pid: " ..
                 tostring(proc.process_pid))
     else
       PROCESSES.hash_table[ tonumber(proc.process_pid) ] = idx
     end
  end
end

-- .......................................................................

function PROCESSES:readProcessesInformation()
  local comm = tostring(PATTERNS.processes_command).." "..self.arguments
  local fname = os.tmpname()
  local csh_command = comm..' > '..fname
  -- servermanager.writeMsg("Executando comando:\n\t\t"..csh_command)

  os.execute( csh_command )
  -- servermanager.writeMsg("Executado comando para processos da máquina.")

  local collect_cpu_info_func
  if(servermanager.getHistoric() == true) then
     collect_cpu_info_func = PATTERNS.collect_process_cpu_info_function
	 if not collect_cpu_info_func or type(collect_cpu_info_func) ~= type(function() end) then
	    servermanager.writeError("Falha na configuração de:\n\t"..
	              "PATTERNS.collect_process_cpu_info_function.")
	 end
  end

  local interp_func = PATTERNS.process_interpreter_function
  if not interp_func or type(interp_func) ~= type(function() end) then
     servermanager.writeError("Falha na configuração de:\n\t"..
               "PATTERNS.process_interpreter_function.")
     return
  end

  -- Dados de uso de disco e rede devem ser pegos uma única vez pois são
  -- iguais para todos os processos
  io_table = {}
  if(servermanager.getHistoric() == true) then
    local collect_io_info_func = PATTERNS.collect_process_io_info_function
    if not collect_io_info_func or type(collect_io_info_func) ~= type(function() end) then
      servermanager.writeError("Falha na configuração de:\n\t"..
                               "PATTERNS.collect_process_io_info_function.")
    else
      io_table = collect_io_info_func()
    end
  end

  local fd = io.open(fname, "r")
  if not fd then
     servermanager.writeError("Falha na leitura do arquivo temporário.")
     return
  end

  local skip = tonumber( PATTERNS.processes_skip_lines ) or 0
  self.processes_table = {}
  for line in fd:lines() do
    if skip == 0 then
       string.gsub( line, self.pattern, function( ... )
          local table_line = {}
          local fields = PROCESSES.ps_fields
          local nfields = #fields
          local f = 1
          while f <= nfields do
            table_line[ fields[f] ] = arg[f]
            f = f + 1
          end

          if collect_cpu_info_func then
            collect_cpu_info_func( table_line )
          end

          for k,v in pairs(io_table) do
            table_line[k] = v
          end

          interp_func( table_line )
          table.insert( PROCESSES.processes_table, table_line )
       end )
    else
      skip = skip - 1
    end
  end
  fd:close()
  os.remove(fname)
  self:mountHashTable()
end

-- .......................................................................

function PROCESSES:convertStringTime(str) -- Retorna segundos
  if not str then
    return nil
  end
  -- str pode ser "SS", "MM:SS", "HH:MM:SS" ou "D-HH:MM:SS"
  local tot = 0
  local day = 0
  str=string.gsub(str, "^(%d+)-", function (n)
    day = n
    return ""
  end)
  string.gsub(str, "(%d+)", function (n)
    tot = tot * 60 + n
  end)
  return (day * 24 * 3600 ) + tot
end
