-- Gatherer
-- Written by Chandora
GATHERER_VERSION="1.9.11";
--
-- Look, seriously a full half of this code is from MapNotes.
-- The only reason I pinched it and put it in here is I couldn't
-- work out how to extend MapNotes to do what I wanted it to do
-- without actually editing the NapNotes files.
-- 
-- Full credit to the MapNotes guys!
-- 
-- Added by Jet:
--    * localization for french version (Gatherer_FindOreType, Gatherer_FindTreasureType and Gatherer_ReadBuff modified).
--      localized text Variables are in the localization.lua file (added for load in Gatherer.xml), french and english (default).
--      Gather_IconSet modified to use variables, added Wildvine herb type (iconic style icon is from MapNotesGathering Addon 
--        and treasure types (including giant clam) in GatherIcons.lua
--    * GatherConfig current values displayed on load (Gatherer_OnEvent) and
--      on /gather (Gatherer_Command) (added useMinimapTexT to GatherConfig to translate useMinimap).
--    * crude filtering of displayed notes (added filter to GatherConfig), 
--      /gather filter command added in Gatherer_Command and filter check in Gatherer_OnUpdate.
--    * added Clam and Cask type to check in Gatherer_FindTreasureType
--    * zone names at the end of GatherRegionData
--
-- Added by Lokibar:
--    * localization for german version (Gatherer_FindOreType, Gatherer_FindTreasureType and Gatherer_ReadBuff modified).
--      localized text Variables are in the localization.lua file (added for load in Gatherer.xml).

GATHERNOTE_UPDATE_INTERVAL = 0.25;
GATHERNOTE_CHECK_INTERVAL = 5.0;
GATHERER_MAXNUMNOTES = 25;
GATHERER_LOADED = false;

GatherItems = { };
GatherConfig = { useMinimap = true, maxDist = 0, number = 10, useMinimapText = "on", iconSet = "shaded", miniIconDist = 40, filter="all" };
GatherZoneData = { };

--The following (and more inside) ripped mercilessly from MapNotes:
GatherRegionData = { };
GatherRegionData.cityZoom    = { };
GatherRegionData.cityZoom[0] = 1.565;
GatherRegionData.cityZoom[1] = 1.687;
GatherRegionData.cityZoom[2] = 1.882;
GatherRegionData.cityZoom[3] = 2.210;
GatherRegionData.cityZoom[4] = 2.575;
GatherRegionData.cityZoom[5] = 2.651;
GatherRegionData[1]           = { scales={} };
GatherRegionData[1].scales[0] = { xscale = 11016.6, yscale = 7399.9 };
GatherRegionData[1].scales[1] = { xscale = 12897.3, yscale = 8638.1 };
GatherRegionData[1].scales[2] = { xscale = 15478.8, yscale = 10368.0 };
GatherRegionData[1].scales[3] = { xscale = 19321.8, yscale = 12992.7 };
GatherRegionData[1].scales[4] = { xscale = 25650.4, yscale = 17253.2 };
GatherRegionData[1].scales[5] = { xscale = 38787.7, yscale = 26032.1 };
GatherRegionData[2]           = { scales={} };
GatherRegionData[2].scales[0] = { xscale = 10448.3, yscale = 7072.7 };
GatherRegionData[2].scales[1] = { xscale = 12160.5, yscale = 8197.8 };
GatherRegionData[2].scales[2] = { xscale = 14703.1, yscale = 9825.0 };
GatherRegionData[2].scales[3] = { xscale = 18568.7, yscale = 12472.2 };
GatherRegionData[2].scales[4] = { xscale = 24390.3, yscale = 15628.5 };
GatherRegionData[2].scales[5] = { xscale = 37012.2, yscale = 25130.6 };
-- table modified for french locale
if ( GetLocale() == "frFR" ) then
	GatherRegionData[1][0]  = { name = "Kalimdor" };
	GatherRegionData[1][1]  = { scale = 0.15670371525706, xoffset = 0.41757282062541, yoffset = 0.33126468682991, name = "Ashenvale" };
	GatherRegionData[1][2]  = { scale = 0.13779501505279, xoffset = 0.55282036918049, yoffset = 0.30400571307545, name = "Azshara" };
	GatherRegionData[1][3] = { scale = 0.10054401185671, xoffset = 0.44927594451520, yoffset = 0.76494573629405, name = "Un'Goro Crater" };
	GatherRegionData[1][4]  = { scale = 0.17799008894522, xoffset = 0.38383175154516, yoffset = 0.18206216123156, name = "Darkshore" };
	GatherRegionData[1][5]  = { scale = 0.02876626176374, xoffset = 0.38392150175204, yoffset = 0.10441296545475, name = "Darnassus" };
	GatherRegionData[1][6]  = { scale = 0.12219839120669, xoffset = 0.34873187115693, yoffset = 0.50331046935371, name = "Desolace" };
	GatherRegionData[1][7]  = { scale = 0.14368294970080, xoffset = 0.51709782709100, yoffset = 0.44802818134926, name = "Durotar" };
	GatherRegionData[1][8]  = { scale = 0.15625084006464, xoffset = 0.41995800144849, yoffset = 0.23097545880609, name = "Felwood" };
	GatherRegionData[1][9]  = { scale = 0.18885970960818, xoffset = 0.31589651244686, yoffset = 0.61820581746798, name = "Feralas" };
	GatherRegionData[1][10] = { scale = 0.27539211944292, xoffset = 0.39249347333450, yoffset = 0.45601063260257, name = "The Barrens" };
	GatherRegionData[1][11]  = { scale = 0.14266384095509, xoffset = 0.49026338351379, yoffset = 0.60461876174686, name = "Dustwallow Marsh" };
	GatherRegionData[1][12] = { scale = 0.11956582877920, xoffset = 0.47554411191734, yoffset = 0.68342356389650, name = "Thousand Needles" };
	GatherRegionData[1][13] = { scale = 0.13272833611061, xoffset = 0.37556627748617, yoffset = 0.40285135292988, name = "Stonetalon Mountains" };
	GatherRegionData[1][14] = { scale = 0.06292695969921, xoffset = 0.50130287793373, yoffset = 0.17560823085517, name = "Moonglade" };
	GatherRegionData[1][15] = { scale = 0.13960673216274, xoffset = 0.40811854919226, yoffset = 0.53286226907346, name = "Mulgore" };
	GatherRegionData[1][16] = { scale = 0.03811449638057, xoffset = 0.56378554142668, yoffset = 0.42905218646258, name = "Orgrimmar" };
	GatherRegionData[1][17] = { scale = 0.18885969712845, xoffset = 0.33763582469211, yoffset = 0.75815224951929, name = "Silithus" };
	GatherRegionData[1][18] = { scale = 0.18750104661175, xoffset = 0.46971301480866, yoffset = 0.76120931364891, name = "Tanaris" };
	GatherRegionData[1][19] = { scale = 0.13836131003639, xoffset = 0.36011098024729, yoffset = 0.03948322979210, name = "Teldrassil" };
	GatherRegionData[1][20] = { scale = 0.02836291430658, xoffset = 0.44972878210917, yoffset = 0.55638479002362, name = "Thunder Bluff" };
	GatherRegionData[1][21] = { scale = 0.19293573573141, xoffset = 0.47237382938446, yoffset = 0.17390990272233, name = "Winterspring" };
	GatherRegionData[2][0]  = { name = "Azeroth" };
	GatherRegionData[2][1]  = { scale = 0.10227310921644, xoffset = 0.47916793249546, yoffset = 0.32386170078419, name = "Arathi Highlands" };
	GatherRegionData[2][2]  = { scale = 0.07066771883566, xoffset = 0.51361415033147, yoffset = 0.56915717993261, name = "Badlands" };
	GatherRegionData[2][3]  = { scale = 0.09517074521836, xoffset = 0.48982154167011, yoffset = 0.76846519986510, name = "Blasted Lands" };
	GatherRegionData[2][4] = { scale = 0.03819701270887, xoffset = 0.41531450060561, yoffset = 0.67097280492581, name = "Stormwind City" };
	GatherRegionData[2][5]  = { scale = 0.07102298961531, xoffset = 0.47822105868635, yoffset = 0.73863555048516, name = "Deadwind Pass" };
	GatherRegionData[2][6] = { scale = 0.09090931690055, xoffset = 0.42424361247460, yoffset = 0.30113436864162, name = "Hillsbrad Foothills" };
	GatherRegionData[2][7]  = { scale = 0.13991525534426, xoffset = 0.40335096278072, yoffset = 0.48339696712179, name = "Dun Morogh" };
	GatherRegionData[2][8]  = { scale = 0.07670475476181, xoffset = 0.43087243362495, yoffset = 0.73224350550454, name = "Duskwood" };
	GatherRegionData[2][9] = { scale = 0.09860350595046, xoffset = 0.41092682316676, yoffset = 0.65651531970162, name = "Elwynn Forest" };
	GatherRegionData[2][10] = { scale = 0.11931848806212, xoffset = 0.35653502290090, yoffset = 0.24715695496522, name = "Silverpine Forest" };
	GatherRegionData[2][11] = { scale = 0.06338794005823, xoffset = 0.46372051266487, yoffset = 0.57812379382509, name = "Searing Gorge" };
	GatherRegionData[2][12] = { scale = 0.02248317426784, xoffset = 0.47481923366335, yoffset = 0.51289242617182, name = "Ironforge" };
	GatherRegionData[2][13] = { scale = 0.10937523495111, xoffset = 0.49929119700867, yoffset = 0.25567971676068, name = "The Hinterlands" };
	GatherRegionData[2][14] = { scale = 0.07839152145224, xoffset = 0.51118749188138, yoffset = 0.50940913489577, name = "Loch Modan" };
	GatherRegionData[2][15] = { scale = 0.06516347991404, xoffset = 0.51769795272070, yoffset = 0.72815974701615, name = "Swamp of Sorrows" };
	GatherRegionData[2][16]  = { scale = 0.07954563533736, xoffset = 0.43229874660542, yoffset = 0.25425926375262, name = "Alterac Mountains" };
	GatherRegionData[2][17] = { scale = 0.06170112311456, xoffset = 0.49917278340928, yoffset = 0.68359285304999, name = "Redridge Mountains" };
	GatherRegionData[2][18]  = { scale = 0.10996723642661, xoffset = 0.51663255550387, yoffset = 0.15624753972085, name = "Eastern Plaguelands" };
	GatherRegionData[2][19] = { scale = 0.12215946583965, xoffset = 0.44270955019641, yoffset = 0.17471356786018, name = "Western Plaguelands" };
	GatherRegionData[2][20] = { scale = 0.12837403412087, xoffset = 0.36837217317549, yoffset = 0.15464954319582, name = "Tirisfal Glades" };
	GatherRegionData[2][21]  = { scale = 0.08321525646393, xoffset = 0.04621224670174, yoffset = 0.61780780524905, name = "Burning Steppes" };
	GatherRegionData[2][22] = { scale = 0.02727719546939, xoffset = 0.42973999245660, yoffset = 0.23815358517831, name = "Undercity" };
	GatherRegionData[2][23] = { scale = 0.18128603034401, xoffset = 0.39145470225916, yoffset = 0.79412224886668, name = "Stranglethorn Vale" };
	GatherRegionData[2][24] = { scale = 0.09943208435841, xoffset = 0.36884571674582, yoffset = 0.71874918595783, name = "Westfall" };
	GatherRegionData[2][25] = { scale = 0.11745423014662, xoffset = 0.46561438951659, yoffset = 0.40971063365152, name = "Wetlands" };
else
	GatherRegionData[1][0]  = { name = "Kalimdor" };
	GatherRegionData[1][1]  = { scale = 0.15670371525706, xoffset = 0.41757282062541, yoffset = 0.33126468682991, name = "Ashenvale" };
	GatherRegionData[1][2]  = { scale = 0.13779501505279, xoffset = 0.55282036918049, yoffset = 0.30400571307545, name = "Azshara" };
	GatherRegionData[1][3]  = { scale = 0.17799008894522, xoffset = 0.38383175154516, yoffset = 0.18206216123156, name = "Darkshore" };
	GatherRegionData[1][4]  = { scale = 0.02876626176374, xoffset = 0.38392150175204, yoffset = 0.10441296545475, name = "Darnassus" };
	GatherRegionData[1][5]  = { scale = 0.12219839120669, xoffset = 0.34873187115693, yoffset = 0.50331046935371, name = "Desolace" };
	GatherRegionData[1][6]  = { scale = 0.14368294970080, xoffset = 0.51709782709100, yoffset = 0.44802818134926, name = "Durotar" };
	GatherRegionData[1][7]  = { scale = 0.14266384095509, xoffset = 0.49026338351379, yoffset = 0.60461876174686, name = "Dustwallow Marsh" };
	GatherRegionData[1][8]  = { scale = 0.15625084006464, xoffset = 0.41995800144849, yoffset = 0.23097545880609, name = "Felwood" };
	GatherRegionData[1][9]  = { scale = 0.18885970960818, xoffset = 0.31589651244686, yoffset = 0.61820581746798, name = "Feralas" };
	GatherRegionData[1][10] = { scale = 0.06292695969921, xoffset = 0.50130287793373, yoffset = 0.17560823085517, name = "Moonglade" };
	GatherRegionData[1][11] = { scale = 0.13960673216274, xoffset = 0.40811854919226, yoffset = 0.53286226907346, name = "Mulgore" };
	GatherRegionData[1][12] = { scale = 0.03811449638057, xoffset = 0.56378554142668, yoffset = 0.42905218646258, name = "Orgrimmar" };
	GatherRegionData[1][13] = { scale = 0.18885969712845, xoffset = 0.33763582469211, yoffset = 0.75815224951929, name = "Silithus" };
	GatherRegionData[1][14] = { scale = 0.13272833611061, xoffset = 0.37556627748617, yoffset = 0.40285135292988, name = "Stonetalon Mountains" };
	GatherRegionData[1][15] = { scale = 0.18750104661175, xoffset = 0.46971301480866, yoffset = 0.76120931364891, name = "Tanaris" };
	GatherRegionData[1][16] = { scale = 0.13836131003639, xoffset = 0.36011098024729, yoffset = 0.03948322979210, name = "Teldrassil" };
	GatherRegionData[1][17] = { scale = 0.27539211944292, xoffset = 0.39249347333450, yoffset = 0.45601063260257, name = "The Barrens" };
	GatherRegionData[1][18] = { scale = 0.11956582877920, xoffset = 0.47554411191734, yoffset = 0.68342356389650, name = "Thousand Needles" };
	GatherRegionData[1][19] = { scale = 0.02836291430658, xoffset = 0.44972878210917, yoffset = 0.55638479002362, name = "Thunder Bluff" };
	GatherRegionData[1][20] = { scale = 0.10054401185671, xoffset = 0.44927594451520, yoffset = 0.76494573629405, name = "Un'Goro Crater" };
	GatherRegionData[1][21] = { scale = 0.19293573573141, xoffset = 0.47237382938446, yoffset = 0.17390990272233, name = "Winterspring" };
	GatherRegionData[2][0]  = { name = "Azeroth" };
	GatherRegionData[2][1]  = { scale = 0.07954563533736, xoffset = 0.43229874660542, yoffset = 0.25425926375262, name = "Alterac Mountains" };
	GatherRegionData[2][2]  = { scale = 0.10227310921644, xoffset = 0.47916793249546, yoffset = 0.32386170078419, name = "Arathi Highlands" };
	GatherRegionData[2][3]  = { scale = 0.07066771883566, xoffset = 0.51361415033147, yoffset = 0.56915717993261, name = "Badlands" };
	GatherRegionData[2][4]  = { scale = 0.09517074521836, xoffset = 0.48982154167011, yoffset = 0.76846519986510, name = "Blasted Lands" };
	GatherRegionData[2][5]  = { scale = 0.08321525646393, xoffset = 0.04621224670174, yoffset = 0.61780780524905, name = "Burning Steppes" };
	GatherRegionData[2][6]  = { scale = 0.07102298961531, xoffset = 0.47822105868635, yoffset = 0.73863555048516, name = "Deadwind Pass" };
	GatherRegionData[2][7]  = { scale = 0.13991525534426, xoffset = 0.40335096278072, yoffset = 0.48339696712179, name = "Dun Morogh" };
	GatherRegionData[2][8]  = { scale = 0.07670475476181, xoffset = 0.43087243362495, yoffset = 0.73224350550454, name = "Duskwood" };
	GatherRegionData[2][9]  = { scale = 0.10996723642661, xoffset = 0.51663255550387, yoffset = 0.15624753972085, name = "Eastern Plaguelands" };
	GatherRegionData[2][10] = { scale = 0.09860350595046, xoffset = 0.41092682316676, yoffset = 0.65651531970162, name = "Elwynn Forest" };
	GatherRegionData[2][11] = { scale = 0.09090931690055, xoffset = 0.42424361247460, yoffset = 0.30113436864162, name = "Hillsbrad Foothills" };
	GatherRegionData[2][12] = { scale = 0.02248317426784, xoffset = 0.47481923366335, yoffset = 0.51289242617182, name = "Ironforge" };
	GatherRegionData[2][13] = { scale = 0.07839152145224, xoffset = 0.51118749188138, yoffset = 0.50940913489577, name = "Loch Modan" };
	GatherRegionData[2][14] = { scale = 0.06170112311456, xoffset = 0.49917278340928, yoffset = 0.68359285304999, name = "Redridge Mountains" };
	GatherRegionData[2][15] = { scale = 0.06338794005823, xoffset = 0.46372051266487, yoffset = 0.57812379382509, name = "Searing Gorge" };
	GatherRegionData[2][16] = { scale = 0.11931848806212, xoffset = 0.35653502290090, yoffset = 0.24715695496522, name = "Silverpine Forest" };
	GatherRegionData[2][17] = { scale = 0.03819701270887, xoffset = 0.41531450060561, yoffset = 0.67097280492581, name = "Stormwind City" };
	GatherRegionData[2][18] = { scale = 0.18128603034401, xoffset = 0.39145470225916, yoffset = 0.79412224886668, name = "Stranglethorn Vale" };
	GatherRegionData[2][19] = { scale = 0.06516347991404, xoffset = 0.51769795272070, yoffset = 0.72815974701615, name = "Swamp of Sorrows" };
	GatherRegionData[2][20] = { scale = 0.10937523495111, xoffset = 0.49929119700867, yoffset = 0.25567971676068, name = "The Hinterlands" };
	GatherRegionData[2][21] = { scale = 0.12837403412087, xoffset = 0.36837217317549, yoffset = 0.15464954319582, name = "Tirisfal Glades" };
	GatherRegionData[2][22] = { scale = 0.02727719546939, xoffset = 0.42973999245660, yoffset = 0.23815358517831, name = "Undercity" };
	GatherRegionData[2][23] = { scale = 0.12215946583965, xoffset = 0.44270955019641, yoffset = 0.17471356786018, name = "Western Plaguelands" };
	GatherRegionData[2][24] = { scale = 0.09943208435841, xoffset = 0.36884571674582, yoffset = 0.71874918595783, name = "Westfall" };
	GatherRegionData[2][25] = { scale = 0.11745423014662, xoffset = 0.46561438951659, yoffset = 0.40971063365152, name = "Wetlands" };
end



function Gatherer_OnLoad()
	this:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF");
	this:RegisterEvent("UNIT_NAME_UPDATE");
	this:RegisterEvent("WORLD_MAP_UPDATE");
	this:RegisterEvent("CLOSE_WORLD_MAP");

	-- event added for impossible to gather item
	this:RegisterEvent("UI_ERROR_MESSAGE");

	Gatherer_LoadContinents(GetMapContinents());

	RegisterForSave("GatherItems");
	RegisterForSave("GatherConfig");

	SLASH_GATHER1 = "/gather";
	SLASH_GATHER2 = "/gatherer";
	SlashCmdList["GATHER"] = function(msg)
		Gatherer_Command(msg);
	end

	-- call to get current character skills
end

function Gatherer_Command(command)
	local i,j, cmd, param = string.find(command, "^([^ ]+) (.+)$");
	if (not cmd) then cmd = command; end
	if (not cmd) then cmd = ""; end
	if (not param) then param = ""; end

	if ((cmd == "") or (cmd == "help")) then
		local useMinimap = "Off";
		if (GatherConfig.useMinimap) then useMinimap = "On"; end
		local useMainmap = "Off";
		if (GatherConfig.useMainmap) then useMainmap = "On"; end
		local mapMinder = "Off";
		if (GatherConfig.mapMinder) then mapMinder = "On"; end
		local minderTime = "5s";
		if (GatherConfig.minderTime) then minderTime = GatherConfig.minderTime.."s"; end
		
		Gatherer_ChatPrint("Usage:");
		Gatherer_ChatPrint("  |cffffffff/gather (on|off|toggle)|r |cff2040ff["..useMinimap.."]|r - turns the gather minimap display on and off");
		Gatherer_ChatPrint("  |cffffffff/gather mainmap (on|off|toggle)|r |cff2040ff["..useMainmap.."]|r - turns the gather mainmap display on and off");
		Gatherer_ChatPrint("  |cffffffff/gather minder (on|off|toggle|<n>)|r |cff2040ff["..mapMinder.."]|r - turns the gather map minder on and off (remembers and reopens your last open main map; within "..minderTime..")");
		Gatherer_ChatPrint("  |cffffffff/gather dist <n>|r |cff2040ff["..GatherConfig.maxDist.."]|r - sets the maximum search distance for display (0=infinite(default), typical=10)");
		Gatherer_ChatPrint("  |cffffffff/gather num <n>|r |cff2040ff["..GatherConfig.number.."]|r - sets the maximum number of items to display (default=10, up to 25)");
		Gatherer_ChatPrint("  |cffffffff/gather fdist <n>|r |cff2040ff["..GatherConfig.fadeDist.."]|r - sets a fade distance (in units) for the icons to fade out by (default = 20)");
		Gatherer_ChatPrint("  |cffffffff/gather fperc <n>|r |cff2040ff["..GatherConfig.fadePerc.."]|r - sets the percentage for fade at max fade distance (default = 80 [=80% faded])");
		Gatherer_ChatPrint("  |cffffffff/gather theme <name>|r |cff2040ff["..GatherConfig.iconSet.."]|r - sets the icon theme: original, shaded (default), or iconic");
		Gatherer_ChatPrint("  |cffffffff/gather idist <n>|r |cff2040ff["..GatherConfig.miniIconDist.."]|r - sets the minimap distance at which the gather icon will become iconic (0 = off, 1-60 = pixel radius on minimap, default = 40)");
		Gatherer_ChatPrint("  |cffffffff/gather herbs (on|off|toggle|auto)|r |cff2040ff["..Gatherer_GetFilterVal("herbs").."]|r - select whether to show herb data on the minimap");
		Gatherer_ChatPrint("  |cffffffff/gather mining (on|off|toggle|auto)|r |cff2040ff["..Gatherer_GetFilterVal("mining").."]|r - select whether to show mining data on the minimap");
		Gatherer_ChatPrint("  |cffffffff/gather treasure (on|off|toggle|auto)|r |cff2040ff["..Gatherer_GetFilterVal("treasure").."]|r - select whether to show treasure data on the minimap");
	elseif (cmd == "on") then
		GatherConfig.useMinimap = true;
		Gatherer_OnUpdate(0, true);
		GatherConfig.useMinimapText = "on";
		Gatherer_ChatPrint("Turned gather minimap display on");
	elseif (cmd == "off") then
		GatherConfig.useMinimap = false;
		GatherConfig.useMinimapText = "off";
		Gatherer_OnUpdate(0, true);
		Gatherer_ChatPrint("Turned gather minimap display off (still collecting)");
	elseif (cmd == "toggle") then
		GatherConfig.useMinimap = not GatherConfig.useMinimap;
		Gatherer_OnUpdate(0, true);
		if (GatherConfig.useMinimap) then
			Gatherer_ChatPrint("Turned gather minimap display on");
		        GatherConfig.useMinimapText = "on";
		else
			Gatherer_ChatPrint("Turned gather minimap display off (still collecting)");
		        GatherConfig.useMinimapText = "off";
		end
	elseif (cmd == "dist") then
		local i,j, value = string.find(param, "(%d+)");
		if (not value) then value = 0; else value = value + 0.0; end
		if (value <= 0) then
			GatherConfig.maxDist = 0;
		else
			GatherConfig.maxDist = value + 0.0;
		end
		Gatherer_ChatPrint("Setting maximum note distance to "..GatherConfig.maxDist);
		Gatherer_OnUpdate(0, true);
	elseif (cmd == "fdist") then
		local i,j, value = string.find(param, "(%d+)");
		if (not value) then value = 0; else value = value + 0.0; end
		if (value <= 0) then
			GatherConfig.fadeDist = 0;
		else
			GatherConfig.fadeDist = value + 0.0;
		end
		Gatherer_ChatPrint("Setting fade distance to "..GatherConfig.fadeDist);
		Gatherer_OnUpdate(0, true);
	elseif (cmd == "fperc") then
		local i,j, value = string.find(param, "(%d+)");
		if (not value) then value = 0; else value = value + 0.0; end
		if (value <= 0) then
			GatherConfig.fadePerc = 0;
		else
			GatherConfig.fadePerc = value + 0.0;
		end
		Gatherer_ChatPrint("Setting fade percent at fade distance to "..GatherConfig.fadePerc);
		Gatherer_OnUpdate(0, true);
	elseif ((cmd == "idist") or (cmd == "icondist")) then
		local i,j, value = string.find(param, "(%d+)");
		if (not value) then value = 0; else value = value + 0; end
		if (value <= 0) then
			GatherConfig.miniIconDist = 0;
		else
			GatherConfig.miniIconDist = value + 0;
		end
		Gatherer_ChatPrint("Setting iconic distance to "..GatherConfig.miniIconDist);
		Gatherer_OnUpdate(0, true);
	elseif (cmd == "theme") then
		if (Gather_IconSet[param]) then
			GatherConfig.iconSet = param;
			Gatherer_ChatPrint("Gatherer theme set to "..GatherConfig.iconSet);
		else
			Gatherer_ChatPrint("Unknown theme: "..param);
		end
		Gatherer_OnUpdate(0, true);
	elseif ((cmd == "num") or (cmd == "number")) then
		local i,j, value = string.find(param, "(%d+)");
		if (not value) then value = 0; else value = value + 0; end
		if (value < 0) then
			GatherConfig.number = 10;
		elseif (value <= GATHERER_MAXNUMNOTES) then
			GatherConfig.number = math.floor(value + 0);
		else
			GatherConfig.number = GATHERER_MAXNUMNOTES;
		end
		if (GatherConfig.number == 0) then
			GatherConfig.useMinimap = false;
		        GatherConfig.useMinimapText = "off";
			Gatherer_OnUpdate(0, true);
			Gatherer_ChatPrint("Turned gather minimap display off (still collecting)");
		else
			if ((GatherConfig.number > 0) and (GatherConfig.useMinimap == false)) then
				GatherConfig.useMinimap = true;
		        	GatherConfig.useMinimapText = "on";
				Gatherer_ChatPrint("Turned gather minimap display on");
			end
			Gatherer_ChatPrint("Displaying "..GatherConfig.number.." notes at once");
			Gatherer_OnUpdate(0, true);
		end
	elseif (cmd == "mainmap") then
		if ((param == "false") or (param == "off") or (param == "no") or (param == "0")) then
			GatherConfig.useMainmap = false;
		elseif (param == "toggle") then
			GatherConfig.useMainmap = not GatherConfig.useMainmap;
		else
			GatherConfig.useMainmap = true;
		end
		if (GatherConfig.useMainmap) then
			Gatherer_ChatPrint("Displaying notes in main map");
		else
			Gatherer_ChatPrint("Not displaying notes in main map");
		end
	elseif (cmd == "minder") then
		if ((param == "false") or (param == "off") or (param == "no") or (param == "0")) then
			GatherConfig.mapMinder = false;
		elseif (param == "toggle") then
			GatherConfig.mapMinder = not GatherConfig.mapMinder;
		elseif (param == "on") then
			GatherConfig.mapMinder = true;
		else
			local i,j, value = string.find(param, "(%d+)");
			if (not value) then value = 0; else value = value + 0; end
			if (value <= 0) then
				GatherConfig.mapMinder = false;
				GatherConfig.minderTime = 0;
			else
				GatherConfig.mapMinder = true;
				GatherConfig.minderTime = value + 0;
			end
			Gatherer_ChatPrint("Setting map minder timeout to "..GatherConfig.minderTime);
		end
		if (GatherConfig.mapMinder) then
			Gatherer_ChatPrint("Map minder activated at "..GatherConfig.minderTime);
		else
			Gatherer_ChatPrint("Not minding your map");
		end
	elseif (cmd == "minder") then
	elseif ((cmd == "herbs") or (cmd == "mining") or (cmd == "treasure")) then
		if ((param == "false") or (param == "off") or (param == "no") or (param == "0")) then
			Gatherer_SetFilter(cmd, "off");
			Gatherer_ChatPrint("Not displaying "..cmd.." notes in minimap");
		elseif (param == "toggle") then
			local cur = Gatherer_GetFilterVal(cmd);
			if ((cur == "on") or (cur == "auto")) then
				cur = "off";
				Gatherer_ChatPrint("Not displaying "..cmd.." notes in minimap");
			else
				cur = "on";
				Gatherer_ChatPrint("Displaying "..cmd.." notes in minimap");
			end
			Gatherer_SetFilter(cmd, cur);
		elseif (param == "auto") then
			Gatherer_SetFilter(cmd, "auto");
			Gatherer_ChatPrint("Displaying "..cmd.." notes in minimap based on ability");
		else
			Gatherer_SetFilter(cmd, "on");
			Gatherer_ChatPrint("Displaying "..cmd.." notes in minimap");
		end
		Gatherer_OnUpdate(0, true);
		GatherMain_Draw();
	end
end

function Gatherer_SetFilter(type, value)
	if (not Gather_Player) then return; end
	if (not GatherConfig.users) then GatherConfig.users = {}; end
	if (not GatherConfig.users[Gather_Player]) then GatherConfig.users[Gather_Player] = {}; end
	if (not GatherConfig.users[Gather_Player].filters) then GatherConfig.users[Gather_Player].filters = {}; end

	GatherConfig.users[Gather_Player].filters[type] = value;
end

function Gatherer_GetFilterVal(type)
	if (not Gather_Player) then return "off"; end
	if (not GatherConfig.users) then GatherConfig.users = {}; end
	if (not GatherConfig.users[Gather_Player]) then GatherConfig.users[Gather_Player] = {}; end
	if (not GatherConfig.users[Gather_Player].filters) then GatherConfig.users[Gather_Player].filters = {}; end
	value = GatherConfig.users[Gather_Player].filters[type];
	if (not value) then return "auto"; end
	return value;
end

function Gatherer_GetFilter(filter)
	value = Gatherer_GetFilterVal(filter);
	if (value == "on") then return true;
	elseif (value == "off") then return false; end
	if (filter == "treasure") then return true; end
	if (not GatherSkills) then return true; end
	if ((GatherSkills[filter]) and (GatherSkills[filter] > 0)) then return true; end
	return false;
end
	
function Gatherer_GetSkills()
   	GatherSkills = {};
	ExpandSkillHeader(0);
	for i=1, GetNumSkillLines(), 1 do
		local skillName, header, isExpanded, skillRank, numTempPoints, skillModifier, skillMaxRank, isAbandonable, stepCost, rankCost, minLevel, skillCostType = GetSkillLineInfo(i);
		if (skillName) then
			if (skillName == TRADE_HERBALISM) then
				GatherSkills.herbs = skillRank;
			elseif (skillName == TRADE_MINING) then
				GatherSkills.mining = skillRank;
			end
		end
	end   
end

GatherMap_InCity = false;
Gatherer_LoadCount = 0;
Gatherer_MapOpen = false;
function Gatherer_OnEvent(event)
	if (strfind(event, "CHAT_MSG")) then
		Gatherer_ReadBuff(event);
	-- process event to record, normally not possible gather (low/inexistant skill)
	elseif (strfind(event, "UI_ERROR_MESSAGE")) then
		-- process gather error message
		-- need to be in standard gather range to get the correct message to process.
		local trade_herb_check
		if ( GetLocale() == "frFR" ) then 
			trade_herb_check = OLD_TRADE_HERBALISM 
		else
			trade_herb_check = TRADE_HERBALISM
		end
               
		if (arg1 and
		    (strfind(arg1, GATHERER_REQUIRE.." "..trade_herb_check) or 
		     strfind(arg1, GATHERER_REQUIRE.." "..TRADE_MINING))) then
			Gatherer_ReadBuff(event);
		end
	elseif (event == "MINIMAP_UPDATE_ZOOM") then
		GatherMap_InCity = isMinimapInCity();
	elseif (event == "WORLD_MAP_UPDATE") then
--		Gatherer_ChatPrint("Map update");
		if (WorldMapFrame:IsVisible()) then
--			Gatherer_ChatPrint("Map is visible");
			local serverTime = GetTime();
			if ((GatherConfig.mapMinder == true) and (Gatherer_MapOpen == false)) then 
--				Gatherer_ChatPrint("Opening map");
				-- Enhancement to open to last opened map if we were there less than a minute ago
				-- Otherwise, go to the player's current position
				local startContinent, startZone;
				if ((Gatherer_CloseMap ~= nil) and (serverTime - Gatherer_CloseMap.time < GatherConfig.minderTime)) then
					startContinent = Gatherer_CloseMap.continent;
					startZone = Gatherer_CloseMap.zone;
--					Gatherer_ChatPrint("Time delta was "..(serverTime - Gatherer_CloseMap.time).." on "..startContinent..":"..startZone);
				else
					if (Gatherer_CloseMap ~= nil) then
--						Gatherer_ChatPrint("Time delta expired: "..(serverTime - Gatherer_CloseMap.time));
					else
--						Gatherer_ChatPrint("Time delta unknown");
					end
					startContinent, startZone = Gatherer_GetCurrentZone();
				end

--				Gatherer_ChatPrint("Changing map to "..startContinent..":"..startZone);
				Gatherer_MapOpen = true;
				SetMapZoom(startContinent, startZone);
			end
			Gatherer_MapOpen = true;
			local mapContinent = GetCurrentMapContinent();
			local mapZone = GetCurrentMapZone();
--			Gatherer_ChatPrint("Setting last map to "..mapContinent..":"..mapZone);
			Gatherer_CloseMap = { continent = mapContinent, zone = mapZone, time = GetTime() };
			GatherMain_Draw();
		elseif (Gatherer_MapOpen) then
--			Gatherer_ChatPrint("Map is not visible but I thought it was open");
			Gatherer_MapOpen = false;
			GatherMain_Draw();
			Gatherer_ChangeMap();
		else
--			Gatherer_ChatPrint("Map is not visible and I think it is closed");
		end
	elseif ( event == "CLOSE_WORLD_MAP" ) then
		Gatherer_MapOpen = false;
		GatherMain_Draw();
		Gatherer_ChangeMap()
	elseif( event == "VARIABLES_LOADED" ) then
		Gatherer_LoadCount = Gatherer_LoadCount + 1;
		if (Gatherer_LoadCount == 10) then
			GATHERER_LOADED = true;
			Gatherer_OnUpdate(0, true);
			GatherMain_Draw();
			Gatherer_Print("Gatherer v"..GATHERER_VERSION.." -- Loaded!");
			if (GatherConfig.useMinimap == nil) then
				GatherConfig.useMinimap = true;
			end
			if (GatherConfig.useMainmap == nil) then
				GatherConfig.useMainmap = true;
			end
			if (not GatherConfig.iconSet) then
				GatherConfig.iconSet = "shaded";
			end
			if (not GatherConfig.number) then
				GatherConfig.number = 10;
			end
			if (not GatherConfig.maxDist) then
				GatherConfig.maxDist = 0;
			end
			if (not GatherConfig.fadeDist) then
				GatherConfig.fadeDist = 40;
			end
			if (not GatherConfig.fadePerc) then
				GatherConfig.fadePerc = 80;
			end
			if (not GatherConfig.miniIconDist) then
				GatherConfig.miniIconDist = 40;
			end
			if (GatherConfig.mapMinder == nil) then
				GatherConfig.mapMinder = true;
			end
			if (not GatherConfig.minderTime) then
				GatherConfig.minderTime = 5;
			end
			if (not GatherConfig.fixscale) then
				if ( GetLocale() == "frFR" ) then
					GatherConfig.fixscale = true;
					RecalcNotes();
				end
			end
		end
	elseif (event == "UNIT_NAME_UPDATE") then
		if ((arg1) and (arg1 == "player")) then
			local playerName = UnitName("player");
			if ((playerName) and (playerName ~= UNKNOWNOBJECT)) then
				Gather_Player = playerName;
				Gatherer_GetSkills();

				local useMinimap = "Off";
				if (GatherConfig.useMinimap) then useMinimap = "On"; end
				local useMainmap = "Off";
				if (GatherConfig.useMainmap) then useMainmap = "On"; end
				local mapMinder = "Off";
				if (GatherConfig.mapMinder) then mapMinder = "On"; end

				local minderTime = "5s";
				if (GatherConfig.minderTime) then minderTime = GatherConfig.minderTime.."s"; end

				Gatherer_Print("[Player: "..Gather_Player..", Theme: "..GatherConfig.iconSet..", Mainmap: "..useMainmap..", Minimap: "..useMinimap..", MaxDist: "..GatherConfig.maxDist.." units, NoteCount: "..GatherConfig.number..", Fade: "..GatherConfig.fadePerc.."% at "..GatherConfig.fadeDist.." units, IconDist: "..GatherConfig.miniIconDist.."px on minimap, MapMinder: "..mapMinder.." ("..minderTime.."), Filters: herbs="..Gatherer_GetFilterVal("herbs")..", mining="..Gatherer_GetFilterVal("mining")..", treasure="..Gatherer_GetFilterVal("treasure").."]");
			end
		end
	end
end

function isMinimapInCity()
	local tempzoom = 0;
	local inCity = false;
	if (GetCVar("minimapZoom") == GetCVar("minimapInsideZoom")) then
		if (GetCVar("minimapInsideZoom")+0 >= 3) then 
			Minimap:SetZoom(Minimap:GetZoom() - 1);
			tempzoom = 1;
		else
			Minimap:SetZoom(Minimap:GetZoom() + 1);
			tempzoom = -1;
		end
	end
	if (GetCVar("minimapInsideZoom")+0 == Minimap:GetZoom()) then inCity = true; end
	Minimap:SetZoom(Minimap:GetZoom() + tempzoom);
	return inCity;
end

function Gatherer_TimeCheck(timeDelta)
	if (not GatherNotes) then
		GatherNotes = { timeDiff=0, checkDiff=0 };
	else
		GatherNotes.checkDiff = GatherNotes.checkDiff + timeDelta;
		if (GatherNotes.checkDiff > GATHERNOTE_CHECK_INTERVAL) then
			GatherNotes.checkDiff = 0;
			Gatherer_GetSkills();
			Gatherer_OnUpdate(0,true);
		end
	end
--	Gatherer_Print("Check test at "..GatherNotes.checkDiff);
end

function Gatherer_HideAll()
--	Gatherer_Print("Hiding all");
	local mmPos = 0;
	while (mmPos < GATHERER_MAXNUMNOTES) do
		mmPos = mmPos+1;
		local gatherNote = getglobal("GatherNote"..mmPos);
		gatherNote:Hide();
	end
end


function Gatherer_OnUpdate(timeDelta, force)
	if (not GATHERER_LOADED) then
		Gatherer_Print("Gatherer not loaded");
		return; 
	end
	
	if (not GatherConfig.useMinimap) then
		Gatherer_HideAll();
		return;
	end
	
	local recalculate = false;
	local needsUpdate = false;
	if (not GatherNotes) then
		GatherNotes = { timeDiff=0, checkDiff=0 };
		needsUpdate = true;
	else
		GatherNotes.timeDiff = GatherNotes.timeDiff + timeDelta;
		if (GatherNotes.timeDiff > GATHERNOTE_UPDATE_INTERVAL) then
			needsUpdate = true;
		end
	end
	if (force) then
		needsUpdate = true;
		recalculate = true;
	end

	if (needsUpdate) then
		if ((not WorldMapFrame:IsVisible()) and (Gatherer_MapOpen)) then
			Gatherer_OnEvent("WORLD_MAP_UPDATE");
		end

		GatherNotes.timeDiff = 0;
		-- Find the 10 closest gathers
		local continent, zone = Gatherer_GetCurrentZone();
		if ((continent == 0) or (zone == 0)) then
			Gatherer_HideAll();
			return;
		end

		local inCity = GatherMap_InCity;
		local zoomLevel = Minimap:GetZoom();
		local px, py = Gatherer_PlayerPos();
		if ((px == 0) and (py == 0)) then 
--			Gatherer_Print("Player is not on a visible map");
			return; 
		end
		local xMovement = 0; if (ClosestGathers) then xMovement = math.abs(ClosestGathers.px - px); end
		local yMovement = 0; if (ClosestGathers) then yMovement = math.abs(ClosestGathers.py - py); end
		if (not ClosestGathers) then recalculate = true;
		elseif (ClosestGathers.playerC ~= continent) then recalculate = true;
		elseif (ClosestGathers.playerZ ~= zone) then recalculate = true;
		elseif (xMovement+yMovement > 0.01) then recalculate = true;
		end

		local displayNumber = 10;
		if ((GatherConfig.number) and (GatherConfig.number > 0)) then
			displayNumber = GatherConfig.number;
		end

		local playerDeltaX = 0;
		local playerDeltaY = 0;
		if (recalculate == true) then
--			Gatherer_Print("Recalculating...");
			ClosestGathers = Gatherer_FindClosest(displayNumber, GatherConfig.interested);
			if (ClosestGathers.count > 0) then
				ClosestGathers.inCity = inCity;
				ClosestGathers.zoomLevel = zoomLevel;
				ClosestGathers.scaleX, ClosestGathers.scaleY = Gatherer_GetMapScale(continent, zone, inCity, zoomLevel);
			end
		else
			if ((inCity ~= ClosestGathers.inCity) or (zoomLevel ~= ClosestGathers.zoomLevel)) then
				ClosestGathers.inCity = inCity;
				ClosestGathers.zoomLevel = zoomLevel;
				ClosestGathers.scaleX, ClosestGathers.scaleY = Gatherer_GetMapScale(continent, zone, inCity, zoomLevel);
			end
			local absX, absY = Gatherer_AbsCoord(continent, zone, px, py);
			playerDeltaX = ClosestGathers.playerX - absX;
			playerDeltaY = ClosestGathers.playerY - absY;
--			Gatherer_Print("PlayerDelta "..playerDeltaX..","..playerDeltaY);
		end

		local maxPos = 0;
		if ((ClosestGathers) and (ClosestGathers.count > 0)) then
			local py;
			for closestPos, closestGather in ClosestGathers.items do
				-- need to position and label the corresponding button
				local gatherNote = getglobal("GatherNote"..closestPos);
				local gatherNoteTexture = getglobal("GatherNote"..closestPos.."Texture");

				local itemDeltaX = closestGather.deltax+playerDeltaX;
				local itemDeltaY = closestGather.deltay+playerDeltaY;
				local offsX, offsY, gDist = Gatherer_MiniMapPos(itemDeltaX, itemDeltaY, ClosestGathers.scaleX, ClosestGathers.scaleY);
--				Gatherer_Print("Got "..offsX..", "..offsY);
				gatherNote:SetPoint("CENTER", "MinimapCluster", "TOPLEFT", 107 + offsX, -92 - offsY);
				
				local iconSet = GatherConfig.iconSet;
				local iDist = GatherConfig.miniIconDist;
				if (not iconSet) then iconSet = "shaded"; end
				if (not iDist) then iDist = 40; end
				if ((iDist > 0) and (gDist > iDist)) then
					iconSet = "iconic";
				end
				local fadeDist = GatherConfig.fadeDist / 1000;
				local fadePerc = GatherConfig.fadePerc / 100;
				local alpha = 1.0;
				if ((fadeDist > 0) and (fadePerc > 0)) then
					local objDist = Gatherer_Pythag(itemDeltaX, itemDeltaY);
					local distRatio = objDist / fadeDist;
					alpha = 1.0 - (math.min(1.0, math.max(0.0, distRatio)) * fadePerc);
				end

				local textureType = closestGather.item.gtype;
				local textureIcon = closestGather.item.icon;

				if (not textureIcon) then textureIcon = "default"; end;
				if (not Gather_IconSet[iconSet]) then iconSet = "shaded"; end
				if (not Gather_IconSet[iconSet][textureType]) then textureType = "Default"; end
				local selectedTexture = Gather_IconSet[iconSet][textureType][textureIcon];
				if (not selectedTexture) then
					selectedTexture = Gather_IconSet[iconSet][textureType]["default"];
				end
				gatherNoteTexture:SetTexture(selectedTexture);
				gatherNote:SetFrameLevel(MiniMapTrackingFrame:GetFrameLevel());
				gatherNote:SetAlpha(alpha);
				gatherNote:Show();
				if (closestPos > maxPos) then maxPos = closestPos; end
			end
		end
		while (maxPos < GATHERER_MAXNUMNOTES) do
			maxPos = maxPos+1;
--			Gatherer_Print("Hiding gather item "..maxPos);
			local gatherNote = getglobal("GatherNote"..maxPos);
			gatherNote:Hide();
		end
	else
--		Gatherer_Print("Update not needed");
	end
end

function GatherMain_Draw()
	local lastUnused = 1000;
	local maxNotes = 1500;
	
	if ((Gatherer_MapOpen) and (GatherConfig.useMainmap)) then
		local mapContinent = GetCurrentMapContinent();
		local mapZone = GetCurrentMapZone();
		if ((mapContinent ~= 0) and (mapZone ~= 0) and (GatherItems[mapContinent]) and (GatherItems[mapContinent][mapZone])) then
			for gatherName, gatherData in GatherItems[mapContinent][mapZone] do
				local gatherType = "Default";
				local specificType = "";
				local allowed = true;

				specificType = Gatherer_FindOreType(gatherName);
				if (specificType) then
					gatherType = "Ore";
					allowed = Gatherer_GetFilter("mining");
				else
					specificType = Gatherer_FindTreasureType(gatherName);
					if (specificType) then
						gatherType = "Treasure";
						allowed = Gatherer_GetFilter("treasure");
					else
						specificType = gatherName;
						gatherType = "Herb"; 
						allowed = Gatherer_GetFilter("herbs");
					end
				end
				if (not specificType) then
					specificType = "default";
				end

				if (allowed) then
					for hPos, gatherInfo in gatherData do
						if ((gatherInfo.x) and (gatherInfo.y) and (gatherInfo.x>0) and (gatherInfo.y>0) and (lastUnused <= maxNotes)) then
							local mainNote = getglobal("GatherMain"..lastUnused);

							local mnX,mnY;
							mnX = gatherInfo.x / 100 * WorldMapDetailFrame:GetWidth();
							mnY = -gatherInfo.y / 100 * WorldMapDetailFrame:GetHeight();
							mainNote:SetAlpha(0.8);
							mainNote:SetPoint("CENTER", "WorldMapDetailFrame", "TOPLEFT", mnX, mnY);
							mainNote:SetFrameLevel(WorldMapPlayer:GetFrameLevel() - 1);
							mainNote.toolTip = Gatherer_TitleCase(specificType);

							if (not Gather_IconSet["iconic"][gatherType]) then 
								gatherType = "Default"; 
							end
							local texture = Gather_IconSet["iconic"][gatherType][specificType];
							if (not texture) then
								texture = Gather_IconSet["iconic"][gatherType]["default"];
							end

							local mainNoteTexture = getglobal("GatherMain"..lastUnused.."Texture");
							mainNoteTexture:SetTexture(texture);
							mainNote:Show();
							lastUnused = lastUnused + 1;
						end
					end
				end
			end
		end
	end
	for i=lastUnused, maxNotes, 1 do
		local mainNote = getglobal("GatherMain"..i);
		mainNote:Hide();
	end
end

function Gatherer_OnClick() -- function changed to be able to ping through the MiniNote (mostly direct copy) (only change: this -> Minimap)
	local x, y = GetCursorPosition();
	x = x / Minimap:GetScale();
	y = y / Minimap:GetScale();

	local cx, cy = Minimap:GetCenter();
	x = x + CURSOR_OFFSET_X - cx;
	y = y + CURSOR_OFFSET_Y - cy;
	if ( sqrt(x * x + y * y) < (Minimap:GetWidth() / 2) ) then
		Minimap:PingLocation(x, y);
	end
end

function Gatherer_MakeName(frameID)
	if (not GATHERER_LOADED) then return "Unknown", 0, 0; end
	local gatherInfo = ClosestGathers.items[frameID];
	local approxDist = math.floor(gatherInfo.dist*10000)/10;
	return Gatherer_TitleCase(gatherInfo.name), gatherInfo.item.count, approxDist;
end


function Gatherer_FindClosest(num, interested)
	local gatherLocal = {playerC=0,playerZ=0,playerX=0,playerY=0,px=0,py=0,items={},count=0};

	if (not GATHERER_LOADED) then return gatherLocal; end

	local continent, zone = Gatherer_GetCurrentZone();
	if ((continent == 0) or (zone == 0)) then return gatherLocal; end
	local px,py = Gatherer_PlayerPos();
	if ((px == 0) and (py == 0)) then return gatherLocal; end

	local absPx, absPy = Gatherer_AbsCoord(continent, zone, px, py);
	gatherLocal = { playerC=continent, playerZ=zone, playerX=absPx, playerY=absPy, px=px, py=py, items={}, count=0 };

	local gatherCount = 0;
	local maxAllowable = 0;
	if ((GatherConfig.maxDist) and (GatherConfig.maxDist > 0)) then
		maxAllowable = GatherConfig.maxDist / 1000;
	end
	
	if (not GatherItems[continent]) then return gatherLocal; end

	for gatherZone, gathersInZone in GatherItems[continent] do
		for gatherName, gatherData in gathersInZone do
			local gatherType = "Default";
			local specificType = "";
			local allowed = true;

			specificType = Gatherer_FindOreType(gatherName);
			if (specificType) then
				gatherType = "Ore";
				allowed = Gatherer_GetFilter("mining");
			else
				specificType = Gatherer_FindTreasureType(gatherName);
				if (specificType) then
					gatherType = "Treasure";
					allowed = Gatherer_GetFilter("treasure");
				else
					specificType = gatherName;
					gatherType = "Herb"; 
					allowed = Gatherer_GetFilter("herbs");
				end
			end
			if (not specificType) then
				specificType = "default";
			end

			if ((allowed) and ((not interested) or (interested[gatherName]))) then
				for hPos, gatherInfo in gatherData do

					local absHx;
					local absHy;

					if ((not gatherInfo.gtype) or (gatherInfo.gtype == "")) then
						gatherInfo.gtype = gatherType;
					end

					if ((not gatherInfo.icon) or (gatherInfo.icon == "")) then
						gatherInfo.icon = specificType;
					end

					if ((gatherInfo.absx) and (gatherInfo.absy)) then
						absHx = gatherInfo.absx;
						absHy = gatherInfo.absy;
					else
						absHx, absHy = Gatherer_AbsCoord(continent, gatherZone, gatherInfo.x/100, gatherInfo.y/100);
						absHx = math.floor(absHx * 100000)/100000;
						absHy = math.floor(absHy * 100000)/100000;
						GatherItems[continent][gatherZone][gatherName][hPos].absx = absHx;
						GatherItems[continent][gatherZone][gatherName][hPos].absx = absHy;
					end
					if ((absHx ~= 0) and (absHy ~= 0)) then
						local maxLocalPos = 0;
						local replCandidate = 0;
						local replPos = 0;
						local dist, deltaX, deltaY = Gatherer_Distance(absPx, absPy, absHx, absHy);
						if ((maxAllowable == 0) or (dist < maxAllowable)) then
							for localPos, localInfo in gatherLocal.items do
								if (localPos > maxLocalPos) then maxLocalPos = localPos; end
								if (localInfo.dist > dist) then
									if ((replCandidate == 0) or (localInfo.dist > gatherLocal.items[replCandidate].dist)) then
	--									Gatherer_Print("Choosing item "..hPos.." over "..replPos..": "..dist.." vs "..localInfo.dist);

										replCandidate = localPos;
										replPos = hPos;
									end
								end
							end
							if (maxLocalPos < num) then
								replCandidate = maxLocalPos+1;
								replPos = 0;
							end
						end

						if (replCandidate > 0) then
--							Gatherer_Print("Setting item "..replCandidate..": pos "..replPos..", "..dist.."d, "..gatherName);
							local setItem = {};
							setItem.name = gatherName;
							setItem.item = gatherInfo;
							setItem.dist = dist;
							setItem.deltax = deltaX;
							setItem.deltay = deltaY;

							gatherLocal["items"][replCandidate] = setItem;
							gatherCount = gatherCount+1;
						else
	--						Gatherer_Print("Ignoring item "..replCandidate..": "..dist..", "..gatherName);
						end
					end
				end
			end
		end
	end
	gatherLocal.count = gatherCount;
	return gatherLocal;
end

function Gatherer_AbsCoord(continent, zone, x, y)
	if ((continent == 0) or (zone == 0)) then return x, y; end
	local r = GatherRegionData[continent][zone];
	local absX = x * r.scale + r.xoffset;
	local absY = y * r.scale + r.yoffset;
	return absX, absY;
end

function Gatherer_Distance(originX, originY, targetX, targetY)
	local dx = (targetX - originX);
	local dy = (targetY - originY);
	local d = Gatherer_Pythag(dx, dy);
	return d, dx, dy;
end
function Gatherer_Pythag(dx, dy)
	local d = math.sqrt(dx*dx + dy*dy); -- (a*a) is usually computationally faster than math.pow(a,2)
	if (d == 0) then d = 0.0000000001; end -- to avoid divide by zero errors.
	return d;
end

function Gatherer_GetMapScale(continent, zone, inCity, zoomLevel)
	if ((continent == nil) or (zoomLevel == nil)) then return 0,0; end
	if ((not GatherRegionData) or 
	(not GatherRegionData[continent]) or 
	(not GatherRegionData[continent].scales) or 
	(not GatherRegionData[continent].scales[zoomLevel]) or 
	(not GatherRegionData[continent].scales[zoomLevel].xscale) or
	(not GatherRegionData[continent].scales[zoomLevel].yscale)) then
--		Gatherer_Print("Returning 0,0 scale");
		return 0,0;
	end

	local scaleX = GatherRegionData[continent].scales[zoomLevel].xscale;
	local scaleY = GatherRegionData[continent].scales[zoomLevel].yscale;
	if (inCity == true) then
		local cityScale = GatherRegionData.cityZoom[zoomLevel];
--		Gatherer_Print("Using city scale "..cityScale);
		scaleX = scaleX * cityScale;
		scaleY = scaleY * cityScale;
	else
--		Gatherer_Print("Using country scale");
	end
	
--	Gatherer_Print("Returning scale "..scaleX..","..scaleY);
	return scaleX, scaleY;
end

function Gatherer_MiniMapPos(deltaX, deltaY, scaleX, scaleY) -- works out the distance on the minimap
	local mapX = deltaX * scaleX;
	local mapY = deltaY * scaleY;
	local mapDist = 0;
	--print2 ("Calculating map pos from "..deltaX..","..deltaY.." @"..scaleX..","..scaleY);
	mapDist = Gatherer_Pythag(mapX, mapY);
	if (mapDist >= 57) then
		-- Remap it to just inside the minimap, by:
		--   converting dx,dy to angle,distance
		--   then truncate distance to 58 
		--   and convert angle,58 to dx,dy
		local flipAxis = 1;
		if (mapX == 0) then mapX = 0.0000000001;
		elseif (mapX < 0) then flipAxis = -1;
		end
		local angle = math.atan(mapY / mapX);
		mapX = math.cos(angle) * 58 * flipAxis;
		mapY = math.sin(angle) * 58 * flipAxis;
	end
	return mapX, mapY, mapDist;
end

function Gatherer_Spam(c, z, num)
	local gatherC, gatherZ = Gatherer_GetCurrentZone();
	if ((c~=nil) and (c~=0)) then gatherC = c; end
	if ((z~=nil) and (z~=0)) then gatherZ = z; end
	if ((num==nil) or (num==0)) then num = 100; end
	if (not GatherItems[gatherC]) then GatherItems[gatherC] = { }; end
	if (not GatherItems[gatherC][gatherZ]) then GatherItems[gatherC][gatherZ] = { }; end
	if (not GatherItems[gatherC][gatherZ]["test"]) then GatherItems[gatherC][gatherZ]["test"] = { }; end

	local px,py = Gatherer_PlayerPos();
	local gatherX;
	local gatherY;
	math.randomseed((px+py)*10000);
	local totalCount = 0;
	for i=1, num, 1 do
		gatherX = math.random(1,999)/10;
		gatherY = math.random(1,999)/10;
		Gatherer_Print("Adding point at "..gatherC..":"..gatherZ..";"..gatherX..","..gatherY);
		Gatherer_AddGather("test", "Test", gatherC, gatherZ, gatherX, gatherY, "test");
		totalCount = totalCount + 1;
	end
	Gatherer_Print("Spammed "..totalCount.." test items around you in c"..gatherC.."z"..gatherZ..". Last one was "..gatherX..","..gatherY.." and you are at "..px..","..py);
	Gatherer_OnUpdate(0,true);
end

function Gatherer_Unspam()
	if (GatherItems) then
		for gatherC, cData in GatherItems do
			for gatherZ, zData in cData do
				if ((zData) and (zData["test"])) then
					GatherItems[gatherC][gatherZ]["test"] = nil;
				end
			end
		end
	end
	Gatherer_Print("Unspammed all items");
	Gatherer_OnUpdate(0,true);
end

-- Test: /script arg1="You perform Herb Gathering on Mageroyal."; Gatherer_ReadBuff("event");
function Gatherer_ReadBuff(event)
	local record = false;
	local gather;
	local gatherType, gatherIcon;
	if (not arg1) then return; end

	if( strfind(event, "CHAT_MSG") or strfind(event, "CHAT_MSG_SPELL_SELF_BUFF")) then
		if (string.find(arg1, HERB_GATHER_STRING)) then
			record = true;
			gather = string.lower(strsub(arg1, HERB_GATHER_LENGTH, HERB_GATHER_END))
			if ( GetLocale() == "frFR" ) then gather = string.gsub(gather , '\'', "’"); end

			gatherType = "Herb";
			gatherIcon = gather;
		elseif (string.find(arg1, ORE_GATHER_STRING)) then
			record = true;
			gather = string.lower(strsub(arg1, ORE_GATHER_LENGTH, ORE_GATHER_END))
			if ( GetLocale() == "frFR" ) then gather = string.gsub(gather , '\'', "’"); end
			gatherType = "Ore";
			gatherIcon = Gatherer_FindOreType(gather);
			if (not gatherIcon) then return; end;
		elseif (string.find(arg1, TREASURE_GATHER_STRING)) then
			record = true;
			gather = string.lower(strsub(arg1, TREASURE_GATHER_LENGTH, TREASURE_GATHER_END));
			gatherType = "Treasure";
			gatherIcon = Gatherer_FindTreasureType(gather);
			if (not gatherIcon) then return; end;
		end
	-- process event to record, normally not possible gather (low/inexistant skill)
	elseif (strfind(event, "UI_ERROR_MESSAGE")) then
		gather = Gatherer_ExtractItemFromTooltip();

		-- process non gatherable item because of low/lack of skill
		local trade_herb_check
		if ( GetLocale() == "frFR" ) 
                then
			-- need to add this since herbalism trade name was translated differently
			-- but the part used in the error messages was not changed to match new name.
			trade_herb_check = OLD_TRADE_HERBALISM
		else
			trade_herb_check = TRADE_HERBALISM
		end

		if(gather and strfind(arg1, trade_herb_check)) then
			record = true;
			gatherType = "Herb";
			gatherIcon = gather;
		elseif (gather and strfind(arg1, TRADE_MINING)) then
			record = true;
			gatherType = "Ore";
			gatherIcon = Gatherer_FindOreType(gather);
		end
	else
		return;
	end

	if (record == true) then
		local gatherC, gatherZ = Gatherer_GetCurrentZone();
		local px, py = Gatherer_PlayerPos();
		if ((px == 0) and (py == 0)) then
			Gatherer_Print("Gatherer: Cannot record item position as client is reporting position 0,0");
			return; 
		end
		local gatherX = px * 100;
		local gatherY = py * 100;
		if (not GatherItems) then
			Gatherer_Print("Initializing empty gatherable database");
			GatherItems = { };
		end

		Gatherer_AddGather(gather, gatherType, gatherC, gatherZ, gatherX, gatherY, gatherIcon);
--		Gatherer_Print("Found "..gather.." at "..gatherX..","..gatherY.." continent "..gatherC.." zone "..gatherZ);
		RegisterForSave("GatherItems");
		Gatherer_OnUpdate(0,true);
		GatherMain_Draw();
	end
end

function Gatherer_AddGather(gather, gatherType, gatherC, gatherZ, gatherX, gatherY, gatherIcon)
	if (not GatherItems[gatherC]) then GatherItems[gatherC] = { }; end
	if (not GatherItems[gatherC][gatherZ]) then GatherItems[gatherC][gatherZ] = { }; end
	if (not GatherItems[gatherC][gatherZ][gather]) then GatherItems[gatherC][gatherZ][gather] = { }; end
	
	local found = 0;
	local lastGather = 0;
	local closest = 0;
	for hPos, gatherData in GatherItems[gatherC][gatherZ][gather] do
--		Gatherer_Print("Checking gather: "..hPos);
		local dist, deltaX, deltaY = Gatherer_Distance(gatherX,gatherY, gatherData.x,gatherData.y);
--		Gatherer_Print("Distance = "..dist);
		if ((dist < 0.5) and ((closest == 0) or (closest > dist))) then -- same gather
--			Gatherer_Print("Same gather as "..hPos);
			gatherX = (gatherX + gatherData.x) / 2;
			gatherY = (gatherY + gatherData.y) / 2;
			closest = dist;
			found = hPos;
		end
		if (hPos > lastGather) then
			lastGather = hPos;
		end
	end
	if (found == 0) then -- need to create a new one (at hPos+1)
		found = lastGather+1;
--		Gatherer_Print("Creating new gatherable at "..found);
	end
	
	local gatherCount = 1;
	if (GatherItems[gatherC][gatherZ][gather][found] == nil) then
--		Gatherer_Print("Initializing GatherItems["..gatherC.."]["..gatherZ.."]["..gather.."]["..found.."]");
		GatherItems[gatherC][gatherZ][gather][found] = { };
	else
		gatherCount = GatherItems[gatherC][gatherZ][gather][found].count + 1;
	end

	-- Round off those coordinates
	local absX, absY = Gatherer_AbsCoord(gatherC, gatherZ, gatherX/100, gatherY/100);
	gatherX = math.floor(gatherX * 100)/100;
	gatherY = math.floor(gatherY * 100)/100;
	absX = math.floor(absX * 10000)/10000;
	absY = math.floor(absY * 10000)/10000;
	
--	Gatherer_Print("Setting GatherItems["..gatherC.."]["..gatherZ.."]["..gather.."]["..found.."].{x="..gatherX..",y="..gatherY..",ax="..absX..",ay="..absY..",count = "..gatherCount.."}");
	GatherItems[gatherC][gatherZ][gather][found].x = gatherX;
	GatherItems[gatherC][gatherZ][gather][found].y = gatherY;
	GatherItems[gatherC][gatherZ][gather][found].absx = absX;
	GatherItems[gatherC][gatherZ][gather][found].absy = absY;
	GatherItems[gatherC][gatherZ][gather][found].gtype = gatherType;
	GatherItems[gatherC][gatherZ][gather][found].count = gatherCount;
	GatherItems[gatherC][gatherZ][gather][found].icon = gatherIcon;
end

function Gatherer_Clear()
	Gatherer_Print("Clearing your gather data");
	GatherItems = { };
	ClosestList = { };
	ClosestSearchGather = "";
	Gatherer_OnUpdate(0,true);
	GatherMain_Draw();
end
 
function Gatherer_Show()
	for gatherCont, contData in GatherItems do
		for gatherZone, zoneData in contData do
			for gatherName, nameData in zoneData do
				for gatherPos, gatherItem in nameData do
					Gatherer_Print(gatherItem.gtype.." "..gatherName.." was found in zone "..gatherCont..":"..gatherZone.." at "..gatherItem.x..","..gatherItem.y.."  ("..gatherItem.count.." times)");
				end
			end
		end
	end
end

function Gatherer_ChatPrint(str)
	if ( DEFAULT_CHAT_FRAME ) then 
		DEFAULT_CHAT_FRAME:AddMessage(str, 1.0, 0.5, 0.25);
	end
end

function Gatherer_Print(str, add)
	if ((Gather_LastPrinted) and (str == Gather_LastPrinted)) then
		return;
	end
	Gather_LastPrinted = str;
	if (add) then
		str = str..": "..add;
	end
	if(ChatFrame2) then
		ChatFrame2:AddMessage(str, 1.0, 1.0, 0.0);
	end
end

function Gatherer_TitleCase(str)
	if (GetLocale() == "frFR") then return str; end

	local function ucaseWord(first, rest)
		return string.upper(first)..string.lower(rest)
	end
	return string.gsub(str, "([a-zA-Z])([a-zA-Z']*)", ucaseWord);
end

function Gatherer_LoadContinents(...)
	GatherZoneData = { };
	for c=1, arg.n, 1 do
		local cname = arg[c];
		Gatherer_LoadZones(c, cname, GetMapZones(c));
	end
end
function Gatherer_LoadZones(c,cname, ...)
	for z=1, arg.n, 1 do
		local zname = arg[z];
		GatherZoneData[zname] = { continent=c, zone=z, continentName=cname };
	end
end

function Gatherer_GetCurrentZone()
	local currentZoneName = GetZoneText();
	for zoneName, zoneData in GatherZoneData do
		if (currentZoneName == zoneName) then
			return zoneData.continent, zoneData.zone;
		end
	end
--	print2("Couldn't get current zone for "..currentZoneName);
	return 0,0;
end
	
Gatherer_LastZone = {};
function Gatherer_ChangeMap()
	local mapContinent = GetCurrentMapContinent();
	local mapZone = GetCurrentMapZone();
	local minderCurZone = false;
	if ((Gatherer_CloseMap ~= nil) and (Gatherer_CloseMap.continent == Gatherer_LastZone.continent) and (Gatherer_CloseMap.zone == Gatherer_LastZone.zone)) then
--		Gatherer_ChatPrint("Player's last zone is minder zone");
		minderCurZone = true;
	end
	local playerContinent, playerZone = Gatherer_GetCurrentZone();
	Gatherer_LastZone = { continent = playerContinent, zone = playerZone };
	if ((playerContinent == 0) or (playerZone == 0)) then return false; end
	if ((playerContinent == mapContinent) and (playerZone == mapZone)) then return true; end
--	Gatherer_ChatPrint("Had to get player zone "..playerContinent..":"..playerZone.." cause map pos was "..mapContinent..":"..mapZone);

	SetMapZoom(playerContinent, playerZone);
	local lastTime = 0;
	if ((Gatherer_CloseMap ~= nil) and (Gatherer_CloseMap.time ~= nil)) then
		lastTime = Gatherer_CloseMap.time;
	end
	
	if (minderCurZone) then
--		Gatherer_ChatPrint("Resetting minder zone to "..playerContinent..":"..playerZone);
		Gatherer_MapOpen = false;
		Gatherer_CloseMap = { continent = playerContinent, zone = playerZone, time = lastTime };
		Gatherer_OnEvent("WORLD_MAP_UPDATE");
	end
	
	return true;
end

function Gatherer_PlayerPos(forced)
	local currentZoneName = GetZoneText();
	if (currentZoneName ~= Gather_LastZone) then
		forced = true;
	end
	Gather_LastZone = currentZoneName;
	if (forced) then Gatherer_ChangeMap(); end

	local px, py = GetPlayerMapPosition("player");
	if ((px == 0) and (py == 0)) then
		if (not forced) then 
			if (Gatherer_ChangeMap()) then
				px, py = GetPlayerMapPosition("player");
			else
				return 0,0;
			end
		end
	end
	return px, py;
end


function RecalcNotes()
	local note_count=0;
	Gatherer_ChatPrint("Starting recompute of mininotes coordinates...")
	for idx_c, rec_continent in GatherItems do
		for idx_z, rec_zone in rec_continent do
			if ( idx_c ~= 0 and idx_z ~= 0) then
				--Gatherer_ChatPrint("continent: "..idx_c.." zone: "..idx_z.. " : "..GatherRegionData[idx_c][idx_z].name);
				for myItems, rec_gatheritem in rec_zone do
					--Gatherer_ChatPrint(" ==>"..myItems);
					for idx_item, myGather in rec_gatheritem do
						--Gatherer_ChatPrint("Id: "..idx_item.." type: "..myGather.gtype.." icon: "..myGather.icon);
						rec_absHx, rec_absHy = Gatherer_AbsCoord(idx_c, idx_z, myGather.x/100, myGather.y/100);
						rec_absHx = math.floor(rec_absHx * 100000)/100000;
						rec_absHy = math.floor(rec_absHy * 100000)/100000;
						GatherItems[idx_c][idx_z][myItems][idx_item].absx = rec_absHx;
						GatherItems[idx_c][idx_z][myItems][idx_item].absy = rec_absHy;
						note_count = note_count + 1;
					end
				end
			end
		end
	end
	Gatherer_ChatPrint("Recompute of mininotes coordinates completed ("..note_count.." items fixed).")
end


