local function class(base)
  local t = base and setmetatable({}, base) or {}
  t.__index = t
  t.__class = t
  t.__base  = base

  function t.new(...)
    local o = setmetatable({}, t)
    if o.__init then
      if t == ... then -- we call as Class:new()
        return o:__init(select(2, ...))
      else             -- we call as Class.new()
        return o:__init(...)
      end
    end
    return o
  end

  return t
end

local EventSocket = class() do

if not freeswitch then

local socket       = require "socket"
local ESLParser    = require "lluv.esl".ESLParser
local split_status = require "lluv.esl.utils".split_status
local Database     = require "resources.functions.database"

local EOL = '\n'

local host, port, auth

function EventSocket:__init()
  if not host then
    require "resources.functions.config";
    host = event_socket_host;
    port = event_socket_port;
    auth = event_socket_password;
  end

  return self:_connect(host, port, auth)
end

function EventSocket:_connect(host, port, password)
  local err
  self._cnn, err  = socket.connect(host, port)
  if not self._cnn then return nil, err end

  self._cnn:settimeout(1)

  self._parser = ESLParser.new()
  local auth
  while true do
    local event
    event, err = self:_recv_event()
    if not event then break end

    local ct = event:getHeader('Content-Type')
    if ct == 'auth/request' then
      self._cnn:send('auth ' .. password .. EOL .. EOL)
    elseif ct == 'command/reply' then
      local reply = event:getHeader('Reply-Text')
      if reply then
        local ok, status, msg = split_status(reply)
        if ok then auth = true else err = msg end
      else
        err = 'invalid response'
      end
      break
    end
  end

  if not auth then
    self._cnn:close()
    self._cnn = nil
    return nil, err
  end

  return self
end

function EventSocket:_recv_event()
  local event, err = self._parser:next_event()

  while event == true do
    local str, rst
    str, err, rst = self._cnn:receive("*l")
    if str then self._parser:append(str):append(EOL) end
    if rst then self._parser:append(rst) end
    if err and err ~= 'timeout' then
      break
    end
    event = self._parser:next_event()
  end

  if (not event) or (event == true) then
    return nil, err
  end

  return event
end

function EventSocket:_request(cmd)
  if not self._cnn then return nil, 'closed' end

  for str in (cmd .. '\n'):gmatch("(.-)\n") do
    self._cnn:send(str .. EOL)
  end
  self._cnn:send(EOL)

  return self:_recv_event()
end

function EventSocket:api(cmd)
  local event, err = self:_request('api ' .. cmd)
  if not event then return nil, err end
  local body = event:getBody()
  if body then
    local ok, status, msg = split_status(body)
    if ok == nil then return body end
    return ok, status, msg
  end
  return event:getReply()
end

function EventSocket:close()
  if self._cnn then
    self._cnn:close()
    self._cnn = nil
  end
end

end

if freeswitch then

local api

-- [+-][OK|ERR|USAGE|...][Message]
local function split_status(str)
  local ok, status, msg = string.match(str, "^%s*([-+])([^%s]+)%s*(.-)%s*$")
  if not ok then return nil, str end
  return ok == '+', status, msg
end

function EventSocket:__init()
  self._api = api or freeswitch.API()
  api = self._api
  return self
end

function EventSocket:api(cmd)
  local result = self._api:executeString(cmd)
  local ok, status, msg = split_status(result)
  if ok == nil then return result end
  return ok, status, msg
end

function EventSocket:close()
  self._api = nil
end

end

end

return EventSocket
