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
