-- Chaves padrões de configuração da biblioteca
_libDefaltConfigKeys = {
  "csbase_num_processors",
  "csbase_memory_ram_info_mb",
  "csbase_memory_swap_info_mb",
  "csbase_job_control_actions",
}

-- Chaves padrões de configuração definidas pelo usuário
_userDefaltConfigKeys = _libDefaltConfigKeys

-- Chaves de configuração disponíveis
-- O valor true indica que é um chave obrigatório e false indica que é opcional
_allConfigKeys = {
  csbase_num_processors = true,
  csbase_memory_ram_info_mb = true,
  csbase_memory_swap_info_mb = true,
  csbase_job_control_actions = true,
}

-- Chaves padrões de informações dinâmicas da biblioteca
_libDefaltInfoKeys = {
  "csbase_load_perc",
  "csbase_memory_ram_free",
  "csbase_memory_swap_free",
  "csbase_number_of_jobs",
}

-- Chaves padrões de informações dinâmicas definidas pelo usuário
_userDefaltInfoKeys = _libDefaltInfoKeys

-- Chaves de informações dinâmicas disponíveis
-- O valor true indica que é um chave obrigatório e false indica que é opcional
_allInfoKeys = {
  csbase_load_perc = true,
  csbase_memory_ram_free = true,
  csbase_memory_swap_free = true,
  csbase_number_of_jobs = true,
}

-- Chaves padrões de informações de processo da biblioteca
_libDefaltProcessKeys = {
  "csbase_command_pid",
  "csbase_command_string",
  "csbase_command_exec_host",
  "csbase_command_state",
  "csbase_command_memory_ram_size_mb",
  "csbase_command_memory_swap_size_mb",
  "csbase_command_cpu_perc",
  "csbase_command_time_sec",
  "csbase_command_wall_time_sec",
  "csbase_command_user_time_sec",
  "csbase_command_system_time_sec",
}

-- Chaves de informações de processos definidas pelo usuário
_userDefaltProcessKeys = _libDefaltProcessKeys

-- Chaves de informações de processos disponíveis
-- O valor true indica que é um chave obrigatório e false indica que é opcional
_allProcessKeys = {
  csbase_command_pid = true,
  csbase_command_string = true,
  csbase_command_exec_host = true,
  csbase_command_state = true,
  csbase_command_memory_ram_size_mb = true,
  csbase_command_memory_swap_size_mb = true,
  csbase_command_cpu_perc = true,
  csbase_command_time_sec = true,
  csbase_command_wall_time_sec = true,
  csbase_command_user_time_sec = true,
  csbase_command_system_time_sec = true,
}

local WinPIDTab = {}
local ErrorFile = "ERROR."

function collectWinPID()
  WinPIDTab = {}

  local output = string.format("collectWinPID.%s.%d" , SGA_PID, os.time())
  os.execute(string.format("ps -l > %s", output))
  for line in io.lines(output) do
    local _,_,pid,ppid,pgid,winpid =
      string.find(line, "^%D+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)")

    if pid then
      pgid = tonumber(pgid)
      winpid = tonumber(winpid)

      if not WinPIDTab[pgid] then
        WinPIDTab[pgid] = {}
      end

      table.insert(WinPIDTab[pgid], winpid)
    end
  end
  os.remove(output)
end

function getWinPID(pgid)
  return WinPIDTab[pgid]
end

-- Redefinição da função executecommand para capturar a saída de erro,
-- caso um arquivo não tenha sido definido.
-- servermanager.winexec = servermanager.executecommand
-- servermanager.executecommand = function(cmdid, command, input,
--   output, err_output, host, startdir)
--   err_output = err_output or ErrorFile .. cmdid
--   return servermanager.winexec(cmdid, command, input,
--     output,err_output, host, startdir)
-- end

-- Se a saída de erro tiver sido capturada, exibe o erro na console do
-- sga.
_checkforerror = function (handle)
  local cmdid = servermanager.getcommandid(handle)
  if not cmdid then
    return
  end
  local errorfile = ErrorFile .. cmdid
  local str
  local fd = io.open(errorfile, "r")
  if not fd then

--   servermanager.writeError( "Erro ao abrir arquivo temporário:\n"..
--                            "\t["..tostring(errorfile).."]\n"..
--                            "\tpara aquisição de dados de execução\n" )
    return
  end
  str = fd:read("*a")
  fd:close()
  os.remove(errorfile)
  if str and string.len(str) > 0 then
    local errormsg = string.format(
      "Possível erro na execução do comando %s:\n%s",
      cmdid, str)
    servermanager.writeError(errormsg)
  end
end

-- A primeira coleta não consegue retornar a carga da cpu porque ela
-- precisa de duas chamadas para poder fazer a conta. Por isso, da
-- primeira vez, chamamos o método que obtém a carga da cpu duas vezes.
servermanager.oldgetcpuload = servermanager.getcpuload
servermanager.firstcpuload = true
servermanager.getcpuload = function (handle)
  if servermanager.firstcpuload then
    local cpuload, err = servermanager.oldgetcpuload(handle)
    servermanager.firstcpuload = false
    if err then
      cpuload, err = servermanager.oldgetcpuload(handle)
    end
    servermanager.getcpuload = servermanager.oldgetcpuload
    return cpuload, err
  end
end

---

getConfiguration = function(server, keys)
  if not keys or #keys == 0 then
    keys = _userDefaltConfigKeys
  end -- if no keys

  local errorKeys = {}

  local configMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_num_processors" then
      local numCPU, err = servermanager.getnumcpus(server)
      if err then
        table.insert(errorKeys, "csbase_memory_ram_info_mb")
      else
        configMap[k] = numCPU
      end
    elseif k == "csbase_memory_ram_info_mb" then
      local memory, err = servermanager.getmemory(server)
      if err then
        table.insert(errorKeys, "csbase_memory_ram_info_mb")
      else
        configMap[k] = memory.ram
      end
    elseif k == "csbase_memory_swap_info_mb" then
      local memory, err = servermanager.getmemory(server)
      if err then
        table.insert(errorKeys, "csbase_memory_ram_info_mb")
      else
        configMap[k] = memory.swap
      end
    elseif k == "csbase_job_control_actions" then
      configMap[k] = _getCommandActions()
    end
  end

  -- @TODO Implementar _buildErrMsg
  local errorMsg = _buildErrMsg(errorKeys)

  return configMap, errorMsg
end -- function getConfiguration

getInfo = function(server, keys, maxold)
  if not keys or #keys == 0 then
    keys = _userDefaltInfoKeys
  end -- if no keys

  local errorKeys = {}
  local infoMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_load_perc" then
      local load, err = servermanager.getcpuload(server, maxold)
      if not err then
        infoMap[k] = load
      else
        table.insert(errorKeys, k)
      end
    elseif k == "csbase_memory_ram_free" then
      local load, err = servermanager.getmemoryload(server, maxold)
      if not err then
        infoMap[k] = load.ram
      else
        table.insert(errorKeys, k)
      end
    elseif k == "csbase_memory_swap_free" then
      local load, err = servermanager.getmemoryload(server, maxold)
      if not err then
        infoMap[k] = load.swap
      else
        table.insert(errorKeys, k)
      end
    elseif k == "csbase_number_of_jobs" then
      local nJobs, err = servermanager.getnumberofjobs(server, maxold)
      if not err then
        infoMap[k] = nJobs
      else
        table.insert(errorKeys, k)
      end
    end
  end

  local errorMsg = _buildErrMsg(errorKeys)
  return infoMap, errorMsg
end -- function getInfo

executeCommand = function(id, command, extraParams, server)
  local input = nil
  local output = nil
  local err_output = nil
  return servermanager.executecommand(id, command, input, output, err_output, server)
end -- function executeCommand

control = function(handle, action, childId)
  -- @TODO Verificar a ação
  servermanager.killcommand(handle)
  return true, nil
end -- function control

getCommandInfo = function(handle, keys, maxold)
  local time, err = servermanager.getcommandtimeusage(handle, maxold)
  if err then
    time = {}
    time.elapsed = 0
    time.user = 0
    time.system = 0
  end

  local mem, err = servermanager.getcommandmemoryload(handle, maxold)
  if err then
    mem = {}
    mem.ram = 0
    mem.swap = 0
  end

  if not keys or #keys == 0 then
    keys = _userDefaltProcessKeys
  end -- if no keys

  local cmdInfoMap = {}

  for _, k in ipairs(keys) do
    if k == "csbase_command_id" then
      cmdInfoMap[k] = servermanager.getcommandid(handle)
    elseif k == "csbase_command_pid" then
      cmdInfoMap[k] = servermanager.getcommandpid(handle, maxold)
    elseif k == "csbase_command_ppid" then
      cmdInfoMap[k] = servermanager.getcommandPPID(handle, maxold)
    elseif k == "csbase_command_exec_host" then
      cmdInfoMap[k] = servermanager.getcommandexechost(handle, maxold)
    elseif k == "csbase_command_state" then
      cmdInfoMap[k] = servermanager.getcommandstatus(handle, maxold)
    elseif k == "csbase_command_memory_ram_size_mb" then
      cmdInfoMap[k] = mem.ram
    elseif k == "csbase_command_memory_swap_size_mb" then
      cmdInfoMap[k] = mem.swap
    elseif k == "csbase_command_cpu_perc" then
      cmdInfoMap[k] = servermanager.getcommandcpuload(handle, maxold)
    elseif k == "csbase_command_cpu_time_sec" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_wall_time_sec" then
      cmdInfoMap[k] = time.elapsed
    elseif k == "csbase_command_user_time_sec" then
      cmdInfoMap[k] = time.user
    elseif k == "csbase_command_system_time_sec" then
      cmdInfoMap[k] = time.system
    end
  end

  return cmdInfoMap, nil
end -- function getCommandInfo

getCommandPersistentData = function(handle)
  return servermanager.getcommandpersistentdata(handle)
end -- getCommandPersistentData

retrieveCommandHandle = function(handle)
  return servermanager.retrievecommandhandle(handle)
end -- retrieveCommandHandle

releaseCommandResources = function(handle)
  return servermanager.releasecommandinfo(handle)
end -- releaseCommandResources

--------------------------------------------------------------------------------
-- Retorna as ação diponiveis para comandos. São as ações aceitas pela função
-- control.
--------------------------------------------------------------------------------
_getCommandActions = function()
  return {
   "SUSPEND",
   "RESUME",
   "HOLD",
   "RELEASE",
   "TERMINATE"
  }
end

------------------------------------------------------------------------------
-- Obtém as informações dos jobs em execução no SGA
------------------------------------------------------------------------------
getJobsInfo = function()
   return nil
end -- function getJobsInfo

_buildErrMsg = function(keys)
  if #keys < 1 then
    return nil
  else
    local errorMessage =
      "Error while retriving configuration for the following keys : ["
    for i, k in ipairs(keys) do
      if i < #keys then
        errorMessage = errorMessage .. k .. ", "
      else
        errorMessage = errorMessage .. k .. "]."
      end
    end
    return errorMessage
  end
end -- function _buildErrMsg

-- Funções de consulta à configuração de servidor:
servermanager.getConfiguration = getConfiguration

-- Funções de monitoração de servidor:
servermanager.getInfo = getInfo

-- Funções de execução, monitoração e controle de processos:
servermanager.executeCommand = executeCommand
servermanager.control = control
servermanager.getCommandInfo = getCommandInfo
servermanager.getCommandPersistentData = getCommandPersistentData
servermanager.retrieveCommandHandle = retrieveCommandHandle
servermanager.releaseCommandResources = releaseCommandResources

servermanager.getJobsInfo = getJobsInfo
