Stuff = {}
Stuff.def = {}

local STUFF_NAME = "Stuff"

function stackItem(fromBag, fromSlot, toBag, toSlot, quantity, name)
    local result = true
    -- just in case
    ClearCursor()
    -- must call secure protected (pickup the item via cursor)
    result = CallSecureProtected("PickupInventoryItem", fromBag, fromSlot, quantity)
    if (result) then
        -- must call secure protected (drop the item on the cursor)
        result = CallSecureProtected("PlaceInInventory", toBag, toSlot)
    end
    -- clear the cursor to avoid issues
    ClearCursor()
    return result
end

function insertItem(itemTable, bag, slot, stack)
    local item = {}
    item.bag = bag
    item.slot = slot
    item.stack = stack
    table.insert(itemTable, item)
end


function moveItem(fromItem, toItem, maxStack, itemName)
    -- d(key .. inspect(item))
    -- the most we can move
    local quantity = math.min(maxStack - toItem.stack, fromItem.stack)
    -- if we can move any
    if (quantity > 0) then
        d(" moving " .. quantity .. " " .. itemName .. " from bag: " .. fromItem.bag .. " slot: " .. fromItem.slot .. " to bag: " .. toItem.bag .. " slot: " .. toItem.slot .. " with: " .. toItem.stack)
        -- move them
        result = stackItem(fromItem.bag, fromItem.slot, toItem.bag, toItem.slot, quantity, itemName)
        if (result) then
            fromItem.stack = fromItem.stack - quantity
            toItem.stack = toItem.stack + quantity
            d("moved " .. quantity .. " " .. itemName .. " from bag: " .. fromItem.bag .. " slot: " .. fromItem.slot .. " to bag: " .. toItem.bag .. " slot: " .. toItem.slot .. " with: " .. toItem.stack)
        end
    end
end

function sortStack (first, second)
    return first.stack > second.stack
end

function reverseStack (first, second)
    return first.stack < second.stack
end


function moveItems(bags, fromBag, toBag)
    for itemName, bagItem in pairs(bags) do
        local fromItems = bagItem[fromBag]
        local toItems = bagItem[toBag]
        loopItems(fromItems, toItems, bagItem.maxStack, itemName)
    end
end

function loopItems(fromItems, toItems, maxStack, itemName)
    table.sort(toItems, sortStack)
    table.sort(fromItems, reverseStack)

    for fromIndex, fromItem in ipairs(fromItems) do
        if (fromItem.stack > 0 and fromItem.stack < maxStack) then
            for toIndex, toItem in ipairs(toItems) do
                if(toItem.stack > 0 and toItem.stack < maxStack) then
                    local sameBag = fromItem.bag == toItem.bag
                    local sameSlot = fromItem.slot == toItem.slot
                    if (not (sameBag and sameSlot)) then
                        moveItem(fromItem, toItem, maxStack, itemName)
                        table.sort(toItems, sortStack)
                        table.sort(fromItems, reverseStack)
                    end
                end
            end
        end
    end
end


function HandleOpenBank(eventCode, addOnName, isManual)
    local maxBags = GetMaxBags()
    local bags = {}

    if (not isManual) then
        ClearCursor()
    end

    for bag = 1, maxBags do
        bagIcon, bagSlots = GetBagInfo(bag)
        for slot = 1, bagSlots do
            stack, maxStack = GetSlotStackSize(bag, slot)
            itemName = GetItemName(bag, slot)

            if (stack > 0 and itemName ~= nil) then
                -- right now this only works with the bank
                -- will add support for ui based guild bank deposit / join
                bagItem = bags[itemName] or {}

                bagItem[BAG_BANK] = bagItem[BAG_BANK] or {}
                bagItem[BAG_BACKPACK] = bagItem[BAG_BACKPACK] or {}

                bagItem.maxStack = maxStack
                bagItem.name = itemName

                local itemTable = bagItem[bag]

                -- insert the slot item in the appropriate table
                insertItem(itemTable, bag, slot, stack)
                bags[itemName] = bagItem
            end
        end
    end

    if (not isManual) then
        -- consolidate source
        moveItems(bags, BAG_BACKPACK, BAG_BACKPACK)
        -- consolidate destination
        moveItems(bags, BAG_BANK, BAG_BANK)
        -- move to bank
        moveItems(bags, BAG_BACKPACK, BAG_BANK)
    end
end


function HandleAddOnLoaded(eventCode, addOnName)
    if addOnName ~= STUFF_NAME then return
    end
    Stuff.Defaults = {}

    Stuff.Saved = ZO_SavedVars:New(STUFF_NAME, 5, nil, Stuff.Defaults, nil)

    if Stuff.Saved ~= nil then
    end

    d(STUFF_NAME .. " loaded")

    HandleOpenBank(eventCode, addOnName, true)
end

EVENT_MANAGER:RegisterForEvent(STUFF_NAME, EVENT_OPEN_BANK, HandleOpenBank)
EVENT_MANAGER:RegisterForEvent(STUFF_NAME, EVENT_ADD_ON_LOADED, HandleAddOnLoaded)