--[[
path: /PetFeeder/
filename: PetFeeder.lua
author: Jeff Parker <jeff3parker@gmail.com>
created: Tue, 22 Jan 2005 14:15:00 -0800
updated: Tue, 22 Jan 2005 21:39:00 -0800

Pet Feeder: a GUI interface allowing you configure happiness level for your pet & 
			drag/drop foods you wish your pet to eat.  When the pet happiness drops below
			the selected threshold will automatically feed your pet.
			To remove a food from the list, simply click on it.
]]


PETFEEDER_ITEM_HEIGHT = 32;
PETFEEDER_ITEMS_SHOWN = 7;

PETFEEDER_FL_DISLIKED = 0;
PETFEEDER_FL_UNKNOWN = 1;
PETFEEDER_FL_APPROVED = 2;


-- Configuration
PeetFeederPlayer_Config = {};

UIPanelWindows["PetFeederFrame"] = { area = "left", pushable = 4 };
PETFEEDER_TAB_SUBFRAMES = {  "PetFeeder_FoodsFrame", "PetFeeder_ApprovedFoodsFrame", "PetFeeder_UnlikedFoodsFrame" };

-- Foods Lists
PetFeederPlayer_Foods = {};
PetFeeder_Foods = {};
PetFeeder_BadFoods = {};
PetFeeder_QuestItems = {};
PetFeeder_PetName = "";
local LastPetName = nil;
PetFeeder_Pets = {};

-- Variables
PetFeeder_Var = { };

PetFeeder_Var.PetInCombat = false;
PetFeeder_Var.PlayerInCombat = false;
PetFeeder_Var.PetDead = false;
PetFeeder_Var.PlayerDead = false;
PetFeeder_Var.TradeOrLoot = false;
PetFeeder_Var.Searching = false;
PetFeeder_Var.isSitting = false;
PetFeeder_Var.sitPosX = -1;
PetFeeder_Var.sitPosY = -1;
PetFeeder_Var.feedStartTime = 0;
PetFeeder_Var.debug = false;
PetFeeder_Var.AutoFindFoodTimer = 0;
PetFeeder_Var.AutoFindFoodTimeout = 60; -- 60 seconds
PetFeeder_Var.LastItemAttempted = nil;
PetFeeder_Var.Feed = false;
PetFeeder_Var.DialogShowing = false;


-- Hooked function variables
Pre_SitOrStand = nil;
Pre_DoEmote = nil;
Pre_PetFeeder_ZMI = nil;
Pre_PetFeeder_ZMO = nil;

--Pre_Jump = nil; -- blocked by Blizzard in v1.10

PetDies_ChatParseInfo = { AddOn = "PetFeeder" };

function PetFeeder_PetIsDead(value)
	PetFeeder_Var.PetDead = value;
	if ( value == true )  then
		PFDebugMessage("PF-Died", "PET DIED. <from messages>", "debug");
	else
		PFDebugMessage("PF-Died", "PET REVIVED. <from messages>", "debug");
	end
end

--[[
=============================================================================
Debug message output 
=============================================================================
]]
function PFDebugMessage(x,y,z) 
	if ( z == "error" ) then 
		DEFAULT_CHAT_FRAME:AddMessage(format("|cffff0000[%s]: %s|r", x, y)) 
	end
	
	if ( PetFeeder_Var.debug ) then
		DEFAULT_CHAT_FRAME:AddMessage(format("|ccfff0000[%s]: %s|r", x, y)) 
	end
end


--[[
=============================================================================
The loading frame handles getting our AddOn initialized properly 
=============================================================================
]]
function PetFeederLoadingFrame_OnUpdate()
	if ( PetFeederFrame.loaded ) then
		PetFeederLoadingFrame:UnregisterEvent("VARIABLES_LOADED");
		PetFeederLoadingFrame:UnregisterEvent("UNIT_NAME_UPDATE");
		PetFeederLoadingFrame:Hide();
		PetFeederLoadingFrame = nil;

		
	elseif ( PetFeederLoadingFrame.loadTime + 10 <= GetTime() ) then
		--PetFeederFrame_LoadData();
		PetFeederFrame.loaded = true;
		PetFeederLoadingFrame.loadTime = GetTime();
	end
end

function PetFeederLoadingFrame_OnLoad()
	PetFeederLoadingFrame:RegisterEvent("VARIABLES_LOADED");
	PetFeederLoadingFrame:RegisterEvent("UNIT_NAME_UPDATE");
	PetFeederLoadingFrame.init = 0;
	PetFeederLoadingFrame.loadTime = GetTime();

end

function PetFeederLoadingFrame_OnEvent(event, arg1)
	if ( not PetFeederLoadingFrame ) then
		return;
	end
	if ( event == "VARIABLES_LOADED" or event == "UNIT_NAME_UPDATE" ) then
		PetFeederLoadingFrame.init = PetFeederLoadingFrame.init + 1;
	end

	

end

--[[
=============================================================================
Initialize data, perform hooks, register for events
=============================================================================
]]


function PetFeederFrame_LoadData()

	if ( UnitClass("player") ~= PETFEEDER_HUNTER) then
		return;
	end

	PetFeederFrame_InitConfig();
	
	PetFeeder_PetName = UnitName( "pet" );
	if ( PetFeeder_PetName == "Unknown Entity" ) then
		PetFeeder_PetName = nil;
		return
	end
	if ( PetFeeder_PetName ~= nil ) then
		initChatParseforPetDiesEvent();
		PetDies_ChatParseInfo.template = PetFeeder_PetName.." dies.";
		PFChatParse_RegisterEvent(PetDies_ChatParseInfo);
		PFDebugMessage("PF-Register", "registering Pet Died event", "debug");
	-- Check for old version of foods
		if ( PetFeederPlayer_Foods[PetFeeder_PetName] and getn(PetFeederPlayer_Foods[PetFeeder_PetName]) > 0) then
			anItem = PetFeederPlayer_Foods[PetFeeder_PetName][1];
			if ( not anItem.quality ) then
				FixOldInventoryData();
			end
		end
		
		if ( PetFeederPlayer_Foods[PetFeeder_PetName] ) then
			for i=1, table.getn( PetFeederPlayer_Foods[PetFeeder_PetName] ) do
				if ( not PetFeederPlayer_Foods[PetFeeder_PetName][i].foodlikedstate ) then
					PetFeederPlayer_Foods[PetFeeder_PetName][i].foodlikedstate = PETFEEDER_FL_UNKNOWN;
					return true;
				end
			end
		end
	end
	
	
		
	PetFeederFrame.loaded = true;
end

function PetFeederFrame_InitConfig()
	
	PetFeederFrame_SetPlayerConfig("Enabled",1);
	PetFeederFrame_SetPlayerConfig("BarEnabled",1);
	PetFeederFrame_SetPlayerConfig("Alert",1);
	PetFeederFrame_SetPlayerConfig("Level",2);
	PetFeederFrame_SetPlayerConfig("SortOption",1);
	PetFeederFrame_SetPlayerConfig("SortOption1",1);
	PetFeederFrame_SetPlayerConfig("SortOption2",1);
	PetFeederFrame_SetPlayerConfig("AutoFindFood",1);
	PetFeederFrame_SetPlayerConfig("SkipBuffFoods",1);
	PetFeederFrame_SetPlayerConfig("RequireApproval",1);
	PetFeederFrame_SetPlayerConfig("FeedOnlyApproved",1);

end
function PetFeederFrame_SetPlayerConfig(ftableitem,defaultvalue)

	if ( not defaultvalue ) then
		defaultvalue = 0;
	end
	
	PeetFeederPlayer_Config[ftableitem] = PeetFeederPlayer_Config[ftableitem] or defaultvalue;
			
end


function initChatParseforPetDiesEvent()
	PetDies_ChatParseInfo.event = "CHAT_MSG_COMBAT_FRIENDLY_DEATH";
	PetDies_ChatParseInfo.func  = function(t) PetFeeder_PetIsDead(true); end;
	PetDies_ChatParseInfo.template = "%s "..PETFEEDER_PET_DIES_MSG;
	PetDies_ChatParseInfo.english = "Aslok dies."; -- example
	PetDies_ChatParseInfo.fields   = {  };
end
--[[
=============================================================================
Initialize saves, slash cmd and panels
=============================================================================
]]
function PetFeederFrame_OnLoad()
	
	if ( UnitClass("player") ~= PETFEEDER_HUNTER ) then
		if ( DEFAULT_CHAT_FRAME ) then
			DEFAULT_CHAT_FRAME:AddMessage("|cFFFFFF00PetFeeder:|r "..PETFEEDER_SESSION_DISABLED);
		end
		return;
	end
	
	PetFeederFrame_LoadData();
	
	-- Register for Events
	this:RegisterEvent("PET_ATTACK_START");
	this:RegisterEvent("PET_ATTACK_STOP");
	this:RegisterEvent("UNIT_HAPPINESS");
	this:RegisterEvent("PLAYER_REGEN_DISABLED");
	this:RegisterEvent("PLAYER_REGEN_ENABLED");
	this:RegisterEvent("PLAYER_PET_CHANGED");
	this:RegisterEvent("PLAYER_ALIVE");
	this:RegisterEvent("PLAYER_UNGHOST")
	this:RegisterEvent("PLAYER_DEAD");
	this:RegisterEvent("TRADE_SHOW");
	this:RegisterEvent("TRADE_CLOSED");
	this:RegisterEvent("LOOT_SHOW");
	this:RegisterEvent("LOOT_CLOSED");
	this:RegisterEvent("CHAT_MSG_COMBAT_FRIENDLY_DEATH");
	this:RegisterEvent("PET_BAR_UPDATE"); -- to get the rez pet event
	this:RegisterEvent("BAG_UPDATE");
	this:RegisterEvent("UNIT_NAME_UPDATE");
	this:RegisterEvent("LOCALPLAYER_PET_RENAMED");	
	
	-- this is player initiated and will help fire the feeding
	this:RegisterEvent("PLAYER_TARGET_CHANGED");
	
	-- Hook functions
	if ( not Pre_DoEmote ) then
		Pre_DoEmote = DoEmote;
		DoEmote = PetFeeder_DoEmote;
	end

	if ( not Pre_PetFeeder_ZMI ) then
		Pre_PetFeeder_ZMI = CameraZoomIn;
		CameraZoomIn = PetFeeder_ZMI;
	end
	
	if ( not Pre_PetFeeder_ZMO ) then
		Pre_PetFeeder_ZMO = CameraZoomOut;
		CameraZoomOut = PetFeeder_ZMO;
	end
	

	local chatParseInfo = { AddOn = "PetFeeder" };
	chatParseInfo.event    = "CHAT_MSG_SPELL_FAILED_LOCALPLAYER";
	chatParseInfo.func     = function(t) PetFeeder_RemoveBadFood(); end;
	chatParseInfo.template = PETFEEDER_FAILED_TO_FEED;
	chatParseInfo.english  = "You fail to perform Feed Pet: Your pet doesn't like that food.";
	chatParseInfo.fields   = {  };

	if ( not PFChatParse_RegisterEvent ) then
		PFDebugMessage("PF", "function PFChatParse_RegisterEvent not defined!", "error");
	end

	PFChatParse_RegisterEvent(chatParseInfo);
	
	local chatParseInfo1 = { AddOn = "PetFeeder" };
	chatParseInfo1.event    = "CHAT_MSG_SPELL_FAILED_LOCALPLAYER";
	chatParseInfo1.func     = function(t) PetFeeder_RemoveBadFood(); end;
	chatParseInfo1.template = PETFEEDER_FOODTOOLOW;
	chatParseInfo1.english  = "You fail to perform Feed Pet: That food's level is not high enough for your pet.";
	chatParseInfo1.fields   = {  };
	PFChatParse_RegisterEvent(chatParseInfo1);

	local chatParseInfo2 = { AddOn = "PetFeeder" };
	chatParseInfo2.event    = "CHAT_MSG_SYSTEM";
	chatParseInfo2.func     = function() PetFeeder_DoEmote("SIT"); end;
	chatParseInfo2.template = PETFEEDER_AFK;
	chatParseInfo2.english  = "You are now AFK: Away from Keyboard";
	chatParseInfo2.fields   = {  };
	PFChatParse_RegisterEvent(chatParseInfo2);

	if ( DEFAULT_CHAT_FRAME ) then
		DEFAULT_CHAT_FRAME:AddMessage("Jeff's "..PETFEEDER_TITLE.." AddOn loaded.  Use /pf");
	end
	UIErrorsFrame:AddMessage(loadMessage, 1.0, 1.0, 0.0, 1.0, UIERRORS_HOLD_TIME);

	-- Register Slash Commands
	SLASH_PetFeeder1 = "/PetFeeder";
	SLASH_PetFeeder2 = "/pf";
	SlashCmdList["PetFeeder"] = function(msg)
		PetFeeder_SlashCmd(msg);
	end

	-- Tab Handling code
	PanelTemplates_SetNumTabs(PetFeederFrame, 3);
	PanelTemplates_SetTab(PetFeederFrame, 1);
	
	this.loaded = nil;
	
end

--[[
=============================================================================
Called when PF opens or closes
=============================================================================
]]
function PetFeederFrame_OnShow()
	PlaySound("igCharacterInfoOpen");
	PetFeeder_Update_Frames();
	
end

function PetFeederFrame_OnHide()
	PlaySound("igCharacterInfoClose");
end

--[[
=============================================================================
Hooked Functions
 replaces the original calls with our own and then calls the original.  These
 methods are hooked so we can detect when a player performs a specific action
 such as sitting, jumping, etc.
=============================================================================
]]
function PetFeeder_DoEmote(token,...)	

	if ( token == "SIT" or token == "sit") then
		 -- DEFAULT_CHAT_FRAME:AddMessage("emote SIT");
		 PetFeeder_Var.sitPosX, PetFeeder_Var.sitPosY = GetPlayerMapPosition("player");
		 PetFeeder_Var.isSitting = true;
	end
	if ( token == "STAND" ) then
		PetFeeder_Var.isSitting = false;
	end
	
	if ( table.getn(arg) > 0 ) then
		Pre_DoEmote(token,arg[1]);	
	else
		Pre_DoEmote(token);
	end
	
end

-- Zoom in mouse wheel hook to call our event handler
function PetFeeder_ZMI(arg1)
	PetFeederFrame_OnEvent("PLAYER_TARGET_CHANGED","pet");
	Pre_PetFeeder_ZMI(arg1);
end

-- Zoom out mouse wheel hook to call our event handler
function PetFeeder_ZMO(arg1)
	PetFeederFrame_OnEvent("PLAYER_TARGET_CHANGED","pet");
	Pre_PetFeeder_ZMO(arg1);
end


--[[
=============================================================================
Walked through our list of foods and obtains the ItemCount for each
=============================================================================
]]
function PetFeeder_UpdateQuantities()
	PetFeeder_GetQuestItems();
	for index, value in PetFeederPlayer_Foods[PetFeeder_PetName] do
		-- update the item count
		PetFeederPlayer_Foods[PetFeeder_PetName][index].quantity = PetFeeder_GetItemCount( value.name );

	end
end


function PetFeeder_GetQuestItems()
	
	PetFeeder_QuestItems = nil;
	PetFeeder_QuestItems = {};
	local numEntries, numQuests = GetNumQuestLogEntries()
	
	for questNum = 1,  GetNumQuestLogEntries() do
		SelectQuestLogEntry(questNum);
		local questTitle, level, questTag, isHeader, isCollapsed, isComplete = GetQuestLogTitle(questNum);
		if ( not isHeader ) then
			for requiredItem = 1, GetNumQuestLeaderBoards(questNum) do
				
				local text, type, finished = GetQuestLogLeaderBoard(requiredItem);
				if ( type == "item" ) then
					local _, _, itemName, numCurrent, numRequired = string.find(text, "(.*): (%d+)/(%d+)");
					--DEFAULT_CHAT_FRAME:AddMessage("itemName="..itemName);	
					--DEFAULT_CHAT_FRAME:AddMessage("numRequired="..numRequired);	
					if (PetFeeder_QuestItems[itemName]) then
						PetFeeder_QuestItems[itemName] = PetFeeder_QuestItems[itemName] + numRequired;
					else
						PetFeeder_QuestItems[itemName] = numRequired;
					end
				end
			end
		end
		
	end
end

function togglePetFeeder(tab)
	if not ( PetFeeder_HasPet() ) then
		UIErrorsFrame:AddMessage(PETFEEDER_ESTABLISH_PET, 0.8, 0, 0, 1.0, UIERRORS_HOLD_TIME);
		return;
	end
	
	if ( not tab ) then
		if ( PetFeederFrame:IsVisible() ) then
			HideUIPanel(PetFeederFrame);
		else
			ShowUIPanel(PetFeederFrame);
			local selectedFrame = getglobal(PETFEEDER_TAB_SUBFRAMES[PetFeederFrame.selectedTab]);
			if ( not selectedFrame:IsVisible() ) then
				selectedFrame:Show();
			end
		end
	else
		local subFrame = getglobal(tab);
		if ( subFrame ) then
			PanelTemplates_SetTab(PetFeederFrame, subFrame:GetID() );
			if ( PetFeederFrame:IsVisible() ) then
				if ( subFrame:IsVisible() ) then
					HideUIPanel( PetFeederFrame );
				else
					PlaySound("igCharacterInfoTab");
					PetFeederFrame_ShowSubFrame(tab);
				end
			else
				ShowUIPanel( PetFeederFrame );
				PetFeederFrame_ShowSubFrame(tab);
			end
		end
	end


end

function PetFeederFrame_ShowSubFrame(frameName)
	for index, value in PETFEEDER_TAB_SUBFRAMES do
		if ( value == frameName ) then
			getglobal(value):Show();
		else
			getglobal(value):Hide();
		end
	end
end
---------------------------------------------------
--Show config dialog when slash-command is called--
---------------------------------------------------
function PetFeeder_SlashCmd(msg)

	if ( not PetFeeder_HasPet() ) then
		UIErrorsFrame:AddMessage(PETFEEDER_ESTABLISH_PET, 0.8, 0, 0, 1.0, UIERRORS_HOLD_TIME);
		return;
	end	
	
	PetFeeder_PetName = UnitName( "pet" );
	
	if (msg == "feed" ) then
	  PetFeeder_Feed();
	elseif ( msg == "clear" ) then			
		PetFeeder_ClearFoods();
	elseif ( msg == "clearbad" ) then			
		PetFeeder_ClearBadFoods();
	elseif ( msg == "dump" ) then
		PetFeeder_DebugDump();
	elseif ( msg == "toggledebug" ) then
		PetFeeder_Var.debug = not PetFeeder_Var.debug;
		if ( PetFeeder_Var.debug ) then
			DEFAULT_CHAT_FRAME:AddMessage("debug is ON");
		else
			DEFAULT_CHAT_FRAME:AddMessage("debug is OFF");
		end
	elseif ( msg == "buff" ) then
		PetFeeder_PlayerBuff();
	elseif ( msg == "populate" ) then
		PetFeeder_PopulateFoods();
	else
			togglePetFeeder(nil);
	end
	
end

-- Do not localize this
function PetFeeder_DebugDump()
	local i;
	DEFAULT_CHAT_FRAME:AddMessage("Petname is "..PetFeeder_PetName);
	DEFAULT_CHAT_FRAME:AddMessage("PetFeeder Foods in the order of consumption");
	for index, value in PetFeederPlayer_Foods[PetFeeder_PetName] do
		DEFAULT_CHAT_FRAME:AddMessage(value.name.." "..value.foodlikedstate);
	end
end

function PetFeeder_GetID(button)
	if ( button == nil ) then
		return 0;
	end

	return (button:GetID())
end

LLLAstEvetnt = nil;
local PetRename = false;


function PetFeederFrame_OnEvent(event, arg1)
	--[[if ( LLLAstEvetnt ~= event ) then
		LLLAstEvetnt = event;
		DEFAULT_CHAT_FRAME:AddMessage(event);
		if ( arg1 ) then
			DEFAULT_CHAT_FRAME:AddMessage("Arg1: "..arg1);
		end
		if ( arg2 ) then
			DEFAULT_CHAT_FRAME:AddMessage("Arg2: "..arg2);
		end
			
	end]]
	
	if (not PeetFeederPlayer_Config.Enabled ) then
		PFDebugMessage("PF", "PF is disabled", "debug");
		return;
	end

	local eventmsg = "event="..event;
	PFDebugMessage("PF", eventmsg, "debug");
	if (arg1 ) then
		eventmsg = "arg1="..arg1;
		PFDebugMessage("PF", eventmsg, "debug");
	end

	 if ( event == "PLAYER_PET_CHANGED" ) then
	 	PetFeeder_Var.PetInCombat = false;
	 	PetFeeder_Var.PetDead = false;
	 	PetFeeder_Var.PlayerInCombat = false;
	 	PetFeeder_Var.PlayerDead = false;
		initChatParseforPetDiesEvent();
		PFChatParse_UnregisterEvent(PetDies_ChatParseInfo);
		PetDies_chatParseInfo.template = PetFeeder_PetName.." dies.";
		PFChatParse_RegisterEvent(PetDies_ChatParseInfo);
		PFDebugMessage("PF-Register", "registering Pet Died event", "debug");
	 	return;
	 end
	
	if ( event == "PET_ATTACK_START" ) then
		PetFeeder_Var.PetInCombat = true;
		PFDebugMessage("PF", "Pet START COMBAT", "debug");
		return;
	elseif ( event == "PLAYER_REGEN_DISABLED" ) then		
		PetFeeder_Var.PlayerInCombat = true;
		return;
	elseif ( event == "PET_ATTACK_STOP" ) then
		PFDebugMessage("PF", "Pet EXIT COMBAT", "debug");
		PetFeeder_Var.PetInCombat = false;
	elseif ( event == "PLAYER_REGEN_ENABLED" ) then
		PetFeeder_Var.PlayerInCombat = false;
	elseif ( event == "PET_BAR_UPDATE" and arg1 == nil) then
		if ( not PetFeeder_HasPet() ) then
			return;
		end
		PetFeeder_PetName = UnitName("pet");
		if ( LastPetName ) then
			LastPetName = nil;
		else
			LastPetName = PetFeeder_PetName;
		end
		PetFeeder_Var.PetDead = false;
		PetFeeder_Var.PetInCombat = false; -- Pet could have died during combat and we don't get notified
		PFDebugMessage("PF-Alive", "Pet is alive <showgrid>", "debug");
		if ( PeetFeederPlayer_Config.AutoFindFood ) then
			PetFeeder_PopulateFoods();
		end
		PetFeeder_Update_Frames();
	elseif ( event == "PLAYER_DEAD" ) then
		PetFeeder_Var.PlayerDead = true;
		return;
	elseif ( event == "PLAYER_ALIVE" or event == "PLAYER_UNGHOST" ) then  -- no notification of deaths, be sure to clear these
		PetFeeder_Var.PetInCombat = false;
		PetFeeder_Var.PlayerInCombat = false;
		PetFeeder_Var.PlayerDead = false;
	elseif ( event == "TRADE_SHOW" or event == "LOOT_SHOW") then
		PetFeeder_Var.TradeOrLoot = true;
		return;
	elseif ( event == "TRADE_CLOSED" or event == "LOOT_CLOSED") then
		PetFeeder_Var.TradeOrLoot = false;
		return;
	elseif (  event == "BAG_UPDATE" or event == "UNIT_PET" ) then
		if ( not PetFeeder_HasPet() ) then
			return;
		end
		if ( PeetFeederPlayer_Config.AutoFindFood ) then
			PetFeeder_PopulateFoods();
		end
		PetFeeder_Update_Frames();
	elseif ( event == "LOCALPLAYER_PET_RENAMED" ) then
		PetRename = true;
	elseif ( event == "UNIT_NAME_UPDATE" ) then
		if ( arg1 and arg1 == "pet" ) then
			if ( not PetFeeder_HasPet() ) then
				return;
			end
			if ( PetFeeder_PetName ~= UnitName("pet") ) then
				--DEFAULT_CHAT_FRAME:AddMessage("UNIT_NAME_UPDATE is running "..UnitName("pet"));
				if ( PetRename ) then
					if ( PetFeederPlayer_Foods[PetFeeder_PetName] ) then
						PetFeederPlayer_Foods[UnitName("pet")] = PetFeederPlayer_Foods[PetFeeder_PetName];
					end
					PetRename = false;
				
				elseif ( PetFeederPlayer_Foods[PetFeeder_PetName] ) then 
					PetFeederPlayer_Foods[PetFeeder_PetName] = {}; 
					LastPetName = UnitName("pet");
				end
				PetFeeder_PetName = UnitName("pet");
				
			else
				
				return;
			end
			if ( PeetFeederPlayer_Config.AutoFindFood ) then
				PetFeeder_PopulateFoods();
			end
			PetFeeder_Update_Frames();
			return;
		else
			return;
		end
	
	end
	

		
	-- HACK
	-- in v1.10 Blizzard changed the DropItemOnUnit( ) method so that it must be player
	-- click event related.  This event identifies such an event.
	-- There's no reason to even try feeding the pet unless this event has fired otherwise the feed attempt will fail.
	if ( event == "PLAYER_TARGET_CHANGED" ) then
		if ( PeetFeederPlayer_Config.Enabled ) then
			PFDebugMessage("PF", "Attempt to feed", "debug");
			if ( PetFeeder_CheckHappiness() == false ) then
				return; -- feeding not necessary
			end
			PetFeeder_Feed();
		end
	end
end


function PetFeeder_CanFeed()
	-- Abort routines
	if not ( PetFeeder_HasPet() ) then
		PFDebugMessage("PF-Abort", "cannot find a pet", "debug");
		PetFeeder_Var.PetInCombat = false;  -- Pet could have died during combat and we don't get notified
		return;
	end
	if ( UnitHealth("pet") <= 0 ) then
		PFDebugMessage("PF-Abort", "Pet Dead", "debug");
		return false;
	end
	if ( UnitHealth("player") <= 0 ) then
		PFDebugMessage("PF-Abort", "Player Dead", "debug");
		return false;
	end
	if ( CastingBarFrameStatusBar:IsVisible() ) then
		PFDebugMessage("PF-Abort", "Player Casting Spell", "debug");
		return false;
	end
	if ( UnitOnTaxi("player") ) then
		PFDebugMessage("PF-Abort", "Player On Taxi", "debug");
		return false;
	end

	
	if ( PetFeeder_Var.PetInCombat == true or PetFeeder_Var.PlayerInCombat == true ) then
		PFDebugMessage("PF-Abort", "Can't feed while in combat", "debug");
		return false;
	end
	
	if ( PetFeeder_HasFeedEffect() ) then
		PFDebugMessage("PF-Abort", "Pet already has Feed effect", "debug");
		return false;
	end
	
-- Extra check to clear sitting flag
	if ( PetFeeder_Var.isSitting == true) then
		local posX, posY = GetPlayerMapPosition("player");
		if ( PetFeeder_Var.sitPosX ~= posX ) then
			PetFeeder_Var.isSitting = false;
		elseif (PetFeeder_Var.sitPosY ~= posY ) then
			PetFeeder_Var.isSitting = false;
		end
	end
	
-- Must have Pet from here down
	if ( not PetFeeder_PetName or PetFeeder_PetName == "" or PetFeeder_PetName == "Unknown Entity" ) then
		PetFeeder_PetName = UnitName( "pet" );
		if ( not PetFeeder_PetName or PetFeeder_PetName == "" ) then
			PFDebugMessage("PF-Critical", "Did not find pet in <event handler>", "debug");
			return false;
		end	
		initChatParseforPetDiesEvent();
		PFChatParse_UnregisterEvent(PetDies_ChatParseInfo);
		PetDies_ChatParseInfo.template = PetFeeder_PetName.." dies.";
		PFChatParse_RegisterEvent(PetDies_ChatParseInfo);
		PFDebugMessage("PF-Register", "registering Pet Died event", "debug");
	end

	if ( not PetFeederPlayer_Foods[PetFeeder_PetName] ) then
	  PFDebugMessage("PF", "inserting new pet", "debug");
	  PetFeederPlayer_Foods[PetFeeder_PetName] = {};
	end
	
	

	-- Is there a debuff on the pet that will break the feeding Buff?
	if ( PetFeeder_PetDebuff() ) then
		PFDebugMessage("PF-Abort", "Pet is debuffed", "debug");
		return false;
	end

	-- check for player buffs that might interfere with feeding
	-- or result in undesired effects (such as stopping feign death or shadowmelding)
	if ( PetFeeder_PlayerBuff() ) then
		PFDebugMessage("PF-Abort", "Can't feed with these player buffs", "debug");
		return false;
	end
	
	return true;
end


-- Check player buffs
-- return true if buff enabled we don't want to break by feeding
-- (i.e. FeignDeath or Shadowmeld )
function PetFeeder_PlayerBuff()
	local i = 1;
	local buff;
	local unit = "player";
	buff = UnitBuff(unit,i);
	while buff do
		--PetFeederTooltip:SetUnitBuff( unit, i );
		--DEFAULT_CHAT_FRAME:AddMessage("debug::Player buff::"..i.."="..buff);
		local debuginfo = "Player buff::"..i.."="..buff;
		if ( isMounted( unit, i, buff ) ) then
			PFDebugMessage("PF-Abort", "Player is mounted", "debug");
			return true;
		elseif ( string.find(buff, "Ability_Rogue_FeignDeath") ) then
			PFDebugMessage("PF-Abort", debuginfo, "debug");
			return true;
		elseif ( string.find(buff, "Ability_Ambush") ) then -- shadowmeld
			PFDebugMessage("PF-Abort", debuginfo, "debug");
			return true;
		elseif ( string.find(buff, "DemonBreath") ) then -- water breathing
			PFDebugMessage("PF-Abort", debuginfo, "debug");
			return true;
		end		
		i = i + 1;
		buff = UnitBuff(unit,i);
	end

	-- Check for eating or drinking
	local playerBuffs = {"Interface\\Icons\\INV_Drink_07","Interface\\Icons\\INV_Misc_Fork&Knife"};
	for i=0, 15 do
		buff = GetPlayerBuffTexture(i);
		if ( buff ) then
			for k,v in playerBuffs do
				if ( buff == v ) then
					PFDebugMessage("PF-Abort", buff, "debug");
					return true;
				end
			end
		end
	end

	if ( PetFeeder_Var.TradeOrLoot ) then
		return true;
	end
	
	return false;
end

--=============================================================================
-- Return information about the pattern
--
-- pattern	pattern to find
local function BuffInformation( pattern )
	if pattern then
		return string.find( PetFeederTooltipTextLeft2:GetText(), pattern );
	else
		return 1;
	end
end
--=============================================================================

-- Check to see if the player is mounted
function isMounted(unit, i, buff )
	PetFeederTooltip:SetUnitBuff( unit, i );
	if ( string.find(buff, "_Mount_" ) or string.find(buff,"INV_Misc_Foot_Kodo") ) then
		local startpos,endpos,buffValue = BuffInformation(" (%d+)%%");
		if ( buffValue ~= nil ) then
			buffValue = buffValue + 0;
			if ( buffValue >=60 ) then
				return true;
			end
		end
	end
	
	return false;
	
end

-- Check pet debuffs
-- return true if pet has debuff that will break feeding
-- (i.e. poisoned )
function PetFeeder_PetDebuff()
	local i = 1;
	local buff;
	buff = UnitDebuff("pet", i);
	
	while buff do
		local debuginfo = "Pet debuff::"..i.."="..buff;
		--PFDebugMessage("PF", debuginfo, "debugbuff");
		
		if ( string.find(buff, "Spell_Nature_CorrosiveBreath") ) then
			return true;  -- this comes from a Venom Spitter when it poisons the target
		end
		if ( string.find(buff, "Spell_Nature") ) then
			return true;   -- this might be too liberal, but should catch all instances we are searching for
		end
		i = i + 1;
		buff = UnitDebuff("pet", i);
	end
	return false;

end

-- Check Feed Effect
function PetFeeder_HasFeedEffect()
	local i = 1;
	local buff;
	buff = UnitBuff("pet", i);
	
	while buff do
		local debuginfo = "Pet buff::"..i.."="..buff;
		--PFDebugMessage("PF", debuginfo, "debugbuff");

		if ( string.find(buff, "Ability_Hunter_BeastTraining") ) then
			return true;
		end
		i = i + 1;
		buff = UnitBuff("pet", i);
	end
	return false;

end

-- Check Happiness
function PetFeeder_CheckHappiness()

	if ( UnitHealth("pet") <= 0 ) then
		PFDebugMessage("PF-Abort", "Pet Dead", "debug");
		return false;
	end
	if ( UnitHealth("player") <= 0 ) then
		PFDebugMessage("PF-Abort", "Player Dead", "debug");
		return false;
	end
	
	-- Get Pet Info
	--local pet = UnitName("pet");
	local happiness, damage, loyalty = GetPetHappiness();
	
	local level = PeetFeederPlayer_Config.Level + 1;
	
	-- Check Happiness
	if ( happiness == 0 ) or ( happiness == nil ) then
		PFDebugMessage("PF-Abort", "Unable to determine pet happiness", "debug");
		return false;
	end
	
	if ( happiness >= level ) then
		local msg = "Feeding not necessary.  Pet happiness is at threshold:"..PETFEEDER_LEVELS_DROPDOWN[happiness-1].name;
		PFDebugMessage("PF-Abort", msg, "debug");
		return false;
	end

	-- Check if Feeding is needed
	PFDebugMessage("PF", "pet isn't happy enough", "debug");

	if not ( PetFeeder_Var.Searching ) then	
		return true;
	else
		PFDebugMessage("PF-Abort", "Pet already searching for food", "debug");
	end

	return false;
end

-- Feed Pet
function PetFeeder_Feed()
	
	if ( not PetFeeder_Var.feedStartTime) then
		PetFeeder_Var.feedStartTime = GetTime();
	else
		local time = GetTime();
		local timePast = time - PetFeeder_Var.feedStartTime;
		if ( timePast < 2 ) then
			return;
		end
	end
	
	

	if ( not PetFeeder_CanFeed() ) then return; end
	
	-- Check if dragging item
	if ( CursorHasItem() ) then 
		PFDebugMessage("PF-Abort", "item is on cursor", "debug"); 
		return; 
	end
		
	-- Make sure PetFeeder_PetName has pet name
	PetFeeder_PetName = UnitName( "pet" );
	if ( not PetFeederPlayer_Foods[PetFeeder_PetName] ) then
		PetFeederPlayer_Foods[PetFeeder_PetName] = {};
	end
	
	PetFeeder_UpdateQuantities();
	PetFeeder_Var.Searching = true;
	if ( PeetFeederPlayer_Config.Alert ) then
		DEFAULT_CHAT_FRAME:AddMessage(PetFeeder_PetName..PETFEEDER_BEGIN_SEARCH);
	end			

	-- 1. Get first available food from our list with quantity > 0
	-- 2. Find the m,n location of it in the pack.
	-- 3. Pick up and eat.
	local lowestQuantity = 99;
	local lowestM = 0;
	local lowestN = 0;
	for index, value in PetFeederPlayer_Foods[PetFeeder_PetName] do
		if ( value.quantity > 0 ) then
			
			if ( PeetFeederPlayer_Config.RequireApproval and PeetFeederPlayer_Config.RequireApproval == 1 and value.foodlikedstate == PETFEEDER_FL_UNKNOWN ) then
				PetFeeder_ApproveFoodItem(value.name);
				PetFeeder_Var.Feed = true;
				
				return;
			end
			if ( (PeetFeederPlayer_Config.FeedOnlyApproved == 1 and value.foodlikedstate == PETFEEDER_FL_APPROVED) or ( PeetFeederPlayer_Config.FeedOnlyApproved == 0 and value.foodlikedstate > 0 ) )then 
				
				 -- Find lowest instance of the food item
				for m = 0, 4 do
					for n = 1, 18 do
						itemObject = PetFeeder_GetItemObject(m,n);
						if ( itemObject and itemObject.name == value.name ) then
							-- Using this itemCount because we need the actual value in the slot
							local texture, itemCount, locked, quality, readable = GetContainerItemInfo(m,n);
							if ( itemCount < lowestQuantity ) then						
								--DEFAULT_CHAT_FRAME:AddMessage("lowest M = "..m.." lowestN="..n.." lowestqty="..itemCount);
								lowestQuantity = itemCount;
								lowestM = m;
								lowestN = n;
							end
						end
					end
				end
				
				local msg = "Feed item in bag,slot="..lowestM..","..lowestN.." which is "..value.name;
				PFDebugMessage("PF", msg, "debug" );
				
				PickupContainerItem( lowestM, lowestN );
	
				PetFeeder_Var.feedStartTime = GetTime();
				
				if ( CursorHasItem() ) then				
					DropItemOnUnit("pet");
					PetFeeder_Var.LastItemAttempted = value;
					PFDebugMessage("PF", "Fed item "..value.name, "debug" );
				end
				if ( CursorHasItem() ) then
					PickupContainerItem(lowestM, lowestN);
				end
	
				value.quantity = value.quantity - 1;
				-- Alert
				if ( PeetFeederPlayer_Config.Alert ) then
					DEFAULT_CHAT_FRAME:AddMessage(PetFeeder_PetName..PETFEEDER_EATS_A..value.name );
				end
	
				PetFeeder_Var.Searching = false;
				return;
			 end
		end
	end
	
	-- No Food Could be Found
	PFDebugMessage("PF", "No food could be found to feed the pet", "debug" );
	if ( PeetFeederPlayer_Config.Alert ) then
		DEFAULT_CHAT_FRAME:AddMessage(PetFeeder_PetName..PETFEEDER_NO_FOOD);
	end
	
	PetFeeder_Var.Searching = false;	
end

function PetFeeder_ApproveFoodItem( lname )
	
	local _,_,name = string.find(lname, "(.*) %(%d+%)");
	if ( not name ) then
		name = lname;
	end
		
	StaticPopupDialogs["PetFeeder_ApproveFoodItem"] = {
		text = PETFEEDER_APPROVE_FOOD..": "..name,
		button1 = PETFEEDER_APPROVE,
		button2 = PETFEEDER_DISLIKE,
		whileDead = 1,
		OnAccept = function()
			PetFeeder_AddFood(name, PETFEEDER_FL_APPROVED);
			PetFeeder_Var.DialogShowing = false;
			PetFeeder_Var.Searching = false;
			if ( PetFeeder_Var.Feed == true ) then
				PetFeeder_Feed();
			end
			PetFeeder_Update_Frames();
		end,
		OnCancel = function()
			PetFeeder_AddFood(name, PETFEEDER_FL_DISLIKED);
			PetFeeder_Var.DialogShowing = false;
			PetFeeder_Var.Searching = false;
			if ( PetFeeder_Var.Feed == true ) then
				-- PetFeeder_Feed();
			end
			PetFeeder_Update_Frames();
		end,
		
		timeout = 0
	};
	PetFeeder_Var.DialogShowing = true;
	StaticPopup_Show("PetFeeder_ApproveFoodItem");
	
end

------------------
-- Threshold Dropdown
------------------
local function PetFeederFrameDropDown_Initialize()
	local info;
	for i = 1, getn(PETFEEDER_LEVELS_DROPDOWN), 1 do
		info = { };
		info.text = PETFEEDER_LEVELS_DROPDOWN[i].name;
		info.func = PetFeederFrameDropDownButton_OnClick;
		UIDropDownMenu_AddButton(info);
	end
end

function PetFeederFrameDropDown_OnLoad()
	UIDropDownMenu_Initialize(PetFeederFrameDropDown, PetFeederFrameDropDown_Initialize);
	UIDropDownMenu_SetWidth(80);
	UIDropDownMenu_SetButtonWidth(24);
	UIDropDownMenu_JustifyText("LEFT", PetFeederFrameDropDown)
end

function PetFeederFrameDropDownButton_OnClick()
	UIDropDownMenu_SetSelectedID(PetFeederFrameDropDown, this:GetID());
	PeetFeederPlayer_Config.Level = UIDropDownMenu_GetSelectedID(PetFeederFrameDropDown);	
end


-- Sorting Algorithms


function PetFeeder_SortFoods()

	if ( PetFeederPlayer_Foods[PetFeeder_PetName] == nil ) then
		PetFeeder_ClearFoods();
		return;
	end
	
	PetFeeder_UpdateQuantities();
	
	table.sort( PetFeederPlayer_Foods[PetFeeder_PetName], function(a,b) return a.name > b.name end );
	
		
	if ( PeetFeederPlayer_Config.SortOption == 1 ) then
		return;
	end;

	
	table.sort( PetFeederPlayer_Foods[PetFeeder_PetName], PetFeeder_CompareItem );

end
--[[
=============================================================================
The primary sort compare method.  We may call up to to actual sorting methods
depending on the results.  The sorting methods return 1, 0, -1 while
this method results true|false;
returns: 
=============================================================================
]]

function PetFeeder_CompareItem(a,b)
	
  local result = 0;

  if ( not PeetFeederPlayer_Config.SortOption ) then  PeetFeederPlayer_Config.SortOption = 1; end
  if ( PETFEEDER_SORTOPTION_DROPDOWN[PeetFeederPlayer_Config.SortOption].func ) then
  	result = PETFEEDER_SORTOPTION_DROPDOWN[PeetFeederPlayer_Config.SortOption].func(a,b);
  end

  if ( not PeetFeederPlayer_Config.SortOption2 ) then  PeetFeederPlayer_Config.SortOption2 = 3; end
   
  if (PETFEEDER_SORTOPTION_DROPDOWN[PeetFeederPlayer_Config.SortOption2].func and result == 0) then
  	result = PETFEEDER_SORTOPTION_DROPDOWN[PeetFeederPlayer_Config.SortOption2].func(a,b);
  end
  

  return result > 0;

end

function sortQuantityHighLow(a,b)
	--table.sort( PetFeeder_Foods[PetFeeder_PetName], function(a,b) return a.quantity > b.quantity end );
	if ( a and b ) then
		return a.quantity - b.quantity;
	else
		return 0;
	end
end
function sortQuantityLowHigh(a,b)
	--table.sort( PetFeeder_Foods[PetFeeder_PetName], function(a,b) return a.quantity < b.quantity end );
	if ( a and b ) then
		return b.quantity - a.quantity;
	else
		return 0;
	end
end
function sortQualityHighLow(a,b)
	--table.sort( PetFeeder_Foods[PetFeeder_PetName], function(a,b) return a.quality > b.quality end );
	if ( a and b ) then
		return a.quality - b.quality;
	else
		return 0;
	end
end
function sortQualityLowHigh(a,b)
	--table.sort( PetFeeder_Foods[PetFeeder_PetName], function(a,b) return a.quality < b.quality end );
	if ( a and b ) then
		return b.quality - a.quality;
	else
		return 0;
	end
end
function sortAlphabeticallyHighLow(a,b)
	--table.sort( PetFeeder_Foods[PetFeeder_PetName], function(a,b) return a.name > b.name end );
	if ( a and b ) then
			if ( a.name > b.name ) then
			return 1;
		elseif ( a.name == b.name ) then
			return 0;
		end
		return -1;
	end
	return 0;
end

function sortAlphabeticallyLowHigh(a,b)
	--table.sort( PetFeeder_Foods[PetFeeder_PetName], function(a,b) return a.name < b.name end );
	if ( a and b ) then
		if ( a.name < b.name ) then
			return 1;
		elseif ( a.name == b.name ) then
			return 0;
		end
		return -1;
	end
	return 0;
end


----------------------------
--PetFeeder Checkbuttons--
-----------------------------
function PetFeeder_PF_Enabled_CheckBt_Update(whatValue)
	PeetFeederPlayer_Config.Enabled = whatValue;
	PetFeeder_Update_Frames();
end

function PetFeeder_PF_AutoFindFood_CheckBt_Update(whatValue)
	PeetFeederPlayer_Config.AutoFindFood = whatValue;
	if ( PeetFeederPlayer_Config.AutoFindFood ) then
		PetFeeder_PopulateFoods();
	end
	PetFeeder_Update_Frames();
	
end

function PetFeeder_PF_Alerts_CheckBt_Update(whatValue)
	PeetFeederPlayer_Config.Alert = whatValue;
end

function PetFeeder_PF_SkipBuffFoods_CheckBt_Update(whatValue)
	PeetFeederPlayer_Config.skipBuffFoods = whatValue;
	if ( PeetFeederPlayer_Config.AutoFindFood ) then
		PetFeeder_PopulateFoods();
	end
	PetFeeder_Update_Frames();
end

function PetFeeder_PF_FeedOnlyApproved_CheckBt_Update(whatValue)
	PeetFeederPlayer_Config.FeedOnlyApproved = whatValue;
end

function PetFeeder_PF_RequireApproval_CheckBt_Update(whatValue)
	PeetFeederPlayer_Config.RequireApproval = whatValue;
end

--[[
=============================================================================
 Food Management Routines 
=============================================================================
]]

--[[
=============================================================================
Adds a food item to the Food list or the Bad food list.  Checks to ensure
that the food isn't already in the table and checks to ensure we aren't
re-adding food the Pet doesn't like.
params:
 - PetFeederPlayer_Foods[PetFeeder_PetName]: list to add the item to.
 - value   : table object for the item
=============================================================================
]]

function PetFeeder_AddFood( value , foodlikedstate )
	
	-- DEFAULT_CHAT_FRAME:AddMessage(value); 
	-- Make sure PetFeeder_PetName has real pet name
	PetFeeder_PetName = UnitName( "pet" );
	if ( not PetFeeder_PetName ) then
		return;
	end
	if ( not PetFeederPlayer_Foods[PetFeeder_PetName] ) then
		PetFeederPlayer_Foods[PetFeeder_PetName] = {};
	end
	
	local changeVal = true;
	if ( not foodlikedstate ) then
		foodlikedstate = PETFEEDER_FL_UNKNOWN;
		changeVal = false;
	end
	
	if ( type(value) ~= "table" ) then
		local _,_,name = string.find(value, "(.*) %(%d+%)");
		if ( not name ) then
			name = value;
		end
		if ( name ) then 
		  	for i=1, table.getn( PetFeederPlayer_Foods[PetFeeder_PetName] ) do
		    		if ( PetFeederPlayer_Foods[PetFeeder_PetName][i].name == name ) then
		    			value = PetFeederPlayer_Foods[PetFeeder_PetName][i];
		    		end
		  	end
		end	

	end
	
	if ( not value ) then
		return;
	end
	
	-- don't add if already in the table
	-- just change foodlikedstate state
	
	PFDebugMessage("PF", "Pet Name: "..PetFeeder_PetName, "debug");
	if ( not PetFeederPlayer_Foods[PetFeeder_PetName] ) then
		PFDebugMessage("PF", "No table for pet", "debug");
	end
	PFDebugMessage("PF", "Food State Name: "..foodlikedstate, "debug");
	for i=1, table.getn( PetFeederPlayer_Foods[PetFeeder_PetName] ) do
	  if ( PetFeederPlayer_Foods[PetFeeder_PetName][i].name == value.name ) then
	  	if ( changeVal or not PetFeederPlayer_Foods[PetFeeder_PetName][i].foodlikedstate ) then
	  		PFDebugMessage("PF", "Change Food State Name: "..foodlikedstate, "debug");
			PetFeederPlayer_Foods[PetFeeder_PetName][i].foodlikedstate = foodlikedstate;
		end
		return true;
	  end
	end
	
	
	value.foodlikedstate = foodlikedstate;
	
	table.insert(PetFeederPlayer_Foods[PetFeeder_PetName], value );

	return true;
end

function PetFeeder_ClearFoods( foodlikedstate )
	if not ( PetFeeder_HasPet() ) then
		UIErrorsFrame:AddMessage(PETFEEDER_NEED_PET, 0.8, 0, 0, 1.0, UIERRORS_HOLD_TIME);
		return;
	end
	
	PetFeeder_PetName = UnitName( "pet" );
	if ( not foodlikedstate ) then
		PetFeederPlayer_Foods[PetFeeder_PetName] = {};
	else
		for i=table.getn( PetFeederPlayer_Foods[PetFeeder_PetName] ), 1,-1  do
		  	if ( PetFeederPlayer_Foods[PetFeeder_PetName][i].foodlikedstate == foodlikedstate ) then
		  		table.remove(PetFeederPlayer_Foods[PetFeeder_PetName],i);
			--	PetFeeder_AddFood(PetFeederPlayer_Foods[PetFeeder_PetName][i], PETFEEDER_FL_UNKNOWN);
		 	end
		end
	end
	PetFeeder_Update_Frames();
end

function PetFeeder_FoodsFrame_UpdateExt( foodlikedstate )

	local frameCoWord;
	
	if ( not foodlikedstate ) then
		foodlikedstate = PETFEEDER_FL_UNKNOWN;
	end
	
	if ( foodlikedstate == PETFEEDER_FL_UNKNOWN ) then
		frameCoWord = "";
	elseif ( foodlikedstate == PETFEEDER_FL_APPROVED ) then
		frameCoWord = "Approved";
	else
		frameCoWord = "Unliked";
	end
	
	local iItem;

	local numEntries = 0;
	for index, value in PetFeederPlayer_Foods[PetFeeder_PetName] do
		if ( value.foodlikedstate == foodlikedstate ) then
			numEntries = numEntries + 1;
		end
	end

	--DEFAULT_CHAT_FRAME:AddMessage(frameCoWord.." numEntries "..numEntries); 
	
	local scrollFrame = getglobal("PetFeeder_"..frameCoWord.."FoodsFrameListScrollFrame");
	FauxScrollFrame_Update(scrollFrame, numEntries, PETFEEDER_ITEMS_SHOWN, PETFEEDER_ITEM_HEIGHT, nil, nil, nil, nil, nil, PETFEEDER_ITEM_HEIGHT);
	
	local scrollFrameOffset = FauxScrollFrame_GetOffset(scrollFrame);
	--DEFAULT_CHAT_FRAME:AddMessage(" scrollFrameOffset "..scrollFrameOffset); 
	local realItem = 1;
	local realIndex = 0;
	local usedButtons = 1;
	while  usedButtons <= PETFEEDER_ITEMS_SHOWN do
		realIndex = realIndex + 1
		local buttonItem = getglobal("PetFeeder_"..frameCoWord.."FoodsFrameItem"..usedButtons);
		if ( usedButtons > numEntries ) then
		  	buttonItem:Hide();
		  	usedButtons = usedButtons + 1;
		else
			
			iconTexture = getglobal("PetFeeder_"..frameCoWord.."FoodsFrameItem"..usedButtons.."ItemIconTexture");
			if ( PetFeederPlayer_Foods[PetFeeder_PetName][realIndex] ) then
				local value = PetFeederPlayer_Foods[PetFeeder_PetName][realIndex];
			
				if ( value.foodlikedstate == foodlikedstate ) then
					if ( realItem > scrollFrameOffset ) then
						--DEFAULT_CHAT_FRAME:AddMessage(" realIndex "..realIndex.." Name "..value.name); 
						if ( value.texture ) then
							iconTexture:SetTexture( value.texture );
						end
			 			local name = value.name.." ("..value.quantity..")";
			 			if ( value.quality ) then
			 				name = name.." - quality: "..value.quality;
			 			end
			
						buttonItem:SetText(name);
						buttonItem:Show();
						usedButtons = usedButtons + 1
					end
					realItem = realItem + 1;
								
				end
			else
				usedButtons = usedButtons + 1;
			end
		end
	end	
end

--[[
=============================================================================
This method gets called by the ChatMsg callback functions when it detects
that a pet doesn't like the food we fed it.
- Bad food will be the first one readily available
- Only gets called when a bad food item is detected
- If the user manually feeds, the timer which is set when WE feed, will be off
  by more than 2 clicks.  We don't want to remove any foods from our list because
  we don't know what the pet didn't like.
  params:
   - t: nil
=============================================================================
]]
function PetFeeder_RemoveBadFood()
	
	PetFeeder_UpdateQuantities();
	
	PFDebugMessage("PF", "Bad food detected "..PetFeeder_Var.LastItemAttempted.name, "debug");
	
	if ( (GetTime() - PetFeeder_Var.feedStartTime) >= 2 ) then
		PFDebugMessage("PF", "Not removing bad food as feed wasn't initiated by PF", "debug");
		return;
	end
	PetFeeder_Var.feedStartTime = 0;
	for index, value in PetFeederPlayer_Foods[PetFeeder_PetName] do
		if ( value.name == PetFeeder_Var.LastItemAttempted.name ) then			
			if ( PeetFeederPlayer_Config.Alert ) then
				DEFAULT_CHAT_FRAME:AddMessage(PETFEEDER_REMOVE_FOOD..value.name);
			end
			
			PetFeeder_AddFood( value , PETFEEDER_FL_DISLIKED );
			break;
		end
	end
	
	PetFeeder_Update_Frames();	
	
end

--[[
=============================================================================
Gets called when we want to drop an item onto the list.  Identifies which
item in bag,slot is being dropped, gets into about that item and then attempts
to add it to the list.
params:
 - PetFeederPlayer_Foods[PetFeeder_PetName]: the name of the list to add the item to.
=============================================================================
]]

function PetFeeder_Update_Frames()
	if ( not UnitName( "pet" ) ) then
		return;
	end
	PetFeeder_SortFoods();
	PetFeeder_UnlikedFoodsFrame_Update();
	PetFeeder_FoodsFrame_Update();
	PetFeeder_ApprovedFoodsFrame_Update();
	
end


function PetFeeder_DroptheItem( accept , foodlikedstate )
	
	if CursorHasItem() and PetFeeder_PickedupItem then
		
		local value = PetFeeder_GetItemObject( PetFeeder_PickedupItem.bag,PetFeeder_PickedupItem.slot );
		if ( value ) then
			--if ( accept == "any" ) then
			-- if ( PetFeederFrame_IsFood( value ) or accept == "any" ) then

				PetFeeder_AddFood( value, foodlikedstate );
					
			--end
		end
		ResetCursor();
		PetFeeder_TakeItemOffCursor(PetFeeder_PickedupItem.bag,PetFeeder_PickedupItem.slot);			
	end  -- if has item and we know where it came from

	PetFeeder_PickedupItem = nil;	

end


--[[
=============================================================================
Returns the item name;
params:
 - bag: the bag number of the item
 - slot: the slot number of the item
=============================================================================
]]
function PetFeeder_GetItemName(bag, slot)
  --local name,totalCount,quality,texture,linktext = PetFeeder_GetItemInfo(bag,slot);   
  local value = PetFeeder_GetItemObject( m, n );
  
  if ( value ) then
  	return value.name;
  end
  
  return nil;
end

--[[
=============================================================================
Looks through all bags/slots to find the item and adds up the total quantity
for it.
params:
 - itemName: name of the item
=============================================================================
]]
function PetFeeder_GetItemCount(itemName)
	--DEFAULT_CHAT_FRAME:AddMessage("itemName="..itemName);  
	local totalItemCount = 0;
	local itemQuality = 0;
  -- calc total number of items
  	for m = 0, 4 do
		for n = 1, 20 do
		      	local curLinkText = GetContainerItemLink(m, n);
		      	local curname;
			if curLinkText then
				_, _, id,curname = string.find(curLinkText, "^.*:(%d+):%d+:%d+:%d+.*%[(.*)%].*$");
	
			end
			if ( curname == itemName ) then
				
				texture, itemCount, locked, quality, readable = GetContainerItemInfo(m,n);
			      	itemQuality = quality;
			      	if ( itemCount ) then
			      		totalItemCount = totalItemCount + itemCount;
			      	end
	      		end
    		end
  	end
  	
  	if ( PetFeeder_QuestItems[itemName] ) then
  		--DEFAULT_CHAT_FRAME:AddMessage("Quest itemName="..itemName); 
  		local sum = totalItemCount - PetFeeder_QuestItems[itemName];
  		--DEFAULT_CHAT_FRAME:AddMessage("sum="..sum); 
  		totalItemCount = max(sum,0);
  	end
  	return totalItemCount, itemQuality;
  
end

function PetFeeder_GetItemObject(bag,slot)
  
  local value = { };
  local linktext = GetContainerItemLink(bag, slot);
  local itemTexture, itemCount, locked, itemQuality, readable = GetContainerItemInfo(bag,slot);
  local itemColor, itemID, itemName;
  local restores, overTime;
  
  value.quality = 61;

	-- no sense processing if there isn't an item
  if ( not linktext ) then
  	return nil;
  end
  
	for itemColor, itemID, itemName in string.gfind(linktext, "|c(%x+)|Hitem:(%d+:%d+:%d+:%d+)|h%[(.-)%]|h|r") do
		if ( itemColor and itemID and itemName ~= "" ) then
			  value.name = itemName;
			  value.color=itemColor;
			  value.id=itemID;
			--DEFAULT_CHAT_FRAME:AddMessage("color="..itemColor.."  itemID="..itemID.."  name="..itemName.." texture="..itemTexture);  	
		end
	end
	
	if ( not value.id ) then
		return nil;
	end
	
	local _, _, _, _, itemType, itemSub = GetItemInfo("item:"..value.id)
	--DEFAULT_CHAT_FRAME:AddMessage(value.name.." - "..itemType.." - "..itemSub)
	value.type = itemSub;
	value.texture = itemTexture;
  	value.link = linktext;

	-- Look for 'Use: Restores XXXX health over YY seconds'
	-- Regex looks for 'Use:', a space, 1 or more characters, a space, 1 or more digits
	-- Return the digits as our version of quality
	if ( value ) then
		PetFeederTooltip:ClearLines();
		PetFeederTooltip:SetHyperlink( "item:"..value.id );
		--PFDebugMessage("PF", "Parse ITEM="..value.name, "info");
	end
	local i;

	if ( PetFeederTooltipTextLeft2:GetText() ) then
		--PFDebugMessage("PF", "TooltipTextLeft2="..PetFeederTooltipTextLeft2:GetText(), "info");
		--for restores in string.gfind(PetFeederTooltipTextLeft2:GetText(), PETFEEDER_RESTORES.." (%d+)") do
		for restores in string.gfind(PetFeederTooltipTextLeft2:GetText(), ITEM_SPELL_TRIGGER_ONUSE.." %w+ (%d+)") do		
			if ( restores ~= "" ) then
				  value.quality = tonumber(restores);
			end
		end
	end		
	if ( PetFeederTooltipTextLeft3:GetText() ) then
		--PFDebugMessage("PF", "TooltipTextLeft3="..PetFeederTooltipTextLeft3:GetText(), "info");
		--for restores in string.gfind(PetFeederTooltipTextLeft3:GetText(), PETFEEDER_RESTORES.." (%d+)") do
		for restores in string.gfind(PetFeederTooltipTextLeft3:GetText(), ITEM_SPELL_TRIGGER_ONUSE.." %w+ (%d+)") do		
			if ( restores ~= "" ) then
				  value.quality = tonumber(restores);
			end
		end
	end
	

	value.attributeBuffs = false;
	if ( PetFeederTooltipTextLeft2:GetText() ) then
		for i = 1, getn(PETFEEDER_ITEM_ATTRIBUTE_BUFFS), 1 do
			for restores in string.gfind(PetFeederTooltipTextLeft2:GetText(), PETFEEDER_ITEM_ATTRIBUTE_BUFFS[i].search ) do		
				if ( restores ~= "" ) then
					  value.attributeBuffs = true;
					  break;
				end
			end
		end
	end

	if ( PetFeederTooltipTextLeft3:GetText() and (value.attributeBuffs == false)) then
		for i = 1, getn(PETFEEDER_ITEM_ATTRIBUTE_BUFFS), 1 do
			for restores in string.gfind(PetFeederTooltipTextLeft3:GetText(), PETFEEDER_ITEM_ATTRIBUTE_BUFFS[i].search ) do		
				if ( restores ~= "" ) then
					  value.attributeBuffs = true;
					  break;
				end
			end
		end
	end


  local totalItemCount = PetFeeder_GetItemCount( name );
  value.quantity = totalItemCount;

--  if ( value.name and value.name == "Mystery Meat" ) then 
--    DEFAULT_CHAT_FRAME:AddMessage("object::color="..value.color.."  itemID="..value.id.."  name="..value.name);
--	  DEFAULT_CHAT_FRAME:AddMessage("object::texture="..value.texture.."  quality="..value.quality.."  link="..value.link);
--	  DEFAULT_CHAT_FRAME:AddMessage("object::quantity="..value.quantity);
--  else
--    DEFAULT_CHAT_FRAME:AddMessage("null name in bag="..bag.." slot="..slot);
--  end
  
  if ( value.name ) then
  	return value;
  end
 
  return nil;
  
end

function PetFeeder_TakeItemOffCursor(srcBag, srcSlot)
	if srcBag == -1 then
		PickupInventoryItem(srcSlot);
	else
		PickupContainerItem(srcBag, srcSlot);
	end
end

--[[
=============================================================================
Algorithm to auto-populate foods into our food list.  Uses pattern matching 
to find foods based on popular words associated with foods in the game.
Alt Algorithm: uses texture to determine whether an item is a food item or not.
=============================================================================
]]
function PetFeeder_PopulateFoods()
	--DEFAULT_CHAT_FRAME:AddMessage("PetFeeder_PopulateFoods");
	if not ( PetFeeder_HasPet() ) then
		return;
	end

  -- Walk through each inventory item
	  for m = 0, 4 do
		for n = 1, 20 do
	      
		  local value = PetFeeder_GetItemObject( m,n );
		  if ( value ) then
			  if ( PetFeederFrame_IsFood( value ) 
			  	and ( (PeetFeederPlayer_Config.skipBuffFoods and value.attributeBuffs == false) 
			  				or (not PeetFeederPlayer_Config.skipBuffFoods)) )
			  then
			  	-- DEFAULT_CHAT_FRAME:AddMessage(value.name);
				PetFeeder_AddFood( value );
			  end
		  end
		  
		end
	  end
  
end

--[[
=============================================================================
Determine whether the item is food or not.  We try three things to figure this out
1. If the texture contains the word _Food_ or _Misc_Bowl_ we assume its a food item
2. If the texture contains certain woods (ie: Weapon, Arrow) we eliminate it as a choice
3. Everything else goes through our food filter list to see if they contain
   keywords for food items.
Even if we mis-identify something as a food item, it will get eliminated from
the pet's diet list as soon as it tries to eat it and the Add_Food routine
will eliminate it for us.
=============================================================================
]]
function PetFeederFrame_IsFood( item )
	
	local isGood = false;
	--DEFAULT_CHAT_FRAME:AddMessage("texture="..item.texture.." item="..item.name);
	
	if ( item.type == PETFEEDER_CONSUMABLE or item.type == PETFEEDER_TRADEGOODS or PF_FOM_IsFood(item) ) then
		local foodTextures = { "_Food_", "_Misc_", "_Fish_","_Mushroom_" };
		local i
		for  i=1, table.getn( foodTextures ) do
			if ( string.find( item.texture, foodTextures[i] ) ) then
				isGood = true;
				break;
			end
		end
	else
		return false;
	end
	
	local notFoodTextures = { "_Gem_", "_ArmorKit_", "_Flower_","_LeatherScrap_","_Bandage_","_MonsterScales_","_Herb_" };
	local i
	for  i=1, table.getn( notFoodTextures ) do
		if ( string.find( item.texture, notFoodTextures[i] ) ) then
			return false;
		end
	end
	return isGood;
	
end



--=============================================================================
-- Return information about the pattern
--
-- pattern	pattern to find
local function BuffInformation( pattern )
	if pattern then
		return string.find( PetFeederTooltipTextLeft2:GetText(), pattern );
	else
		return 1;
	end
end
--=============================================================================


-- Called when you click on a Tab
function PetFeederTab_OnClick()
	if ( this:GetName() == "PetFeederFrameTab1" ) then
		togglePetFeeder("PetFeeder_FoodsFrame");
	elseif ( this:GetName() == "PetFeederFrameTab2" ) then
		togglePetFeeder("PetFeeder_ApprovedFoodsFrame");
	elseif ( this:GetName() == "PetFeederFrameTab3" ) then
		togglePetFeeder("PetFeeder_UnlikedFoodsFrame");	
	end
	PlaySound("igCharacterInfoTab");
end

--[[
=============================================================================
Hooked function that gets called when a user picks up an item
=============================================================================
]]
local PetFeeder_Save_PickupContainerItem = PickupContainerItem;
PickupContainerItem = function (bag,slot)
	PetFeeder_PickedupItem = { };
	PetFeeder_PickedupItem.bag = bag;
	PetFeeder_PickedupItem.slot = slot;
	
	return PetFeeder_Save_PickupContainerItem(bag,slot);
end

--[[
=============================================================================
Hooked function that gets called when a user picks up an item
=============================================================================
]]
local PetFeeder_Save_PickupInventoryItem = PickupInventoryItem;
PickupInventoryItem = function (slot)
	PetFeeder_PickedupItem = { };
	PetFeeder_PickedupItem.bag = -1;
	PetFeeder_PickedupItem.slot = slot;
	return PetFeeder_Save_PickupInventoryItem(slot);
end

function PetFeederItemButton_OnEnter(lname)
	
	local _,_,name = string.find(lname, "(.*) %(%d+%)");
	if ( not name ) then
		name = lname;
	end
	
	local link = nil;
	
	for index, value in PetFeederPlayer_Foods[PetFeeder_PetName] do
		if ( value.name == name ) then
			link = "item:"..value.id;
		end
	end
	
	if( link ) then
		GameTooltip:SetOwner(this,"ANCHOR_RIGHT");
		GameTooltip:SetHyperlink(link);
		GameTooltip:Show();
	end
end

function PetFeederItemButton_OnLeave()
	if( PetFeederFrame.TooltipButton ) then
		PetFeederFrame.TooltipButton = nil;
		HideUIPanel(PetFeederDisplayTooltip);
	end
end

function FixOldInventoryData()
	local itemObject;
	
	for index, value in PetFeederPlayer_Foods[PetFeeder_PetName] do
		for m = 0, 4 do
			for n = 1, 18 do
				itemObject = PetFeeder_GetItemObject(m,n);
				if ( itemObject and itemObject.name == value.name ) then
					value = itemObject;
					break;
				end
			end
			if ( itemObject and itemObject.name == value.name ) then
				break;
			end
		end		
	end
end


--[[
=============================================================================
  Test whether a pet is present or not.
=============================================================================
]]
function PetFeeder_HasPet()
	if (  not UnitExists("pet") ) then
		return false;
	elseif ( UnitName("pet") == "Unknown Entity"  or UnitName("pet") == "Unknown" ) then
		return false;
	end
	
	return true;
end



function PetFeeder_IdFromLink(link)
	if (link == nil) then return nil; end
	local _, _, itemID  = string.find(link, "(%d+):%d+:%d+:%d+");
--	DEFAULT_CHAT_FRAME:AddMessage("link ".. link .." to "..itemID);
	
	if (tonumber(itemID)) then
		return tonumber(itemID);
	else
		return nil;
	end
end