------------------------------------------------------------------------
-- Restack --
-- by VincentGdG	--
------------------------------------------------------------------------

-- initial values each time the addon is loaded
Restack = {}
Restack.BagsUpdatedTime = 0 -- time since the last BAG_UPDATE event happened
Restack.WaitforBagUpdate = false
Restack.UpdateTimer = 1.0		-- static time in seconds after last BAG_UPDATE before reacting
Restack.StartupTimer = 5.0	-- time in seconds that restack will not respond to events, because of that lousy programming of WoW (BAG_UPDATE, before even the names of the bags are known)
Restack.Done = true					-- Is our restacking complete?
Restack.Full = false				-- full restack?
Restack.Enabled = true			-- addon enabled?

ItemsBase = {}	-- this is the array where items are stored for restacking

-- these are the stored values

local function Restack_Reset()
	Restack_Config = {}
	Restack_Config.minfree = 2
	Restack_Config.showdebug = false
	Restack_Config.verbose = true
	Restack_Config.showfull = true
end

-- some message functions
local function print(msg)
	DEFAULT_CHAT_FRAME:AddMessage(RS_CYAN .. "Restack: |r" .. msg);
end

local function error(msg)
	DEFAULT_CHAT_FRAME:AddMessage(RS_RED .. "Restack: " .. msg .. "|r");
	UIErrorsFrame:AddMessage(msg, 1.0, 1.0, 0, 1, 2)
end

local function debug(msg)
	if Restack_Config.showdebug then
		DEFAULT_CHAT_FRAME:AddMessage(RS_GREEN .. "Restack-Debug: " .. msg .. "|r");
	end
end

local function verbose(msg)
	if Restack_Config.verbose then
		DEFAULT_CHAT_FRAME:AddMessage(RS_GREY_HI .. "Restack: |r" .. msg);
	end
end

function inBase(itemName)
	for BaseIndex = 1 , table.getn(ItemsBase) do
		local _, _, aktName, aktBag, aktSlot, aktCount, aktMax = string.find(ItemsBase[BaseIndex], "(.+):(%d+):(%d+):(%d+):(%d+)")
		if (aktName == itemName) then -- item already known
			local aktSpaceLeft = aktMax - aktCount
			if aktSpaceLeft > 0 then
				return BaseIndex, aktBag, aktSlot, aktSpaceLeft
			end
		end
	end		
	return 0
end


function Restack_Done()
	Restack.Done = true
	Restack.Full = false
	Restack.FreeSlots = FreeSlots()
	verbose(RS_DONE)
end


function Restack_DoStack()
	local aktBagNum, aktSlotNum=nil;

	ItemsBase = {}

	Restack.WaitforBagUpdate = false
	Restack.Done = false;
	
	if FreeSlots() >= Restack_Config.minfree and not Restack.Full then
		Restack_Done()
		return
	end

	-- run through all bags
	for aktBagNum = 0,4 do 
		if (isValidBag(aktBagNum)) then
			if CursorBusy() then
				verbose(RS_HANDBUSY)
				if not PutItemInPrefBag() then
					error(RS_CANNOTEMPTYHAND)
					Restack_Done()
					return
				end
			end
			-- run through the bag
			for aktSlotNum = 1 , GetContainerNumSlots(aktBagNum) do
				local itemName, itemLink, itemRarity, itemMinLevel, itemType, itemSubType, itemMaxStack, itemCount  = GetItemDataByLoc(aktBagNum,aktSlotNum)
				-- item in slot?
				if (itemName) then
					local BaseIndex, BaseBag, BaseSlot, BaseSpaceLeft = inBase(itemName)

					-- item already seen before and space left on that stack?
					if BaseIndex > 0 then
						-- this whole stack fits on the other stack
						if itemCount <= BaseSpaceLeft then
							debug("stacking all " .. itemCount .. " items of " .. itemName .. " from bag " .. aktBagNum .. ", slot " .. aktSlotNum .. " to bag " .. BaseBag .. ", slot " .. BaseSlot);
							PickupContainerItem(aktBagNum,aktSlotNum);
							PickupContainerItem(BaseBag,BaseSlot);
							Restack.WaitforBagUpdate = true
							return						
						else
						-- split this stack and put onto the other what fits there
							debug("stacking " .. BaseSpaceLeft .. " items of " .. itemName .. " from bag " .. aktBagNum .. ", slot " .. aktSlotNum .. " to bag " .. BaseBag .. ", slot " .. BaseSlot);
							SplitContainerItem(aktBagNum,aktSlotNum,BaseSpaceLeft);
							PickupContainerItem(BaseBag,BaseSlot);
							Restack.WaitforBagUpdate = true
							return						
						end
					elseif itemMaxStack > 1 then
						-- not seen this item before, so store it.
						local aktItemData = itemName .. ":" .. aktBagNum .. ":" .. aktSlotNum .. ":" .. itemCount .. ":" .. itemMaxStack
						--debug(aktItemData)
						table.insert(ItemsBase,aktItemData)
					end
				end
			end
		end
	end
	-- we are through all bags, so we are done.
	if (FreeSlots() < Restack_Config.minfree and Restack_Config.showfull) then
		error(RS_NOTENOUGHFREE);
	end
	Restack_Done()
end

function Restack_Init()
	SLASH_Restack1 = "/restack";
	SlashCmdList["Restack"] = function(msg) Restack_CmdHandler(msg); end
	Restack.FreeSlots = FreeSlots() -- store number of free slots at startup
	startTime = time()
	if not Restack_Config then
		Restack_Reset()
	end
	print(RESTACK_NAME..RS_LOADED .. " -- " .. RS_USAGE);
end

function Restack_OnEvent()
	debug(event)
	if (event == "BAG_UPDATE") then
		Restack.WaitforBagUpdate = false
		Restack.BagsUpdatedTime = 0; -- set counter for elapsed time since last event to zero
	end
end

function Restack_OnUpdate(arg1)
	if not Restack.Enabled then
		return
	end

	Restack.BagsUpdatedTime = Restack.BagsUpdatedTime + arg1

	if 	not Restack.Done and 
			Restack.BagsUpdatedTime > Restack.UpdateTimer and 
			not Restack.WaitforBagUpdate and 
			not BankIsOpen() and 
			not UnitAffectingCombat("player") then
		Restack_DoStack()
	end
end

function Restack_CmdHandler(msg)
	local Cmd, SubCmd = GetCmd(msg);	
	if Cmd == "stack" then
		verbose(RS_STARTED);
		Restack.Full = true
		Restack_DoStack()
	elseif Cmd == "reset" then
		Restack_Reset()
		print(RS_RESET)
	elseif Cmd == "off" then
		Restack.Enabled = false
		this:UnregisterEvent("BAG_UPDATE")
		print(RS_ISOFF)
		Restack_Done()
	elseif Cmd == "on" then
		Restack.Enabled = true
		this:RegisterEvent("BAG_UPDATE")
		print(RS_ISON)
	elseif Cmd == "minfree" then
		if (SubCmd * 1 > 0 and SubCmd * 1 <= 20) then
			Restack_Config.minfree = SubCmd * 1
			print(RS_MINFREE_SET_TO .. Restack_Config.minfree)
		end
	elseif Cmd == "verbose" then
		Restack_Config.verbose = not Restack_Config.verbose;
		if Restack_Config.verbose then
			print(RS_VERBOSEON)
		else
			print(RS_VERBOSEOFF)
		end
	elseif Cmd == "debug" then
		Restack_Config.showdebug = not Restack_Config.showdebug;
		if Restack_Config.showdebug then
			print(RS_DEBUGON)
		else
			print(RS_DEBUGOFF)
		end
	elseif Cmd == "showfull" then
		Restack_Config.showfull = not Restack_Config.showfull
		if Restack_Config.showfull then
			print(RS_SHOWFULLON)
		else
			print(RS_SHOWFULLOFF)
		end
	elseif Cmd == "info" then
		if Restack.Enabled then
			print(RS_ISON)
		else
			print(RS_ISOFF)
		end
		print(RS_MINFREE .. Restack_Config.minfree);
		if Restack_Config.verbose then
			print(RS_VERBOSEON)
		else
			print(RS_VERBOSEOFF)
		end
		if Restack_Config.showdebug then
			print(RS_DEBUGON)
		else
			print(RS_DEBUGOFF)
		end
		if Restack_Config.showfull then
			print(RS_SHOWFULLON)
		else
			print(RS_SHOWFULLOFF)
		end
	else
		print(RS_HELP)
	end
end
