local ZGV = ZygorGuidesViewer ; if not ZGV then return end

local table,string,tonumber,ipairs,pairs,setmetatable,tinsert = table,string,tonumber,ipairs,pairs,setmetatable,tinsert

local MapSpotSet = { }
ZGV.MapSpotSetProto = MapSpotSet
ZGV.MapSpotSetProto_mt = { __index=MapSpotSet }

local BZL=ZGV.BZL

function MapSpotSet:New()
	return setmetatable({['spots']={},['visible']=true},ZGV.MapSpotSetProto_mt)
end

function MapSpotSet:NewRaw(title,tit,data)
	local set = MapSpotSet.New()
	set.title=title
	set.title_short=tit
	set.rawdata=data
	return set
end

local function split(str,sep)
	local fields = {}
	str = str..sep
	local tinsert=tinsert
	str:gsub("(.-)"..sep, function(c) tinsert(fields, c) end)
	return fields
end

--local baditems={}
--- parse ONE guide section into usable arrays.
function MapSpotSet:ParseRaw()
	local text = self.rawdata
	if not text then return nil,"No text!",0 end
	local index = 1

	local prevmap
	local prevlevel = 1

	local set = self
	local spot

	text = text .. "\n"

	local linecount=0

	local function COLOR_LOC(s) return "|cffffee77"..s.."|r" end

	local _


	while (index<#text) do
		local st,en,line=string.find(text,"%s*(.-)%s*\n",index)
		if not en then break end
		index = en + 1

		linecount=linecount+1
		if linecount>100000 then return nil,linecount,"More than 100000 lines!?" end

		line = line:gsub("//.*$","") .."|";

		local chunkcount=1

		for chunk in line:gmatch("%s*(.-)%s*|+") do
			local cmd,params = chunk:match("([^%s]*)%s?(.*)")

			    if cmd=="map" then
				prevmap = params
				if BZL[prevmap] then prevmap=BZL[prevmap] end
			elseif cmd=="author" then
				set.author=params
			elseif cmd=="description" then
				set.description=params

			-- node types
			elseif cmd=="at" then
				-- create new node
				spot = ZGV.MapSpotProto:New("at",prevlevel)
				set:AddSpot(spot)

				-- fill it!
				local map,x,y,dist = ZGV:ParseMapXYDist(params)

				if BZL[map] then map=BZL[map] end

				spot.map = map or prevmap
				prevmap = spot.map

				spot.x = x
				spot.y = y
				spot.dist = dist

				if not spot.map then
					return nil,"'"..cmd.."' has no map parameter, neither has one been given before.",linecount,chunk
				end

				spot.itemsource="drops"

			-- step parameters
			elseif cmd=="level" or cmd=="lv" then
				prevlevel=tonumber(params)
				spot.level=prevlevel
			elseif cmd=="title" then
				spot.title=params
			elseif cmd=="desc" then
				spot.desc=params
				--ZGV:Print(spot.desc)

--[[
			elseif cmd=="@" then
				local map,x,y
				map,x,y = params:match("(.+),([0-9.]+),([0-9.]+)")
				if not map then
					x,y = params:match("([0-9.]+),([0-9.]+)")
				end
				if not x then
					map = params
				end
				if not map then
					map = prevmap
				end
				step['map']=map
				prevmap=map
				if x or y then
					step['x']=x
					step['y']=y
				end
--]]

			-- node subcommands
			elseif cmd=="from" then
				params=params:gsub(",%s+",",")
				spot.mobsraw = params
				local mobs = split(params,",")
				spot.mobspre = mobs
				spot.mobs = {}
				for i,mob in ipairs(mobs) do
					local name,plural = mob:match("^(.+)(%+)$")
					if not plural then name=mob end

					local nm,id = ZGV:ParseID(name)
					
					local name,tip = ZGV.Localizers:GetTranslatedNPC(id)

					tinsert(spot.mobs,{name=nm or name,id=id,pl=plural and true or false})
				end

				spot.itemsource="drop"

			elseif cmd=="vendor" then
				spot.vendor,spot.vendorid = ZGV:ParseID(params)

				spot.itemsource="vendor"
				spot.title = spot.title or spot.vendor

			elseif cmd=="item" then
				params = params..","
				for itemstr in params:gmatch("%s*(.-)%s*,") do
					-- create the goods
					local object = { type="item" }
					tinsert(spot.objects,object)

					-- first, extract the count
					local name,perc = itemstr:match("^(.+) ([0-9]+)%%$")
					if not name then name=itemstr end
					object.perc = perc

					-- alias?
					local alias=ZGV.MapSpotNodeAliases[name]
					if alias then
						if type(alias)=="table" then
							-- woot, alias
							object.item=alias
						elseif type(alias)=="number" then
							object.item={id=alias} -- and that's all we have
						end
					else
						object.item={}
						object.item.name,object.item.id = ZGV:ParseID(name)
						if not object.item.id then --and not baditems[name] 
							ZGV:Print("|cffff6600ERROR: unknown item |cffffaa00"..name.."|cffff6600 in the gold guide|r")
							--baditems[name]=true
						end
					end


					if object.perc then object.perc=tonumber(object.perc) end

					-- something missing?
					if not object.item then return nil,"no parameter",linecount,chunk end
					--[[
					if goal.target:match("%+%+") then
						if goal.target:match("%+%+$") then
							goal.target = goal.target:gsub("%+%+","")
							goal.targets = goal.target
						else
							local sing,pl = goal.target:match("(.+)%+%+%+(.+)")
							if not sing or not pl then
								sing = goal.target:gsub("([^%s%+]+)++([^%s%+]+)","%1")
								pl = goal.target:gsub("([^%s%+]+)++([^%s%+]+)","%2")
							end
							goal.target = sing
							goal.targets = pl
						end
					end
					--]]

					if spot.itemsource=="vendor" then object.vendor=1 end
				end

			end

			chunkcount=chunkcount+1
			if chunkcount>20 then
				return nil,"More than 20 chunks in line",linecount,line
			end
		end

		--[[
		if #TableKeys(node)>0 then
			if not node then return nil,"What? Unknown data before first node, or what?",linecount,line end
			-- so there's something to record? go ahead.
		end
		--]]

	end

	self.rawdata=nil
	return true
end

function MapSpotSet:AddSpot(spot)
	spot.parentSet = self
	spot.num=#self.spots+1
	tinsert(self.spots,spot)
end

function MapSpotSet:Show()
	self.visible=true
	for s,spot in ipairs(self.spots) do
		spot:Show()
	end
end

function MapSpotSet:GetSpotsInRange(into)
	local ret=into or {}
	local tinsert=tinsert
	for s,spot in ipairs(self.spots) do
		if spot.waypoint and spot.waypoint.minimapFrame and spot.waypoint.minimapFrame:IsShown() and spot.waypoint.minimapFrame.dist and spot.waypoint.minimapFrame.dist<=ZGV.db.profile.golddetectiondist then
			tinsert(ret,spot)
		end
	end
	table.sort(ret,function(a,b) return a.waypoint.minimapFrame.dist and a.waypoint.minimapFrame.dist and a.waypoint.minimapFrame.dist<b.waypoint.minimapFrame.dist or false end)
	return ret
end

function MapSpotSet:UpdateVisibilities()
	for s,spot in ipairs(self.spots) do
		spot:UpdateVisibility()
	end
end





function ZGV:GetMapSpotsInRange()
	local ret={}
	local tinsert=tinsert
	for i,set in ipairs(self.registeredmapspotsets) do
		set:GetSpotsInRange(ret)
	end
	return ret
end

function ZGV:GetMapSpotsInZone()
	local ret={}
	local tinsert=tinsert
	for i,set in ipairs(self.registeredmapspotsets) do
		for s,spot in ipairs(set.spots) do
			if spot.waypoint and not spot.hidden and spot.map==GetRealZoneText() then
				tinsert(ret,spot)
			end
		end
	end
	return ret
end

function ZGV:GetAllMapSpots()
	local ret={}
	local tinsert=tinsert
	for i,set in ipairs(self.registeredmapspotsets) do
		for s,spot in ipairs(set.spots) do
			tinsert(ret,spot)
		end
	end
	return ret
end

function ZGV:UpdateMapSpots()
	for i,set in ipairs(self.registeredmapspotsets) do
		for s,spot in ipairs(set.spots) do
			spot:UpdateVisibility()
		end
	end
end

function ZGV:UpdateMapSpotVisibilities()
	for i,set in ipairs(self.registeredmapspotsets) do
		set:UpdateVisibilities()
	end
	ZGV.Pointer.Overlay_OnEvent(ZGV.Pointer.OverlayFrame,"WORLD_MAP_UPDATE")
	self:UpdateFrame(true)
end