local t_insert = table.insert
local t_remove = table.remove
local t_concat = table.concat
local strbyte = string.byte
local strformat = string.format

local cursor_methods = { }
local cursor_mt = { __index = cursor_methods }

local function new_cursor(col, query, returnfields, num_each_query)
  return setmetatable ( {
    col = col ;
    query = query ;
    returnfields = returnfields ;

    id = false ;
    results = { } ;

    done = false ;
    i = 0;
    limit_n = 0;
    num_each = num_each_query;
  } , cursor_mt )
end

cursor_mt.__gc = function( self )
  self.col:kill_cursors({ self.id })
end

cursor_mt.__tostring = function ( ob )
  local t = { }
  for i = 1 , 8 do
    t_insert(t, strformat("%02x", strbyte(ob.id, i, i)))
  end
  return "CursorId(" .. t_concat ( t ) .. ")"
end

function cursor_methods:limit(n)
  assert(n)
  self.limit_n = n
end

--todo
--function cursor_methods:skip(n)

function cursor_methods:sort(field, size)
  size = size or 10000
  if size < 2 then return nil, "number of object must > 1" end
  if not field then return nil, "field should not be nil" end

  local key, asc, t
  for k,v in pairs(field) do
    key = k
    asc = v
    break
  end
  if asc ~= 1 and asc ~= -1 then return nil, "order must be 1 or -1" end

  local sort_f = 
  function(a, b) 
    if not a and not b then return false end
    if not a then return true end
    if not b then return false end
    if asc == 1 then
      return a[key] < b[key]
    else
      return a[key] > b[key]
    end
  end

  if #self.results > self.i then
    table.sort(self.results, sort_f)
  elseif #self.results == 0 and self.i == 0 then
    if self.num_each == 0 and self.limit_n ~= 0 then
      size = self.limit_n
    elseif self.num_each ~= 0 and self.limit_n == 0 then
      size = self.num_each
    else
      size = (self.num_each < self.limit_n 
      and self.num_each) or self.limit_n
    end

    self.id, self.results, t = self.col:query(self.query, 
    self.returnfields, self.i, size)
    table.sort(self.results, sort_f)
  else
    return nil, "sort must be an array"
  end
  return self.results
end

function cursor_methods:next()
  if self.limit_n > 0 and self.i >= self.limit_n then return nil end

  local v = self.results [ self.i + 1 ]
  if v ~= nil then
    self.i = self.i + 1
    self.results [ self.i ] = nil
    return self.i , v
  end

  if self.done then return nil end

  local t
  if not self.id then
    self.id, self.results, t = self.col:query(self.query, 
    self.returnfields, self.i, self.num_each)
    if self.id == "\0\0\0\0\0\0\0\0" then
      self.done = true
    end
  else
    self.id, self.results, t = self.col:getmore(self.id, 
    self.num_each, self.i)
    if self.id == "\0\0\0\0\0\0\0\0" then
      self.done = true
    elseif t.CursorNotFound then
      self.id = false
    end
  end
  return self:next ( )
end

function cursor_methods:pairs( )
  return self.next, self
end

return new_cursor
