

--===================================================
--									
--										GHI Trade
--									ghi_trade.lua
--									<< Singleton >>
--
--					Trade of items between players
--	
-- 						(c)2011 The Gryphonheart Team
--								All rights reservated
--
--===================================================	

local class
function GHI_Trade()
	if class then return class end;
	
	class = GHClass("GHI_Trade");
	
	local tradeItemsPlayer = {};
	local tradeItemsRecipient = {}; 
	local tradeLinksSend = {}; --needed?
	local playerAcceptState,recipientAcceptState;
	local tradePlayer;
	local comm = GHI_Comm();
	local orig = {};
	local ping = GHI_Ping();
	local tradeRecipientGotGHI;
	local tradeItemTypeTexts = {};
	local recipientItemTypeTexts = {};
	local itemDataTransfer = GHI_ItemDataTransfer();
	local sendLinksIDs = {};
	
	local GetFreeBagIndex,AcceptTrade,ClearRecipientButton,ClearTradeButton,ClickTradeButton,GetGhiItemFromSlot,GetRecipientTradeItem,GetTradeItemBagInfo,GetTradeItemDuration,InsertItem,LockContainerBagSlot,OnEvent,PickUpGhiItem,PickupGhiItemPlaceGhiItem,PickupGhiItemPlaceWowItem,PickupNonePlaceGhiItem,PickupWowItemPlaceGhiItem,RecieveBag,RecieveItem,RecievePlayerPing,RecieveRemoveTradeItem,RecieveTradeItem,RemoveBag,RemoveItem,SendTradeInfo,SetGhiItemInSlot,SetTradeItem,UpdateRecipientTradeItem,UpdateTradeButton;
	
	class.GetEmptySlot = function()
		for i=1,6 do
			local slotGotGhiItem = tradeItemsPlayer[i] and true;
			local slotGotNormalItem = GetTradePlayerItemInfo(i) and true;
			if not(slotGotGhiItem or slotGotNormalItem) then
				return i;
			end
		end
	end
	class.GetDurationInfo = function(fromRecipient,slot)
		if fromRecipient then
			local t = tradeItemsRecipient[slot] or {};
			return unpack(t.duration or{});
		else
			local ID,amount,frame,bagID = GetGhiItemFromSlot(slot);
			local bagSlot = frame:GetID();
			return unpack(GetTradeItemDuration(ID,bagID,bagSlot) or {});			
		end
	end
	
	OnEvent = function(self,event,arg1,arg2,...)
		if ( event == "TRADE_CLOSED" ) then
			if (playerAcceptState == 1 and recipientAcceptState == 1) then
				
				comm.Send(nil,tradePlayer,"TradeAccepted",nil);
				AcceptTrade(tradePlayer);
			end
			GHI_ClearLocked();
			
		elseif ( event == "TRADE_ACCEPT_UPDATE" ) then
			
			playerAcceptState = TradeHighlightPlayer:IsShown();
			recipientAcceptState = TradeHighlightRecipient:IsShown();
			
		elseif ( event == "TRADE_SHOW" ) then
			ClearAll();
			tradePlayer = TradeFrameRecipientNameText:GetText();
			ping.SendPing(tradePlayer,true); 
		end
	end
	
	ClearAll = function()
		tradeItemsPlayer = {}
		tradeItemsRecipient = {}
		tradeLinksSend = {};
		playerAcceptState = nil;
		recipientAcceptState = nil;
		tradePlayer = nil;
		tradeRecipientGotGHI = nil;
		sendLinksIDs = {};
		
		for _,f in pairs(tradeItemTypeTexts) do
			f:Hide();		
		end
		for _,f in pairs(recipientItemTypeTexts) do
			f:Hide();		
		end
	end
	
	CreateItemTypeTexts  = function()
		for i=1,6 do
			local f = TradeFrame:CreateFontString();
			f:SetPoint("LEFT", _G["TradePlayerItem"..i.."Name"] ,"LEFT", -2, -12);
			f:SetFontObject(GameFontHighlightSmall);
			f:SetTextColor(1,0.8196079,0);
			f:Hide();
			tradeItemTypeTexts[i] = f;
		end
		
		for i=1,6 do
			local f = TradeFrame:CreateFontString();
			f:SetPoint("LEFT", _G["TradeRecipientItem"..i.."Name"] ,"LEFT", -2, -12);
			f:SetFontObject(GameFontHighlightSmall);
			f:SetTextColor(1,0.8196079,0);
			f:Hide();
			recipientItemTypeTexts[i] = f;
		end		
	end
	
	ClickTradeButton = function(slot)
		if slot > 6 then return orig.ClickTradeButton(slot); end;
	
		local cursorGotGhiItem = GHI_GetCursor() == "item";
		local slotGotGhiItem = tradeItemsPlayer[slot] and true;
		local slotGotNormalItem = GetTradePlayerItemInfo(slot) and true;
		local slotGotNoItem = not(slotGotGhiItem or slotGotNormalItem);
				
		if cursorGotGhiItem and not(tradeRecipientGotGHI) then
			return NoTradeResponceError();
		end
		
		if cursorGotGhiItem and slotGotNormalItem then
			PickupWowItemPlaceGhiItem(slot);		
		elseif cursorGotGhiItem and slotGotGhiItem then
			PickupGhiItemPlaceGhiItem(slot);	
		elseif cursorGotGhiItem and slotGotNoItem then
			PickupNonePlaceGhiItem(slot);	
		elseif not(cursorGotGhiItem) and slotGotNormalItem then
			orig.ClickTradeButton(slot)
		elseif not(cursorGotGhiItem) and slotGotGhiItem then
			PickupGhiItemPlaceWowItem(slot) -- placing nothing instead of wow item
		elseif not(cursorGotGhiItem) and slotGotNoItem then	
			orig.ClickTradeButton(slot)
		end
							
		CancelAcceptTrade();
		
	end	
	
	LockContainerBagSlot = function(bagID,bagSlotId)
		GHI_ContainerData[bagID][bagSlotId].locked = true;
		GHI_UpdateContainers()
	end
	
	PickupWowItemPlaceGhiItem = function(slot)
		local ID,amount,frame,bag,bagSlot = GetItemInfoFromCursor();
		orig.ClickTradeButton(slot)			
		SetGhiItemInSlot(slot,ID,amount,frame,bag);
		GHI_ResetCursor();
		LockContainerBagSlot(bag,bagSlot);
	end
	
	PickupGhiItemPlaceWowItem = function(slot)
		local id,amount,frame,bag = GetGhiItemFromSlot(slot);
		ClearTradeButton(slot);	
		orig.ClickTradeButton(slot);
		PickUpGhiItem(id,amount,frame,bag);		
	end
	
	PickupGhiItemPlaceGhiItem = function(slot)
		local ID,amount,frame,bag,bagSlot = GetItemInfoFromCursor();	
		local id,amount,frame,bag = GetGhiItemFromSlot(slot);
		tradeItemsPlayer[slot] = nil;
		PickUpGhiItem(id,amount,frame,bag);		
		SetGhiItemInSlot(slot,ID,amount,frame,bag);
		LockContainerBagSlot(bag,bagSlot);
	end
	
	PickupNonePlaceGhiItem = function(slot)
		local ID,amount,frame,bag,bagSlot = GetItemInfoFromCursor();				
		SetGhiItemInSlot(slot,ID,amount,frame,bag);
		GHI_ResetCursor();
		LockContainerBagSlot(bag,bagSlot);
	end
	
		
	NoTradeResponceError = function()
		local tradePlayer = TradeFrameRecipientNameText:GetText();
		if tradePlayer and GHI_AddOns:PlayerGotAddOn(tradePlayer,"GHI") then
			GHI_Message(GHI_TRADE_BUSY);
			ping.SendPing(tradePlayer,true); 
			return
		end
		GHI_Message(GHI_TRADE_NO_GHI);
	end;
	
	RecievePlayerPing = function(player,version)
		if player == tradePlayer then
			tradeRecipientGotGHI = true;
		end
	end
	
	CancelAcceptTrade = function()
		local n = GetPlayerTradeMoney();
		SetTradeMoney(1); 
		SetTradeMoney(n);
	end
	
  SetTradeItem = function(slot,ID,amount,texture,isOfficial,name)
		local itemButton = _G["TradePlayerItem"..slot.."ItemButton"];
		
		SetItemButtonTexture(itemButton, texture);
		SetItemButtonCount(itemButton, amount);
		
		local itemType;
		if isOfficial then  -- official item
			itemType = "|CFF" ..string.format("%.2x",0.7*255) .. string.format("%.2x",0*255) .. string.format("%.2x",0*255) .."Official GH item|r"
		else
			itemType = "|CFF" ..string.format("%.2x",0.0*255) .. string.format("%.2x",0.7*255) .. string.format("%.2x",0.5*255) .."Custom made item|r"
		end
		tradeItemTypeTexts[slot]:Show();
		tradeItemTypeTexts[slot]:SetText(itemType);
		_G["TradePlayerItem"..slot.."Name"]:SetText(name);
		
	end
	
	UpdateTradeButton = function(slot)
		local ID,amount,frame,bag = GetGhiItemFromSlot(slot);
		local bagSlot =  frame:GetID();
		local bagID = bag;
		local name,texture = GHI_GetItemInfo(ID);
		if not(name) then return; end
		amount = amount or 1;
		
		SetTradeItem(slot,ID,amount,texture,isOfficial,name)
		SendTradeInfo(tradePlayer,slot,ID,amount,bagID,bagSlot,texture,name)
		
		
	end
	
	ClearTradeButton = function(slot)
		tradeItemsPlayer[slot] = nil;
		local itemButton = _G["TradePlayerItem"..slot.."ItemButton"];
		SetItemButtonTexture(itemButton, "");
		SetItemButtonCount(itemButton, 1);
		_G["TradePlayerItem"..slot.."Name"]:SetText("");		
		tradeItemTypeTexts[slot]:Hide();
		comm.Send("ALERT",tradePlayer,"RemoveTradeItem",slot);
	end
	
	SetGhiItemInSlot = function(slot,ID,amount,frame,bag)
		tradeItemsPlayer[slot] = { 
			ID = ID,
			amount = amount,
			OrriginalFrame = frame,
			itemOrigBag = bag,
			bagSlot = frame:GetID(),
		};
		UpdateTradeButton(slot);
	end
	
	GetGhiItemFromSlot = function(slot)
		local t = tradeItemsPlayer[slot];
		if not(type(t)=="table") then 
			return		
		end
		return t.ID,t.amount,t.OrriginalFrame,t.itemOrigBag,t.bagSlot;
	end
	
	
	PickUpGhiItem = function(id,amount,frame,bag)
		local _,icon = GHI_GetItemInfo(id);
		GHI_SetCursor("item",{
			ID = id,
			iconTexture = icon,
			ItemOrigFrame = frame,
			ItemOrigBag = bag,
			ItemAmount = amount,
		});
		
	end
	
	
	
	GetItemInfoFromCursor = function()
		local _,details = GHI_GetCursor();
		
		local bagSlot = details.ItemOrigFrame:GetID();
		local bag = details.ItemOrigFrame:GetParent():GetID();
		local ID = GHI_ContainerData[bag][bagSlot].ID;
		
		local amount = details.ItemAmount;
		if amount == 0 then
			amount = GHI_ContainerData[bag][bagSlot].amount;
		end
		return ID,amount,details.ItemOrigFrame,details.ItemOrigBag,bagSlot;
	end
	
	SendTradeInfo = function(player,slot,ID,amount,bagID,bagSlot,texture,name) 
		if not(sendLinksIDs[ID]) then
			itemDataTransfer.SyncItemLinkData(tradePlayer,ID);
			sendLinksIDs[ID] = true;
		end

		local duration = GetTradeItemDuration(ID,bagID,bagSlot) or false;
		local bagInfo = GetTradeItemBagInfo(ID) or false; 
		comm.Send(nil,player,"TradeItem",slot,ID,amount,name,texture,3,duration,bagInfo);
	end
	
	GetTradeItemDuration = function(ID,bagID,bagSlot)
		
		local data1,data2 = GetContainerDurationInfo(bagID,bagSlot);
		local size,Type,start_event = GetDurationInfo(ID);
		if Type then
			
			local duration = 0;
			if data1 then
				if Type == "real_time" then
					duration = floor(data1 - GetTime());		
				elseif Type == "played_time" then
					duration = floor(data1 - (GetTime() - GHI_StartedPlayedTime));
				elseif Type == "charges" then
					duration = data1;
				end
			elseif start_event == "traded" then
				duration = floor(size);
			end
			return {duration,Type,data2};			
		end 	
	end 
	
	GetTradeItemBagInfo = function(ID)
		local itemBagId = GHI_GetBagID(ID)
		
		local bagDetails ={};
		local bagSize = GHI_GetContainerSize(itemBagId);
		for i = 1,bagSize do
			local info = GHI_GetContainerInfo(itemBagId,i);
			if info then
				bagDetails[i] = { 
					containerInfo = info, 
					duration = GetTradeItemDuration(info.ID,itemBagId,i),
					subBagInfo = GetTradeItemBagInfo(info.ID),
				};
			end
		end
		
		bagDetails.size = bagSize;
		return bagDetails;
	end
	
	RecieveTradeItem = function(player,slot,ID,amount,name,texture,itemType,duration,bagInfo,...) 
		if player == tradePlayer then
			CancelAcceptTrade();
			tradeItemsRecipient[slot] = {
				ID = ID,
				amount = amount,
				name = name,
				itemType = itemType,
				texture = texture,
				duration = duration,
				bagInfo = bagInfo,
			};
			TRADEINFO = tradeItemsRecipient;
			UpdateRecipientTradeItem(slot,name,texture,amount,itemType);
		end
	end
	
	RecieveRemoveTradeItem = function(player,slot)
		CancelAcceptTrade();
		
		ClearRecipientButton(slot);
	end
	
	UpdateRecipientTradeItem = function(slot,name,texture,amount,itemType)
		local itemButton = _G["TradeRecipientItem"..slot.."ItemButton"];
		
		SetItemButtonTexture(itemButton, texture);
		SetItemButtonCount(itemButton, amount);
		
		local itemType;
		if itemType == 1 then  -- official item
			itemType = "|CFF" ..string.format("%.2x",0.7*255) .. string.format("%.2x",0*255) .. string.format("%.2x",0*255) .."Official GH item|r"
		else
			itemType = "|CFF" ..string.format("%.2x",0.0*255) .. string.format("%.2x",0.7*255) .. string.format("%.2x",0.5*255) .."Custom made item|r"
		end
		recipientItemTypeTexts[slot]:Show();
		recipientItemTypeTexts[slot]:SetText(itemType);
		_G["TradeRecipientItem"..slot.."Name"]:SetText(name);
		
		
	end
	ClearRecipientButton = function(slot)
		tradeItemsRecipient[slot] = {};		
		local itemButton = _G["TradeRecipientItem"..slot.."ItemButton"];
	
		SetItemButtonTexture(itemButton, "");
		SetItemButtonCount(itemButton, 1);
		
		recipientItemTypeTexts[slot]:Hide();
		_G["TradeRecipientItem"..slot.."Name"]:SetText("");
		CancelAcceptTrade();
	end
	
	AcceptTrade = function(name,...)
		if name == tradePlayer then
			-- delete
			for i = 1,6 do
				local ID,amount,frame,bag,bagSlot = GetGhiItemFromSlot(i);
				if ID then
					RemoveItem(ID,amount,bagSlot,bag);
				end
			end
			
			-- insert
			for i = 1,6 do
				local ID,amount,duration,bagInfo = GetRecipientTradeItem(i);
				RecieveItem(ID,amount,duration,bagInfo);
			end      
			ClearAll(); 
		end
		GHI_UpdateContainers();
	end
	
	GetRecipientTradeItem = function(slot)
		local t = tradeItemsRecipient[slot];
		if type(t)=="table" then
			return t.ID,t.amount,t.duration,t.bagInfo;	
		end
	end
	
	RecieveItem = function(ID,amount,durationInfo,bagInfo)
		if ID then
			itemDataTransfer.SyncItemActionData(tradePlayer,ID);
			InsertItem(ID,amount,durationInfo);
			RecieveBag(ID,bagInfo);
			local dependingItems = GHI_GetDependingItems(ID) or {};
			for _,dependingItemID in pairs(dependingItems) do 
				itemDataTransfer.SyncItemActionData(tradePlayer,dependingItemID);
			end
		end
	end
	
	InsertItem = function(ID,amount,durationInfo)
		local bag,slot = GHI_GetFreeSpace();
		
		GHI_ContainerData[bag][slot] = {
			ID = ID,
			amount = amount,
		};
		SetTradeItemDuration(bag,slot,durationInfo);
	end
	
	RecieveBag = function(ID,bagContenst)
		if type(bagContenst) == "table" then
			local bagIndex = GetFreeBagIndex(ID);		
			GHI_SetContainerInfo(bagIndex,0,{item_id = ID,size = bagContenst.size});
			for slot,info in pairs(bagContenst) do
				if type(slot)=="number" then
					local containerInfo = info.containerInfo;
					local durationInfo = info.duration;
					local subBagInfo = info.subBagInfo;
					
					GHI_SetContainerInfo(bagIndex,slot,containerInfo);
					SetTradeItemDuration(bagIndex,slot,durationInfo);	
					RecieveBag(containerInfo.ID,subBagInfo); 
					itemDataTransfer.SyncItemActionData(tradePlayer,containerInfo.ID);
				end
			end
		end
	end
	
	SetTradeItemDuration = function(bag,slot,durationInfo)
		if durationInfo then
			local duration,durationType,data2 = unpack(durationInfo);
			if durationType == "real_time" then
				duration = GetTime()  + duration;
			elseif durationType == "played_time" then
				duration = (GetTime()-GHI_StartedPlayedTime)  +duration;
			end
			SetContainerDurationInfo(bag,slot,duration,data2);
		end
	end
	
	GetFreeBagIndex = function(ID)
		
		local bags = GHI_GetContainerList(2);
		
		for _,bagIndex in pairs(bags) do
			local info = GHI_GetContainerInfo(bagIndex,0);
			if (info or {}).item_id == ID then
				return bagIndex;
			end
		end
		
		local i = 1;
		while( i <= #(bags) and i == bags[i]-200) do
			i = i+1;
		end
		return i+200;
	end
	
	RemoveItem = function(ID,amount,slot,bag)
		local slotAmount = GHI_ContainerData[bag][slot].amount;
		
		local amountLeft = amount;
		if amountLeft < slotAmount then
			GHI_ContainerData[bag][slot].amount = slotAmount - amountLeft;
			amountLeft = 0;
		else
			GHI_ContainerData[bag][slot] = nil;
			amountLeft = amountLeft - slotAmount;
		end
		
		while amountLeft > 0 do
			bag, slot = GHI_FindItem(ID);
			if bag and slot then
				local slotAmount = GHI_ContainerData[bag][slot].amount;
				
				if amountLeft < slotAmount then
					GHI_ContainerData[bag][slot].amount = slotAmount - amountLeft;
					amountLeft = 0;
				else
					GHI_ContainerData[bag][slot] = nil;
					amountLeft = amountLeft - slotAmount;
				end
			else
				GHI_Message("Could not find all items");
				break;
			end			
		end
				
		if not(GHI_FindItem(ID)) then
			local bagID = GHI_GetBagID(ID);
			RemoveBag(bagID);
		end
	end
	
	RemoveBag = function(bagID)
		if (bagID) then
				GHI_ContainerData[bagID] = {};
		end
	end
	
	TradeItemButtonOnEnter = function(self,slot)
		GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
		local ID = GetGhiItemFromSlot(slot);
		if ID then
			GHI_ItemTooltip(self,ID);
			CursorUpdate();
		end
	end
	TradeItemButtonOnUpdate = function(self)
		if ( self.updateTooltip ) then
			self.updateTooltip = self.updateTooltip - elapsed;
			if ( self.updateTooltip > 0 ) then
				return;
			end
		end
	
		if ( GameTooltip:IsOwned(self) ) then
			TradeItemButtonOnEnter(self,self:GetParent():GetID());
		end
	end
	RecipientTradeItemButtonOnEnter = function(self,slot)
		GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
		local ID = GetRecipientTradeItem(slot);
		if ID then
			GHI_ItemTooltip(self,ID);
			CursorUpdate();
		end
	end
	RecipientTradeItemButtonOnUpdate = function(self)
		if ( self.updateTooltip ) then
			self.updateTooltip = self.updateTooltip - elapsed;
			if ( self.updateTooltip > 0 ) then
				return;
			end
		end
	
		if ( GameTooltip:IsOwned(self) ) then
			RecipientTradeItemButtonOnEnter(self,self:GetParent():GetID());
		end
	end
	
	
	-- SetUp 
	class:SetScript("OnEvent",OnEvent);
	class:RegisterEvent("TRADE_CLOSED");
	class:RegisterEvent("TRADE_ACCEPT_UPDATE");
	class:RegisterEvent("TRADE_SHOW");
	
	comm.AddRecieveFunc("TradeAccepted",AcceptTrade);
	comm.AddRecieveFunc("TradeItem",RecieveTradeItem); 
	comm.AddRecieveFunc("RemoveTradeItem",RecieveRemoveTradeItem);
	
	ping.RegisterRecievePingFunc(RecievePlayerPing);
	
	orig["ClickTradeButton"] = _G["ClickTradeButton"];
	_G["ClickTradeButton"] = ClickTradeButton;
	
	
	local Old_Script_ItemButton = TradePlayerItem1ItemButton:GetScript("OnEnter");
	local Old_Script_ItemButtonUpdate = TradePlayerItem1ItemButton:GetScript("OnUpdate");
	local Old_Script_RecipientItemButton = TradeRecipientItem1ItemButton:GetScript("OnEnter");
	local Old_Script_RecipientItemButtonUpdate = TradeRecipientItem1ItemButton:GetScript("OnUpdate");
	
	for i=1,6 do
		_G["TradePlayerItem"..i.."ItemButton"]:SetScript("OnEnter", function(self) Old_Script_ItemButton(self); TradeItemButtonOnEnter(self,self:GetParent():GetID()); end);
		_G["TradePlayerItem"..i.."ItemButton"]:SetScript("OnUpdate", function(self) Old_Script_ItemButtonUpdate(self); TradeItemButtonOnUpdate(self,arg1); end);
		_G["TradeRecipientItem"..i.."ItemButton"]:SetScript("OnEnter", function(self) Old_Script_RecipientItemButton(self); RecipientTradeItemButtonOnEnter(self,self:GetParent():GetID()); end);
		_G["TradeRecipientItem"..i.."ItemButton"]:SetScript("OnUpdate", function(self) Old_Script_RecipientItemButtonUpdate(self); RecipientTradeItemButtonOnUpdate(self,arg1); end);
	end
	
	CreateItemTypeTexts();
	
	if TradeFrame:IsShown() then
		OnEvent(nil,"TRADE_SHOW");
	end	
	return class;
end

