require 'resources.functions.config'

-----------------------------------------------------------
local OdbcDatabase = {} if not freeswitch then
OdbcDatabase.__index = OdbcDatabase

local odbc = require "odbc.dba"

function OdbcDatabase.new(name)
  local self = setmetatable({}, OdbcDatabase)

  local connection_string = assert(database[name])

  local typ, dsn, user, password = connection_string:match("^(.-)://(.-):(.-):(.-)$")
  assert(typ == 'odbc', "unsupported connection string:" .. connection_string)

  self._dbh = odbc.Connect(dsn, user, password)

  return self
end

function OdbcDatabase:query(sql, fn)
  self._rows_affected = nil
  if fn then
    return self._dbh:neach(sql, function(row)
      local o = {}
      for k, v in pairs(row) do
        if v == odbc.NULL then 
          o[k] = nil
        else
          o[k] = tostring(v)
        end
      end
      return fn(o)
    end)
  end
  local ok, err = self._dbh:exec(sql)
  if not ok then return nil, err end
  self._rows_affected = ok
  return self._rows_affected
end

function OdbcDatabase:affected_rows()
  return self._rows_affected;
end

function OdbcDatabase:release()
  if self._dbh then
    self._dbh:destroy()
    self._dbh = nil
  end
end

function OdbcDatabase:connected()
  return self._dbh and self._dbh:connected()
end

end
-----------------------------------------------------------

-----------------------------------------------------------
local FsDatabase = {} if freeswitch then

require "resources.functions.file_exists"
require "resources.functions.database_handle"

FsDatabase.__index = FsDatabase

function FsDatabase.new(name)
  local dbh = assert(name)
  if type(name) == 'string' then
    if name == 'switch' and file_exists(database_dir.."/core.db") then
      dbh = freeswitch.Dbh("sqlite://"..database_dir.."/core.db")
    else
      dbh = database_handle(name)
    end
  end
  assert(dbh:connected())

  local self = setmetatable({
    _dbh = dbh;
  }, FsDatabase)

  return self
end

function FsDatabase:query(sql, fn)
  if fn then
    return self._dbh:query(sql, fn)
  end
  return self._dbh:query(sql)
end

function FsDatabase:affected_rows()
  if self._dbh then
    return self._dbh:affected_rows()
  end
end

function FsDatabase:release()
  if self._dbh then
    self._dbh:release()
    self._dbh = nil
  end
end

function FsDatabase:connected()
  return self._dbh and self._dbh:connected()
end

end
-----------------------------------------------------------

-----------------------------------------------------------
local Database = {} do
Database.__index = Database
Database.__base = freeswitch and FsDatabase or OdbcDatabase
Database = setmetatable(Database, Database.__base)

function Database.new(...)
  local self = Database.__base.new(...)
  setmetatable(self, Database)
  return self
end

function Database:first_row(sql)
  local result
  local ok, err = self:query(sql, function(row)
    result = row
    return 1
  end)
  if not ok then return nil, err end
  return result
end

function Database:first_value(sql)
  local result, err = self:first_row(sql)
  if not result then return nil, err end
  local k, v = next(result)
  return v
end

function Database:first(sql, ...)
  local result, err = self:first_row(sql)
  if not result then return nil, err end
  local t, n = {}, select('#', ...)
  for i = 1, n do
    t[i] = result[(select(i, ...))]
  end
  return unpack(t, 1, n)
end

function Database:fetch_all(sql)
  local result = {}
  local ok, err = self:query(sql, function(row)
    result[#result + 1] = row
  end)
  if (not ok) and err then return nil, err end
  return result
end

function Database.__self_test__(...)
  local db = Database.new(...)
  assert(db:connected())

  assert("1" == db:first_value("select 1 as v union all select 2 as v"))

  local t = assert(db:first_row("select '1' as v union all select '2' as v"))
  assert(t.v == "1")

  t = assert(db:fetch_all("select '1' as v union all select '2' as v"))
  assert(#t == 2)
  assert(t[1].v == "1")
  assert(t[2].v == "2")

  local a, b = assert(db:first("select '1' as b, '2' as a", 'a', 'b'))
  assert(a == "2")
  assert(b == "1")

  -- assert(nil == db:first_value("some non sql query"))

  db:release()
  assert(not db:connected())
  print(" * databse - OK!")
end

end
-----------------------------------------------------------

return Database