--------------------------------------------------------------------------------
-- API para criação de um novo módulo SGA.
--------------------------------------------------------------------------------
--
-- Esse arquivo possui o "esqueleto" das funções que são necessárias
-- para implementar um novo SGA. A configuração do nó (em sgad-cnf.lua),
-- deverá usar o atributo loadlib para indicar o nome do arquivo que
-- contém o código desenvolvido. Esse atributo faz com que o
-- sga-daemon carregue e execute as funções implementadas. Por exemplo,
-- supondo que o arquivo com a nova API possua o nome NovaAPI.lua, a
-- configuração do nó seria da seguinte forma:
--
-- Node {
--   name = "nomeDaMaquina",
--   platform_id = "SGANovo",
--   loadlib = "NovaAPI",
-- }
--
-- A escolha de configurar o SGA como Node ou como Grid vai depender de
-- como você deseja que este SGA apareça na interface do usuário. Se
-- o usuário deve ver o SGA Boinc como uma única máquina, você deve
-- configurá-lo como Node. Porém, se o Boinc possui vários nós e você
-- deseja que o usuário possa monitorá-los, você deve configurá-lo
-- como Grid.
--
-- Grid {
--   name = "nomeDaMaquina",
--   loadlib = "NovaAPI",
--   default = {
--     platform_id = "SGANovo",
--   },
-- }
--
-- Você encontra mais detalhes em:
-- https://jira.tecgraf.puc-rio.br/confluence/pages/viewpage.action?pageId=37814618
--------------------------------------------------------------------------------

-- @TODO Rever nomenclatura do parametro server na funções

--------------------------------------------------------------------------------
--
-- Especificação das funções públicas
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Incializa o módulo para execução. Essa função deve ser chamada antes
-- de qualquer outra função desta biblioteca.
--
-- Retorna verdadeiro caso o módulo seja inicializado com sucesso. Em caso
-- de erro, retorna nil e a mensagem de erro.
--
-- ok, err = open()
--------------------------------------------------------------------------------
open = function()
  return true, nil
end -- function open

--------------------------------------------------------------------------------
-- Termina a execução do módulo. Os processos disparados que ainda estejam
-- rodando não são afetados por essa chamada. Essa função não retorna valores.
--
-- close()
--------------------------------------------------------------------------------
close = function()
end -- function close

--------------------------------------------------------------------------------
--
-- Funções de consulta à configuração de servidor.
--
-- Em SGAs que são clusters, o parâmetro server indica o nó sobre o
-- qual se deseja obter a informação.
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Retorna as informações de configuração correspondentes às chaves e ao
-- servidor especificados.
--
-- @param server o servidor do qual as informações serão obtidas. Caso o valor
--        seja nil será assumido localhost.
-- @param keys a lista contendo as chaves das informações que serão obtidas. Se
--        a lista for nil ou vazia serão retornadas as informações padrões.
--
-- @return o mapa contendo as chaves com o valor correspondente da informação e
--         uma mensagem em caso de erro.
--         As informações padrões que devem ser suportadas são:
--           csbase_num_processors
--           csbase_memory_ram_info_mb
--           csbase_memory_swap_info_mb
--           csbase_job_control_actions
--          @TODO referenciar a IDL
--         Além destas, podem-se adicionar informações padrões específicas de
--         plataforma.
--
-- map, err = getConfiguration(server, keys)
--------------------------------------------------------------------------------
getConfiguration = function(server, keys)
  -- @TODO verificar como acessar, via Oil, as constantes definidas na interface IDL
  -- const string SGA_NODE_NUM_PROCESSORS = "csbase_num_processors";
  -- const string SGA_NODE_MEMORY_RAM_INFO_MB = "csbase_memory_ram_info_mb";
  -- const string SGA_NODE_MEMORY_SWAP_INFO_MB = "csbase_memory_swap_info_mb";
  -- @TODO Verificar se essas duas informações são obtidas neste nível ou no daemon
  -- const string SGA_NODE_CLOCK_SPEED_MHZ = "clock_speed_mhz";
  -- const string SGA_NODE_PLATFORM_ID = "csbase_platform_id";
  if not keys or #keys == 0 then
    return {
      csbase_num_processors = 1,
      csbase_memory_ram_info_mb = 0,
      csbase_memory_swap_info_mb = 0,
      csbase_job_control_actions = {},
    }, nil
  end -- if no keys

  local configMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_num_processors" then
      configMap[k] = 1
    elseif k == "csbase_memory_ram_info_mb" then
      configMap[k] = 0
    elseif k == "csbase_memory_swap_info_mb" then
      configMap[k] = 0
    elseif k == "csbase_job_control_actions" then
      configMap[k] = {}
    end
  end
  return configMap, nil
end -- function getConfiguration

--------------------------------------------------------------------------------
--
-- Funções de monitoração de servidor.
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Retorna as informações dinâmicas correspondentes às chaves e ao servidor
-- especificados.
--
-- @param server o servidor do qual as informações serão obtidas. Caso o valor
--        seja nil será assumido localhost.
-- @param keys a lista contendo as chaves das informações que serão obtidas. Se
--        a lista for nil ou vazia serão retornadas as informações padrões.
-- @param maxold indica o tempo máximo que a informação pode ter. Caso maxold
--        seja nil, é assumido zero e a informação será buscada.
--
-- @return o mapa contendo as chaves com o valor correspondente da informação e
--         uma mensagem em caso de erro.
--         As informações padrões são:
--           csbase_load_perc
--           csbase_memory_ram_free
--           csbase_memory_swap_free
--           csbase_number_of_jobs
--         Além destas, podem-se adicionar informações padrões específicas de
--         plataforma.
--
-- Algunes detalhes sobre as informações padrões:
-- csbase_load_perc:
--   a taxa média de ocupação de CPU do último minuto. Esse valor considera o
--   número de processadores que o servidor possui. Por exemplo, caso a métrica
--   de ocupação seja o número de processos na fila de prontos, este número
--   estará dividido pela quantidade de processadores.
-- csbase_memory_ram_free_perc e csbase_memory_swap_free_perc:
--   a média de ocupação, em bytes, de memória do último minuto.
-- csbase_number_of_jobs:
--   o número de jobs que estão em execução.
--
-- map, err = getInfo(server, keys, maxold)
--------------------------------------------------------------------------------
getInfo = function(server, keys, maxold)
  if not keys or #keys == 0 then
    return {
      csbase_load_perc = 0,
      csbase_memory_ram_free = 0,
      csbase_memory_swap_free = 0,
      csbase_number_of_jobs = 0,
    }, nil
  end -- if no keys

  local infoMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_load_perc" then
      infoMap[k] = 0
    elseif k == "csbase_memory_ram_free" then
      infoMap[k] = 0
    elseif k == "csbase_memory_swap_free" then
      infoMap[k] = 0
    elseif k == "csbase_number_of_jobs" then
      infoMap[k] = 0
    end
  end
  return infoMap, nil
end -- function getInfo

--------------------------------------------------------------------------------
--
-- Funções de execução, monitoração e controle de processos.
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Executa um comando no servidor especificado e retorna um handle que é a
-- referência para o comando no contexto desta biblioteca. O handle pode ter
-- qualquer formato.
-- Os parâmetros extras para a execução podem ser específicos da plataforma ou
-- um dos seguintes já definidos:
--   csbase_command_input_path caminho do direcionamento da entrada padrão.
--   csbase_command_output_path caminho do direcionamento da saída padrão.
--   csbase_command_error_path caminho do direcionamento da saída de erro.
-- Os valores de csbase_command_output_path e csbase_command_error_path podem
-- apontar para o mesmo caminho.
--
-- @param id o identificador único para este comando.
-- @param command o comando a ser executado.
-- @param extraParams os parâmetros extras para a excução do comando.
-- @param server o servidor onde o comando será executado. Caso seja nil será
--        assumido localhost.
--
-- @return o handle do comando e, em caso de erro, nil e a mensagem de erro.
--
-- handle, err = executeCommand(id, command, extraParams, server)
--------------------------------------------------------------------------------
executeCommand = function(id, command, extraParams, server)
  -- const string COMMAND_EXECUTION_INPUT_PATH = "csbase_command_input_path";
  -- const string COMMAND_EXECUTION_OUTPUT_PATH = "csbase_command_output_path";
  -- const string COMMAND_EXECUTION_ERROR_PATH = "csbase_command_error_path";
  local handle = {
    id = id,
    command = command
  }
  return handle, nil
end -- function executeCommand

--------------------------------------------------------------------------------
-- Excerce uma ação sobre um comando iniciado através da função executecommand.
--
-- @param handle o handle do comando.
-- @param action a ação que será feito sobre o comando. Os valores estão
--        definidos na enumeração JobControlAction:
--          SUSPEND: suspender a execução do comando;
--          RESUME: (re)iniciar o comando;
--          HOLD: alterar o estado do comando para on-hold;
--          RELEASE: retirar o comando do estado de on-hold;
--          TERMINATE: terminar o comando.
-- @param childId identificador do job filho no qual será feita a ação, ou nulo
-- se a ação deve ser feita no comando.
--
-- @return true se a ação foi exercida com sucesso e, se cocorrer algum erro,
--         false ou nil e a mensagem de erro.
-- @TODO pensar em como lançar as exceções ActionNotSupportedException, InvalidActionException e InvalidTransitionException
--
-- ok, ex = control(handle, action, childId)
--------------------------------------------------------------------------------
control = function(handle, action, childId)
  return true, nil
end -- function control

--------------------------------------------------------------------------------
-- Retorna uma tabela Lua com os dados do comando que precisam ser persistidos.
-- Estes dados devem ser suficientes para que um comando seja 'recuperado' após
-- a reinicialização do SGA, através da chamada à retrievecommandhandle.
--
-- @param handle o handle do comando.
--
-- @return os dados do comando ou nil e mensagem de erro na ocorrência de erro.
--
-- pdata, err = getcommandpersistentdata(handle)
--------------------------------------------------------------------------------
getCommandPersistentData = function(handle)
  return handle, nil
end -- function getCommandPersistentData

--------------------------------------------------------------------------------
-- A partir dos dados persistidos, retorna um handle que é a referência para o
-- comando no contexto desta biblioteca. O handle pode ter qualquer formato.
--
-- @param pdata os dados do comando.
--
-- @return o handle do comando ou nil e mensagem de erro na ocorrência de erro.
--
-- handle, err = retrievecommandhandle(pdata)
--------------------------------------------------------------------------------
retrieveCommandHandle = function(pdata)
  return pdata, nil
end -- function retrieveCommandHandle

--------------------------------------------------------------------------------
-- Libera todos os recursos relacionados ao processo especificado. Esta função
-- precisa ser chamada após o término do processo para que eventuais recursos
-- alocados a ele possam ser liberados. Após a chamada desta função, o handle
-- do processo não poderá mais ser utilizado.

-- @return true se os recursos foram leberados com sucesso e, se cocorrer algum
--         erro, false ou nil e a mensagem de erro.
--
-- ok, err = releaseCommandResources(handle)
--------------------------------------------------------------------------------
releaseCommandResources = function(handle)
   return true, nil
end -- function releaseCommandResources

--------------------------------------------------------------------------------
-- Retorna as informações correspondentes às chaves e ao comando especificados.
--
-- @param handle o handle do comando.
-- @param keys a lista contendo as chaves das informações que serão obtidas. Se
--        a lista for nil ou vazia serão retornadas as informações padrões.
-- @param maxold indica o tempo máximo que a informação pode ter. Caso maxold
--        seja nil, é assumido zero e a informação será buscada.
--
-- @return o mapa contendo as chaves com o valor correspondente da informação e
--         uma mensagem em caso de erro.
--         As informações padrões são:
--           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
--         Além destas, podem-se adicionar informações padrões específicas de
--         plataforma.
--
-- Algunes detalhes sobre as informações padrões:
-- csbase_command_id:
--   identificador do comando recebido na função executecommand.
-- csbase_command_pid:
--   identificador do processo iniciado através da função executecommand.
-- csbase_command_ppid:
--   identificador do processo pai iniciado através da função executecommand.
-- csbase_command_string:
--   o comando em execução.
-- csbase_command_exec_host:
--   a máquina que está executando o comando.
-- csbase_command_state:
--   o estado de um processo iniciado através da função executecommand.
-- csbase_command_memory_ram_size_mb:
--   a média de ocupação de memória do último minuto (em MB).
-- csbase_command_memory_swap_size_mb:
--   a média de ocupação da área de swap do último minuto (em MB).
-- csbase_command_cpu_perc:
--   a taxa média de ocupação de CPU do último minuto pelo comando.
-- csbase_command_cpu_time_sec:
--   o tempo de CPU da execução do comando (em SEC).
-- csbase_command_wall_time_sec:
--   o tempo de relógio da execução do comando (em SEC).
-- csbase_command_user_time_sec:
--   o tempo de usuário da execução do comando (em SEC).
-- csbase_command_system_time_sec:
--   o tempo de systema da execução do comando (em SEC).
-- csbase_command_virtual_memory_size_mb:
--   quantidade de memória virtual utilizado na execução do comando (em MB).
-- csbase_command_bytes_in_kb:
--   quantidade de dados lidos na execução do comando (em KB).
-- csbase_command_bytes_out_kb:
--   quantidade de dados escritos na execução do comando (em KB).
-- csbase_command_disk_bytes_read_kb:
--   quantidade de dados lidos do disco na execução do comando (em KB).
-- csbase_command_disk_bytes_write_kb:
--   quantidade de dados escritos no disco na execução do comando (em KB).
--
-- map, err = getCommandInfo(handle, keys, maxold)
--------------------------------------------------------------------------------
getCommandInfo = function(handle, keys, maxold)
  if not keys or #keys == 0 then
    return {
      csbase_command_id = 0,
      csbase_command_pid = 0,
      csbase_command_ppid = 0,
      csbase_command_string = handle.command,
      csbase_command_exec_host = "unknown",
      csbase_command_state = servermanager.FINISHED,
      csbase_command_memory_ram_size_mb = 0,
      csbase_command_memory_swap_size_mb = 0,
      csbase_command_cpu_perc = 0,
      csbase_command_cpu_time_sec = 0,
      csbase_command_wall_time_sec = 0,
      csbase_command_user_time_sec = 0,
      csbase_command_system_time_sec = 0,
    }, nil
  end -- if no keys

  local cmdInfoMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_command_id" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_pid" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_ppid" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_string" then
      cmdInfoMap[k] = handle.command
    elseif k == "csbase_command_exec_host" then
      cmdInfoMap[k] = "unknown"
    elseif k == "csbase_command_state" then
      cmdInfoMap[k] = servermanager.FINISHED
    elseif k == "csbase_command_memory_ram_size_mb" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_memory_swap_size_mb" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_cpu_perc" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_cpu_time_sec" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_wall_time_sec" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_user_time_sec" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_system_time_sec" then
      cmdInfoMap[k] = 0
    end
  end
  return cmdInfoMap, nil
end


--------------------------------------------------------------------------------
-- Define as chaves padrões de informações dinâmicas que serão usadas na função
-- getConfiguration, getInfo e getCommandInfo.
-- Caso o usuário não tenha defindo o conjunto de chaves padrões a biblioteca
-- deve ter um conjunto mínimo, que deverá conter as chaves:
--   csbase_load_perc
--   csbase_memory_ram_free_perc
--   csbase_memory_swap_free_perc
--   csbase_number_of_jobs
--
--   csbase_num_processors
--   csbase_memory_ram_info_mb
--   csbase_memory_swap_info_mb
--   csbase_job_control_actions
--   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
-- @param keys o conjunto de chaves de configuração
--------------------------------------------------------------------------------
setDefaultKeys = function(keys)
end -- function setDefaultKeys

--------------------------------------------------------------------------------
-- Retirna todas as chaves disponíveis.
--------------------------------------------------------------------------------
getAllKeys = function()
end -- function getAllKeys

--------------------------------------------------------------------------------
--
-- Funções de consulta à clusters.
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Obtém as informações de todos os processos em execução no SGA.
-- ** Em desenvolvimento **
--------------------------------------------------------------------------------
getJobsInfo = function()
  return nil
end

--------------------------------------------------------------------------------
-- Retorna uma lista com os nós do servidor.
--
-- @return a lista de nós e, em caso de erro, nil e a mensagem de erro.
--
-- nodes, err = getnodes(server)
--------------------------------------------------------------------------------
getNodes = function()
  return { "localhost" }, nil
end

--------------------------------------------------------------------------------
--
-- Funções de log de histórico
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Indica se o histórico deve ser ativado ou não para o SGA em questão.
--
-- setHistoric(historic)
--------------------------------------------------------------------------------
setHistoric = function(historic)
  servermanager._enableHistoric = historic
end

--------------------------------------------------------------------------------
-- Retorna true se o histórico deve ser ativado para o sga em questão,
-- ou false caso contrário.
--
-- enableHistoric = getHistoric()
--------------------------------------------------------------------------------
getHistoric = function()
  return servermanager._enableHistoric
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
servermanager = {
  -------------
  -- private --
  -------------
  _enableHistoric = false,

  -----------------------
  -- Constantes do módulo
  -----------------------
  RUNNING = 0,
  NOT_RESPONDING = 1,
  WAITING = 2,
  FINISHED = 3,

  ERROR_CODE = -1,

  -------------
  -- public --
  -------------
  now = now, -- Definida em C nos binários para Unix.
  sleep = sleep, -- Definida em C nos binários para Unix.

  -- Funções de controle do módulo:
  open = open,
  close = close,

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

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

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

  -- Funções para clusters:
  getJobsInfo = getJobsInfo,
  getNodes = getNodes, -- OPCIONAL

  -- Funções de log de histórico:
  setHistoric = setHistoric, -- OPCIONAL
  getHistoric = getHistoric, -- OPCIONAL
}
