local GlobalAddonName, ExRT = ...

local module = ExRT:New("WAChecker",ExRT.L.WAChecker)
local ELib,L = ExRT.lib,ExRT.L

local LibDeflate = LibStub:GetLibrary("LibDeflate")

module.db.responces = {}
module.db.lastReq = {}

function module.options:Load()
	self:CreateTilte()
	
	local UpdatePage

	local Filter

	local errorNoWA = ELib:Text(self,L.WACheckerWANotFound):Point("TOP",0,-30)
	errorNoWA:Hide()
	
	local PAGE_HEIGHT,PAGE_WIDTH = 520,680
	local LINE_HEIGHT,LINE_NAME_WIDTH = 16,160
	local VERTICALNAME_WIDTH = 20
	local VERTICALNAME_COUNT = 24
	
	local mainScroll = ELib:ScrollFrame(self):Size(PAGE_WIDTH,PAGE_HEIGHT):Point("TOP",0,-80):Height(700)
	ELib:Border(mainScroll,0)

	ELib:DecorationLine(self):Point("BOTTOM",mainScroll,"TOP",0,0):Point("LEFT",self):Point("RIGHT",self):Size(0,1)
	ELib:DecorationLine(self):Point("TOP",mainScroll,"BOTTOM",0,0):Point("LEFT",self):Point("RIGHT",self):Size(0,1)
	
	local prevTopLine = 0
	local prevPlayerCol = 0
	
	mainScroll.ScrollBar:ClickRange(LINE_HEIGHT)
	mainScroll.ScrollBar.slider:SetScript("OnValueChanged", function (self,value)
		local parent = self:GetParent():GetParent()
		parent:SetVerticalScroll(value % LINE_HEIGHT) 
		self:UpdateButtons()
		local currTopLine = floor(value / LINE_HEIGHT)
		if currTopLine ~= prevTopLine then
			prevTopLine = currTopLine
			UpdatePage()
		end
	end)
	
	local raidSlider = ELib:Slider(self,""):Point("TOPLEFT",mainScroll,"BOTTOMLEFT",LINE_NAME_WIDTH + 15,-3):Range(0,25):Size(VERTICALNAME_WIDTH*VERTICALNAME_COUNT):SetTo(0):OnChange(function(self,value)
		local currPlayerCol = floor(value)
		if currPlayerCol ~= prevPlayerCol then
			prevPlayerCol = currPlayerCol
			UpdatePage()
		end
	end)
	raidSlider.Low:Hide()
	raidSlider.High:Hide()
	raidSlider.text:Hide()
	raidSlider.Low.Show = raidSlider.Low.Hide
	raidSlider.High.Show = raidSlider.High.Hide

	
	local function SetIcon(self,type)
		if not type or type == 0 then
			self:SetAlpha(0)
		elseif type == 1 then
			self:SetTexCoord(0.5,0.5625,0.5,0.625)
			self:SetVertexColor(.8,0,0,1)
		elseif type == 2 then
			self:SetTexCoord(0.5625,0.625,0.5,0.625)
			self:SetVertexColor(0,.8,0,1)
		elseif type == 3 then
			self:SetTexCoord(0.625,0.6875,0.5,0.625)
			self:SetVertexColor(.8,.8,0,1)
		elseif type == 4 then
			self:SetTexCoord(0.875,0.9375,0.5,0.625)
			self:SetVertexColor(.8,.8,0,1)
		elseif type == -1 or type < 0 then
			if module.SetIconExtra then
				module.SetIconExtra(self,type)
			end
		end		
	end
	
	self.helpicons = {}
	for i=0,2 do
		local icon = self:CreateTexture(nil,"ARTWORK")
		icon:SetPoint("TOPLEFT",2,-20-i*12)
		icon:SetSize(14,14)
		icon:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128")
		SetIcon(icon,i+1)
		local t = ELib:Text(self,"",10):Point("LEFT",icon,"RIGHT",2,0):Size(0,16):Color(1,1,1)
		if i==0 then
			t:SetText(L.WACheckerMissingAura)
		elseif i==1 then
			t:SetText(L.WACheckerExistsAura)
		elseif i==2 then
			t:SetText(L.WACheckerPlayerHaveNotWA)
		end
		self.helpicons[i+1] = {icon,t}
	end

	self.filterEdit = ELib:Edit(self):Size(LINE_NAME_WIDTH,16):Point("BOTTOMLEFT",mainScroll,"TOPLEFT",-1,4):Tooltip(FILTER):OnChange(function(self,isUser)
		if not isUser then
			return
		end
		if self:GetText() == "" then
			Filter = nil
		else
			Filter = self:GetText():lower()
		end
		UpdatePage()
	end)

	local function LineName_OnClick(self,_,_,force)
		if IsShiftKeyDown() or force then
			local name, realm = UnitFullName("player")
			local fullName = name.."-"..realm
			local id = self:GetParent().db.data.id
			local link = "[WeakAuras: "..fullName.." - "..id.."]"
		
			local editbox = GetCurrentKeyBoardFocus()
			if(editbox) then
				editbox:Insert(link)
			else
				ChatFrame_OpenChat(link)
			end
		else
			local db = self:GetParent().db
			local id = db and db.data and db.data.id or "--"
			module:SendReq({[id]=true})
		end
	end
	local function LineName_ShareButton_OnEnter(self)
		if module.ShareButtonHover then
			module.ShareButtonHover(self)
		end
		self.background:SetVertexColor(1,1,0,1)
	end	
	local function LineName_ShareButton_OnLeave(self)
		if module.ShareButtonLeave then
			module.ShareButtonLeave(self)
		end
		self.background:SetVertexColor(1,1,1,0.7)
	end
	local function LineName_ShareButton_OnClick(self,...)
		if not module.ExportWA then
			LineName_OnClick(self:GetParent().name,nil,nil,true)
		else
			module.ShareButtonClick(self,...)
		end
	end	

	local function LineName_Icon_OnEnter(self)
		if self.HOVER_TEXT then
			ELib.Tooltip.Show(self,nil,self.HOVER_TEXT)
		end
		if module.IconHoverFunctions then
			for i=1,#module.IconHoverFunctions do
				module.IconHoverFunctions[i](self,true)
			end
		end
	end	
	local function LineName_Icon_OnLeave(self)
		if self.HOVER_TEXT then
			ELib.Tooltip.Hide()
		end
		if module.IconHoverFunctions then
			for i=1,#module.IconHoverFunctions do
				module.IconHoverFunctions[i](self,false)
			end
		end
	end	

	local lines = {}
	self.lines = lines
	for i=1,floor(PAGE_HEIGHT / LINE_HEIGHT) + 2 do
		local line = CreateFrame("Frame",nil,mainScroll.C)
		lines[i] = line
		line:SetPoint("TOPLEFT",0,-(i-1)*LINE_HEIGHT)
		line:SetPoint("TOPRIGHT",0,-(i-1)*LINE_HEIGHT)
		line:SetSize(0,LINE_HEIGHT)
		
		line.name = ELib:Text(line,"",10):Point("LEFT",2,0):Size(LINE_NAME_WIDTH-LINE_HEIGHT/2,LINE_HEIGHT):Color(1,1,1):Tooltip("ANCHOR_LEFT",true)
		line.name.TooltipFrame:SetScript("OnClick",LineName_OnClick)
		
		line.share = CreateFrame("Button",nil,line)
		line.share:SetPoint("LEFT",line.name,"RIGHT",0,0)
		line.share:SetSize(LINE_HEIGHT,LINE_HEIGHT)
		line.share:SetScript("OnEnter",LineName_ShareButton_OnEnter)
		line.share:SetScript("OnLeave",LineName_ShareButton_OnLeave)
		line.share:SetScript("OnClick",LineName_ShareButton_OnClick)
		line.share:RegisterForClicks("LeftButtonUp","RightButtonUp")
		
		line.share.background = line.share:CreateTexture(nil,"ARTWORK")
		line.share.background:SetPoint("CENTER")
		line.share.background:SetSize(LINE_HEIGHT,LINE_HEIGHT)
		line.share.background:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128")
		line.share.background:SetTexCoord(0.125+(0.1875 - 0.125)*4,0.1875+(0.1875 - 0.125)*4,0.5,0.625)
		line.share.background:SetVertexColor(1,1,1,0.7)
		
		line.icons = {}
		local iconSize = min(VERTICALNAME_WIDTH,LINE_HEIGHT)
		for j=1,VERTICALNAME_COUNT do
			local icon = line:CreateTexture(nil,"ARTWORK")
			line.icons[j] = icon
			icon:SetPoint("CENTER",line,"LEFT",LINE_NAME_WIDTH + 15 + VERTICALNAME_WIDTH*(j-1) + VERTICALNAME_WIDTH / 2,0)
			icon:SetSize(iconSize,iconSize)
			icon:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128")
			SetIcon(icon,(i+j)%4)

			icon.hoverFrame = CreateFrame("Frame",nil,line)
			icon.hoverFrame:Hide()
			icon.hoverFrame:SetAllPoints(icon)
			icon.hoverFrame:SetScript("OnEnter",LineName_Icon_OnEnter)
			icon.hoverFrame:SetScript("OnLeave",LineName_Icon_OnLeave)
		end
		
		line.t=line:CreateTexture(nil,"BACKGROUND")
		line.t:SetAllPoints()
		line.t:SetColorTexture(1,1,1,.05)
	end
	
	local function RaidNames_OnEnter(self)
		local t = self.t:GetText()
		if t ~= "" then
			ELib.Tooltip.Show(self,"ANCHOR_LEFT",t)
		end
	end
	
	local raidNames = CreateFrame("Frame",nil,self)
	for i=1,VERTICALNAME_COUNT do
		raidNames[i] = ELib:Text(raidNames,"RaidName"..i,10):Point("BOTTOMLEFT",mainScroll,"TOPLEFT",LINE_NAME_WIDTH + 15 + VERTICALNAME_WIDTH*(i-1),0):Color(1,1,1)

		local f = CreateFrame("Frame",nil,self)
		f:SetPoint("BOTTOMLEFT",mainScroll,"TOPLEFT",LINE_NAME_WIDTH + 15 + VERTICALNAME_WIDTH*(i-1),0)
		f:SetSize(VERTICALNAME_WIDTH,80)
		f:SetScript("OnEnter",RaidNames_OnEnter)
		f:SetScript("OnLeave",ELib.Tooltip.Hide)
		f.t = raidNames[i]
		
		local t=mainScroll:CreateTexture(nil,"BACKGROUND")
		raidNames[i].t = t
		t:SetPoint("TOPLEFT",LINE_NAME_WIDTH + 15 + VERTICALNAME_WIDTH*(i-1),0)
		t:SetSize(VERTICALNAME_WIDTH,PAGE_HEIGHT)
		if i%2==1 then
			t:SetColorTexture(.5,.5,1,.05)
			t.Vis = true
		end
	end
	local group = raidNames:CreateAnimationGroup()
	group:SetScript('OnFinished', function() group:Play() end)
	local rotation = group:CreateAnimation('Rotation')
	rotation:SetDuration(0.000001)
	rotation:SetEndDelay(2147483647)
	rotation:SetOrigin('BOTTOMRIGHT', 0, 0)
	--rotation:SetDegrees(90)
	rotation:SetDegrees(60)
	group:Play()
	
	local highlight_y = mainScroll.C:CreateTexture(nil,"BACKGROUND",nil,2)
	highlight_y:SetColorTexture(1,1,1,.2)
	local highlight_x = mainScroll:CreateTexture(nil,"BACKGROUND",nil,2)
	highlight_x:SetColorTexture(1,1,1,.2)
	
	local highlight_onupdate_maxY = (floor(PAGE_HEIGHT / LINE_HEIGHT) + 2) * LINE_HEIGHT
	local highlight_onupdate_minX = LINE_NAME_WIDTH + 15
	local highlight_onupdate_maxX = highlight_onupdate_minX + #raidNames * VERTICALNAME_WIDTH
	mainScroll.C:SetScript("OnUpdate",function(self)
		local x,y = ExRT.F.GetCursorPos(mainScroll)
		if y < 0 or y > PAGE_HEIGHT then
			highlight_x:Hide()
			highlight_y:Hide()
			return
		end	
		local x,y = ExRT.F.GetCursorPos(self)
		if y >= 0 and y <= highlight_onupdate_maxY then
			y = floor(y / LINE_HEIGHT)
			highlight_y:ClearAllPoints()
			highlight_y:SetAllPoints(lines[y+1])
			highlight_y:Show()
		else
			highlight_x:Hide()
			highlight_y:Hide()
			return
		end
		if x >= highlight_onupdate_minX and x <= highlight_onupdate_maxX then
			x = floor((x - highlight_onupdate_minX) / VERTICALNAME_WIDTH)
			highlight_x:ClearAllPoints()
			highlight_x:SetAllPoints(raidNames[x+1].t)
			highlight_x:Show()
		elseif x >= 0 and x <= (PAGE_WIDTH - 16) then
			highlight_x:Hide()
		else
			highlight_x:Hide()
			highlight_y:Hide()
		end
	end)
	
	local UpdateButton = ELib:Button(self,UPDATE):Point("TOPLEFT",mainScroll,"BOTTOMLEFT",-2,-5):Size(130,20):OnClick(function()
		module:SendReq()
	end)
	
	local function sortByName(a,b)
		if a and b and a.name and b.name then
			return a.name < b.name
		end
	end
	
	function UpdatePage()
		if not WeakAurasSaved then
			errorNoWA:Show()
			mainScroll:Hide()
			raidSlider:Hide()
			for i=1,#self.helpicons do
				self.helpicons[i][1]:SetAlpha(0)
				self.helpicons[i][2]:SetAlpha(0)
			end
			UpdateButton:Hide()
			raidNames:Hide()
			self.filterEdit:Hide()
			self.allIsHidden = true
			return
		end
		if self.allIsHidden then
			self.allIsHidden = false
			errorNoWA:Hide()
			mainScroll:Show()
			for i=1,#self.helpicons do
				self.helpicons[i][1]:SetAlpha(1)
				self.helpicons[i][2]:SetAlpha(1)
			end
			UpdateButton:Show()
			raidNames:Show()
		end
		
		local auras,auras2 = {},{}
		for WA_name,WA_data in pairs(WeakAurasSaved.displays) do
			local aura = auras2[WA_name]
			if aura then
				aura.name = WA_name
				aura.data = WA_data
			else
				aura = {
					name = WA_name,
					data = WA_data,
				}
			end
			if not Filter or WA_name:lower():find(Filter) then
				local parent = WA_data.parent
				if parent then
					local a = auras2[parent] or {}
					auras2[parent] = a
					a[#a+1] = aura
				else
					auras[#auras+1] = aura
				end
			end
			auras2[WA_name] = aura
		end
		if Filter then
			local inList = {}
			for i=1,#auras do
				inList[ auras[i] ] = true
			end
			for k,v in pairs(auras2) do
				if #v > 0 and not inList[v] and v.name then
					auras[#auras+1] = v
				end
			end
		end
		sort(auras,sortByName)
		for i=1,#auras do
			sort(auras[i],sortByName)
		end
		local sortedTable = {}
		if not Filter then
			sortedTable[#sortedTable+1] = {name="VERSION"}
		end
		for i=1,#auras do
			sortedTable[#sortedTable+1] = auras[i]
			for j=1,#auras[i] do
				sortedTable[#sortedTable+1] = auras[i][j]
				auras[i][j].isChild = true
			end
		end
		mainScroll.ScrollBar:Range(0,max(0,#sortedTable * LINE_HEIGHT - 1 - PAGE_HEIGHT),nil,true)
		
		local namesList,namesList2 = {},{}
		for _,name,_,class in ExRT.F.IterateRoster do
			namesList[#namesList + 1] = {
				name = name,
				class = class,
			}
		end
		sort(namesList,sortByName)
		
		if #namesList <= VERTICALNAME_COUNT then
			raidSlider:Hide()
			prevPlayerCol = 0
		else
			raidSlider:Show()
			raidSlider:Range(0,#namesList - VERTICALNAME_COUNT)
		end
		
		local raidNamesUsed = 0
		for i=1+prevPlayerCol,#namesList do
			raidNamesUsed = raidNamesUsed + 1
			if not raidNames[raidNamesUsed] then
				break
			end
			local name = ExRT.F.delUnitNameServer(namesList[i].name)
			raidNames[raidNamesUsed]:SetText(name)
			raidNames[raidNamesUsed]:SetTextColor(ExRT.F.classColorNum(namesList[i].class))
			namesList2[raidNamesUsed] = name
			if raidNames[raidNamesUsed].Vis then
				raidNames[raidNamesUsed]:SetAlpha(.05)
			end
		end
		for i=raidNamesUsed+1,#raidNames do
			raidNames[i]:SetText("")
			raidNames[i].t:SetAlpha(0)
		end
		
		local lineNum = 1
		local backgroundLineStatus = (prevTopLine % 2) == 1

		local myWAVER = WeakAuras.versionString

		for i=prevTopLine+1,#sortedTable do
			local aura = sortedTable[i]
			local line = lines[lineNum]
			if not line then
				break
			end
			line:Show()
			line.name:SetText((aura.isChild and "- " or "")..aura.name)
			line.db = aura
			line.t:SetShown(backgroundLineStatus)
			if i == 1 and aura.name == "VERSION" then
				line.share:Hide()
			else
				line.share:Show()
			end
			for j=1,VERTICALNAME_COUNT do
				local pname = namesList2[j] or "-"
				
				local db
				for name,DB in pairs(module.db.responces) do
					if name == pname or name:find("^"..pname) then
						db = DB
						break
					end
				end

				local hoverText
				
				if not db then
					SetIcon(line.icons[j],0)
				elseif db.noWA then
					SetIcon(line.icons[j],3)
				elseif aura.name == "VERSION" then
					hoverText = db.wa_ver or "NO DATA"
					SetIcon(line.icons[j],myWAVER == db.wa_ver and 2 or (db.wa_ver and 1) or 3)
				elseif type(db[ aura.name ]) == 'number' then
					SetIcon(line.icons[j],db[ aura.name ])
				elseif db[ aura.name ] then
					SetIcon(line.icons[j],2)
				else
					SetIcon(line.icons[j],1)
				end

				if module.ShowHoverIcons then
					line.icons[j].hoverFrame.HOVER_TEXT = nil
					line.icons[j].hoverFrame.name = pname
					line.icons[j].hoverFrame:Show()
				elseif hoverText then
					line.icons[j].hoverFrame.HOVER_TEXT = hoverText
					line.icons[j].hoverFrame:Show()
				else
					line.icons[j].hoverFrame.HOVER_TEXT = nil
					line.icons[j].hoverFrame:Hide()
				end
			end
			backgroundLineStatus = not backgroundLineStatus
			lineNum = lineNum + 1
		end
		for i=lineNum,#lines do
			lines[i]:Hide()
		end
	end
	self.UpdatePage = UpdatePage
	
	function self:OnShow()
		UpdatePage()
	end
end

function module:SendReq(ownList)
	local str = ""
	local c = 0
	if type(ownList) == "table" then
		for WA_name in pairs(ownList) do
			str = str..WA_name.."''"
			c = c + 1
		end
	else
		for WA_name,WA_data in pairs(WeakAurasSaved.displays) do
			str = str..WA_name.."''"
			c = c + 1
		end
	end
	str = str:gsub("''$","")

	if #str == 0 then
		return
	end

	local compressed = LibDeflate:CompressDeflate(str,{level = 7})
	local encoded = LibDeflate:EncodeForWoWAddonChannel(compressed)
	encoded = encoded .. "##F##"
	local parts = ceil(#encoded / 245)

	--print(#str,#encoded,parts,c)

	for i=1,parts do
		local msg = encoded:sub( (i-1)*245+1 , i*245 )
		if i == 1 then
			ExRT.F.SendExMsg("wac2", ExRT.F.CreateAddonMsg("G","H",msg))
		else
			ExRT.F.SendExMsg("wac2", ExRT.F.CreateAddonMsg("G",msg))
		end
	end
end

local SendRespSch = nil

function module:SendResp()
	SendRespSch = nil
	if not WeakAurasSaved then
		ExRT.F.SendExMsg("wachk", ExRT.F.CreateAddonMsg("R","NOWA"))
		return
	end
	ExRT.F.SendExMsg("wachk", ExRT.F.CreateAddonMsg("R","DATA",tostring(WeakAuras.versionString)))

	local isChanged = true
	local buffer,bufferStart = {},0
	local r,rNow = 0,0
	for i=1,#module.db.lastReq do
		if WeakAurasSaved.displays[ module.db.lastReq[i] ] then
			r = bit.bor(r,2^rNow)
		end
		rNow = rNow + 1
		isChanged = true
		if i % 32 == 0 then
			buffer[#buffer + 1] = r
			r = 0
			rNow = 0
			if #buffer == 19 then
				ExRT.F.SendExMsg("wachk", ExRT.F.CreateAddonMsg("R",bufferStart,unpack(buffer)))
				wipe(buffer)
				bufferStart = i
				isChanged = false
			end
		end
	end
	if isChanged then
		buffer[#buffer + 1] = r
		ExRT.F.SendExMsg("wachk", ExRT.F.CreateAddonMsg("R",bufferStart,unpack(buffer)))
	end
end

function module.main:ADDON_LOADED()
	module:RegisterAddonMessage()
end

local lastSenderTime,lastSender = 0

function module:addonMessage(sender, prefix, prefix2, ...)
	if prefix == "wachk" then
		if prefix2 == "G" then
			local time = GetTime()
			if lastSender ~= sender and (time - lastSenderTime) < 1.5 then
				return
			end
			lastSender = sender
			lastSenderTime = time
			local str1, str2 = ...
			if str1 == "H" and str2 then
				wipe(module.db.lastReq)
				str1 = str2
			end
			if not str1 then
				return
			end
			
			while str1:find("''") do
				local wa_name,o = str1:match("^(.-)''(.*)$")
			
				module.db.lastReq[#module.db.lastReq + 1] = wa_name
			
				str1 = o
			end
			
			module.db.lastReq[#module.db.lastReq + 1] = str1
			
			if not SendRespSch then
				SendRespSch = C_Timer.NewTimer(1,module.SendResp)
			end
		elseif prefix2 == "R" then
			local str1, str2 = ...
			module.db.responces[ sender ] = module.db.responces[ sender ] or {}
			if str1 == "NOWA" then
				module.db.responces[ sender ].noWA = true
				return
			elseif str1 == "DATA" then
				local _, wa_ver = ...
				module.db.responces[ sender ].wa_ver = wa_ver

				if module.options:IsVisible() and module.options.UpdatePage then
					module.options.UpdatePage()
				end
				return
			end
			local start = tonumber(str1 or "?")
			if not start then
				return
			end
			module.db.responces[ sender ].noWA = nil
			for j=2,select("#", ...) do
				local res = tonumber(select(j, ...),nil)
				
				for i=1,32 do
					if not module.db.lastReq[i + start] then
						break
					elseif bit.band(res,2^(i-1)) > 0 then
						module.db.responces[ sender ][ module.db.lastReq[i + start] ] = true
					else
						module.db.responces[ sender ][ module.db.lastReq[i + start] ] = false
					end
				end
				
				start = start + 32
			end
			
			if module.options:IsVisible() and module.options.UpdatePage then
				module.options.UpdatePage()
			end
		end
	elseif prefix == "wac2" then
		if prefix2 == "G" then
			local time = GetTime()
			if lastSender ~= sender and (time - lastSenderTime) < 1.5 then
				return
			end
			lastSender = sender
			lastSenderTime = time
			if ... == "H" then
				wipe(module.db.lastReq)
				module.db.syncStr = ""
			end

			local str = table.concat({select(... == "H" and 2 or 1,...)}, "\t")
			module.db.syncStr = module.db.syncStr or ""
			module.db.syncStr = module.db.syncStr .. str
			if module.db.syncStr:find("##F##$") then
				local str = module.db.syncStr:sub(1,-6)
				module.db.syncStr = nil
		
				local decoded = LibDeflate:DecodeForWoWAddonChannel(str)
				local decompressed = LibDeflate:DecompressDeflate(decoded)
		
				while decompressed:find("''") do
					local wa_name,o = decompressed:match("^(.-)''(.*)$")
				
					module.db.lastReq[#module.db.lastReq + 1] = wa_name
				
					decompressed = o
				end
				
				module.db.lastReq[#module.db.lastReq + 1] = decompressed
			
				module:SendResp()
			end
		end
	end
end