local GlobalAddonName, ExRT = ...

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

local LibDeflate = LibStub:GetLibrary("LibDeflate")

local UnitGroupRolesAssigned = UnitGroupRolesAssigned or ExRT.NULLfunc

local VMRT = nil

function module.main:ADDON_LOADED()
	VMRT = _G.VMRT

	VMRT.RaidGroups = VMRT.RaidGroups or {}
	VMRT.RaidGroups.profiles = VMRT.RaidGroups.profiles or {}

	if not VMRT.RaidGroups.upd4550 then
		VMRT.RaidGroups.KeepPosInGroup = true
		VMRT.RaidGroups.upd4550 = true
	end

	if not VMRT.RaidGroups.SplitGroups then
		VMRT.RaidGroups.SplitGroups = {true,true,true,true}
	end
	if not VMRT.RaidGroups.SplitParts then
		VMRT.RaidGroups.SplitParts = 2
	end

	module:RegisterSlash()
end

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

	local function EditOnDragStart(self)
		if self:IsMovable() then
			self:StartMoving()
		end	  
	end
	local function EditOnDragStop(self)
		self:StopMovingOrSizing()

		for i=1,#module.options.edits do
			local edit2 = module.options.edits[i]
			if edit2:IsMouseOver() and edit2 ~= self then
				local textSelf = self:GetText()
				local textMO = edit2:GetText()
				self:SetText(textMO)
				edit2:SetText(textSelf)
				self:SetCursorPosition(1)
				edit2:SetCursorPosition(1)

				module.options:SaveCurrentSave()
				break
			end
		end

		self:NewPoint("TOPLEFT",module.options,10+((self.index-1) % 10 < 5 and 0 or 165),-40-((self.index-1) % 5)*22-floor((self.index-1)/10)*132) 

		self:ClearFocus() 
	end
	local function EditOnDragStopNotInList(self)
		self:StopMovingOrSizing()

		for i=1,#module.options.edits do
			local edit2 = module.options.edits[i]
			if edit2:IsMouseOver() and edit2 ~= self then
				local textSelf = self.preName or self:GetText()
				edit2:SetText(textSelf)
				edit2:SetCursorPosition(1)

				module.options:SaveCurrentSave()
				break
			end
		end

		self:NewPoint("TOPLEFT",module.options,355,-40-(self.index-1)*14) 

		self:ClearFocus() 

		module.options:UpdateNotInList()
	end
	local function EditOnChange(self,isUser)
		local name = self:GetText()
		if name and UnitName(name) then
			local r,g,b = ExRT.F.classColorNum(select(2,UnitClass(name)))
			self:SetTextColor(r,g,b,1)
			self:ColorBorder()
		elseif self.preclass then
			local r,g,b = ExRT.F.classColorNum(self.preclass)
			self:SetTextColor(r,g,b,1)
			self:ColorBorder()
		else
			self:SetTextColor(.7,.7,.7,1)
			if name and #name > 0 then
				self:ColorBorder(0.5,0.25,0.3,1)
			else
				self:ColorBorder()
			end
		end
		if name and UnitName(name) and UnitGroupRolesAssigned(name) then
			local role = UnitGroupRolesAssigned(name)
			if role == "HEALER" then
				self.roleIcon:SetAtlas("groupfinder-icon-role-large-heal")
				self.roleIcon:Show()
			elseif role == "DAMAGER" then
				self.roleIcon:SetAtlas("groupfinder-icon-role-large-dps")
				self.roleIcon:Show()
			elseif role == "TANK" then
				self.roleIcon:SetAtlas("groupfinder-icon-role-large-tank")
				self.roleIcon:Show()
			else
				self.roleIcon:Hide()
			end
		else
			self.roleIcon:Hide()
		end
		if isUser then
			module.options:UpdateNotInList()

			module.options:SaveCurrentSave()
		end
	end

	function self:UpdateTextColors()
		for i=1,40 do
			EditOnChange(self.edits[i],false)
		end
	end

	self.edits = {}
	for i=1,40 do
		local edit = ELib:Edit(self):Size(150,18)
		self.edits[i] = edit

		edit:SetText(i)
		edit.index = i

		edit:Point("TOPLEFT",10+((i-1) % 10 < 5 and 0 or 165),-40-((i-1) % 5)*22-floor((i-1)/10)*132)
		edit:OnChange(EditOnChange)

		edit.roleIcon = edit:CreateTexture(nil, "BACKGROUND")
		edit.roleIcon:SetPoint("RIGHT")
		edit.roleIcon:SetSize(16,16)
		edit.roleIcon:Hide()

		edit:SetMovable(true)
		edit:RegisterForDrag("LeftButton")
		edit:SetScript("OnDragStart", EditOnDragStart)
		edit:SetScript("OnDragStop", EditOnDragStop)
	end
	for i=1,4 do
		for j=1,2 do
			ELib:Text(self,GROUP.." "..((i-1)*2+j)):Point("TOPLEFT",10+(j == 1 and 0 or 165)+3,-40-(i-1)*132+14):Color():Shadow()
		end
	end

	ELib:Text(self,L.RaidGroupsTextMovable,8):Point("TOPLEFT",12,-547):Color():Shadow()

	self.scrollNotInList = ELib:ScrollBar(self):Point("TOPLEFT",478,-40):Size(12,558):SetMinMaxValues(0,1):SetValue(0):SetObey(true):OnChange(function()
		self:UpdateNotInList()
	end)

	self.edits_notinlist = {}
	function self:UpdateNotInList()
		local inList,notInList = {},{}
		local scrollShow
		for i=1,40 do 
			local name = module.options.edits[i]:GetText()
			if name then
				inList[name] = true
			end
		end
		if self.isGuild then
			local myLvl = UnitLevel("player")
			local guildList = {}
			for i=1, GetNumGuildMembers() do
				local name, _, rankIndex, level, _, _, _, _, _, _, class = GetGuildRosterInfo(i)
				if select(2,strsplit("-",name)) == ExRT.SDB.realmKey then
					name = strsplit("-",name)
				end
				if not inList[name] and (level or 0) >= myLvl then
					guildList[#guildList+1] = {name,class,rankIndex,"|cffbbbbbb["..rankIndex.."]|r "..name}
				end
			end
			sort(guildList,function(a,b)
				if a[3] == b[3] then
					return a[1] < b[1]
				else
					return a[3] < b[3]
				end
			end)
			local start = floor(self.scrollNotInList:GetValue()+0.5)
			for i=start+1,#guildList do
				notInList[#notInList+1] = guildList[i]
			end
			if #guildList > 40 then
				scrollShow = #guildList - 40
			end
		else
			for i=1,GetNumGroupMembers() do
				local name = GetRaidRosterInfo(i)
				if not inList[name] then
					if not name:find("%-") or not inList[strsplit("-",name)] then
						notInList[#notInList+1] = name
					end
				end
			end
		end
		if scrollShow then
			self.scrollNotInList:SetMinMaxValues(0,scrollShow)
		end
		self.scrollNotInList:SetShown(scrollShow and true or false)

		for i=1,min(#notInList,40) do
			local edit = self.edits_notinlist[i]
			if not edit then
				edit = ELib:Edit(self):Size(120,12)
				self.edits_notinlist[i] = edit
		
				edit.index = i
				edit:Point("TOPLEFT",355,-40-(i-1)*14):OnChange(EditOnChange)

				edit.roleIcon = edit:CreateTexture(nil, "BACKGROUND")
				edit.roleIcon:SetPoint("RIGHT")
				edit.roleIcon:SetSize(10,10)
				edit.roleIcon:Hide()

				edit:SetFont(edit:GetFont(),10,"")
				edit:SetEnabled(false)
		
				edit:SetMovable(true)
				edit:RegisterForDrag("LeftButton")
				edit:SetScript("OnDragStart", EditOnDragStart)
				edit:SetScript("OnDragStop", EditOnDragStopNotInList)

			end
			if type(notInList[i]) == "table" then
				edit:SetText("")
				edit:SetText(notInList[i][4])
				edit.preclass = notInList[i][2]
				edit.preName = notInList[i][1]
			else
				edit:SetText("")
				edit:SetText(notInList[i])
				edit.preclass = nil
				edit.preName = nil
			end
			edit:SetCursorPosition(1)
			edit:Show()
		end
		for i=#notInList+1,#self.edits_notinlist do 
			self.edits_notinlist[i]:Hide()
		end
	end

	self.chk_raid = ELib:Radio(self,RAID,true):Point("TOPLEFT",355,-20):OnClick(function(self)
		module.options.isGuild = false
		self:SetChecked(true)
		module.options.chk_guild:SetChecked(false)
		module.options:UpdateNotInList()
	end)
	self.chk_guild = ELib:Radio(self,GUILD):Point("BOTTOM",self.chk_raid,"TOP",0,-2):OnClick(function(self)
		module.options.isGuild = true
		self:SetChecked(true)
		module.options.chk_raid:SetChecked(false)
		C_GuildInfo.GuildRoster()
		module.options:UpdateNotInList()
		C_Timer.After(1,function()
			module.options:UpdateNotInList()
		end)
	end)

	self.updateRoster = ELib:Button(self,L.RaidGroupsCurrentRoster):Size(ExRT.isClassic and not ExRT.isCata and 315 or 155,20):Point("TOPLEFT",10,-565):OnClick(function() 
		module.options:ResetCurrentSave()
	
		local roster = {}
		for i=1,8 do roster[i] = {} end

		for i=1,GetNumGroupMembers() do
			local name, rank, subgroup = GetRaidRosterInfo(i)
			tinsert(roster[subgroup],name)
		end

		for i=1,8 do 
			for j=1,5 do
				local edit = module.options.edits[(i-1)*5+j]
				edit:SetText(roster[i][j] or "")
				edit:SetCursorPosition(1)
			end
		end

		module.options:UpdateNotInList()
	end) 

	self.splitRoster = ELib:Button(self,L.RaidGroupsSplitRoster):Size(135,20):Point("LEFT",self.updateRoster,"RIGHT",5,0):Shown(not ExRT.isClassic or ExRT.isCata):OnClick(function() 
		module.options:SplitRoster()
	end)

	self.splitRosterOptions = ELib:DropDownButton(self,"|TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:14:14:0:0:256:128:16:32:64:80|t",200,-1):Size(20,20):Point("LEFT",self.splitRoster,"RIGHT",0,0):Shown(not ExRT.isClassic or ExRT.isCata)
	do
		local gfs = {}
		local function gfs_check(_,g)
			VMRT.RaidGroups.SplitGroups[g] = not VMRT.RaidGroups.SplitGroups[g]
			ELib.ScrollDropDown.UpdateChecks()
		end
		for i=1,8 do
			gfs[#gfs+1] = {
				text = (GROUP or "Group").." "..i,
				checkable = true,
				func = gfs_check,
				arg1 = i,
			}
		end

		local nfs = {}
		local function nfs_check(_,g)
			VMRT.RaidGroups.SplitParts = g
			ELib.ScrollDropDown.UpdateChecks()
		end
		for i=1,8 do
			nfs[#nfs+1] = {
				text = i,
				radio = true,
				func = nfs_check,
				arg1 = i,
			}
		end

		local sr = {}
		local function sr_check(_,g)
			VMRT.RaidGroups.SplitRule = g
			ELib.ScrollDropDown.UpdateChecks()
		end
		sr[#sr+1] = {
			text = "1,2,3,4 vs 5,6,7,8",
			radio = true,
			func = sr_check,
			arg1 = 1,
		}
		sr[#sr+1] = {
			text = "1,3,5,7 vs 2,4,6,8",
			radio = true,
			func = sr_check,
			arg1 = 2,
		}

		self.splitRosterOptions.List = {
			{
				text = L.RaidGroupsSplitGroups,
				subMenu = gfs,
			},
			{
				text = L.RaidGroupsSplitParts,
				subMenu = nfs,
			},
			{
				text = L.RaidGroupsSplitRule,
				subMenu = sr,
			},
			{ text = CLOSE, func = function() ELib.ScrollDropDown.Close() end, notCheckable = true },
		}
		function self.splitRosterOptions:PreUpdate()
			for i=1,#gfs do
				local line = gfs[i]
				line.checkState = VMRT.RaidGroups.SplitGroups[i]
			end
			for i=1,#nfs do
				local line = nfs[i]
				line.checkState = i == VMRT.RaidGroups.SplitParts
				if line.checkState then
					line.colorCode = "|cff00ff00"
				else
					line.colorCode = nil
				end
			end
			for i=1,#sr do
				local line = sr[i]
				line.checkState = (i == VMRT.RaidGroups.SplitRule) or (i==1 and not VMRT.RaidGroups.SplitRule)
				if line.checkState then
					line.colorCode = "|cff00ff00"
				else
					line.colorCode = nil
				end
			end
		end
		function self.splitRosterOptions:additionalToggle()
			self:PreUpdate()
		end
	end

	local splitClassPrio = {
		[1]=15,--WARRIOR
		[2]=14,--PALADIN
		[4]=13,--ROGUE
		[6]=12,--DEATHKNIGHT
		[12]=11,--DEMONHUNTER
		[7]=10,--SHAMAN
		[10]=9,--MONK
		[11]=8,--DRUID
		[3]=7,--HUNTER
		[5]=6,--PRIEST
		[8]=5,--MAGE
		[9]=4,--WARLOCK
		[13]=3,--EVOKER
		[14]=1,
		[15]=2,
	}

	function module.options:SplitRoster()
		local GROUPS_OPT = VMRT.RaidGroups.SplitGroups
		local SPLIT_MAX = VMRT.RaidGroups.SplitParts

		local roster = {}
		local groups_opted_max = 0
		for i=1,8 do 
			if GROUPS_OPT[i] then
				groups_opted_max = groups_opted_max + 1
				for j=1,5 do
					local edit = module.options.edits[(i-1)*5+j]
					local name = edit:GetText()
					if name and name:trim() ~= "" then
						local prio = 0
						if UnitName(name) then
							prio = prio + 100
						end
						local role = UnitGroupRolesAssigned(name)
						if role == "TANK" then
							prio = prio + 60
						elseif role == "DAMAGER" then
							prio = prio + 40
						elseif role == "HEALER" then
							prio = prio + 20
						end
						local classNum = select(3,UnitClass(name))
						if classNum then
							prio = prio + splitClassPrio[classNum] or classNum
						end
						roster[#roster+1] = {name = name, prio = prio}
					end
				end
			end
		end

		if (groups_opted_max / SPLIT_MAX) % 1 ~= 0 then
			print('Groups are imbalanced, sorting is impossible.')
			print('Change number of parts or number of affected groups.')
			return
		end

		sort(roster,function(a,b) if a.prio == b.prio then return a.name < b.name else return a.prio > b.prio end end)

		local splits = {}
		local groups_opted_num = 0
		for i=1,8 do 
			if GROUPS_OPT[i] then
				groups_opted_num = groups_opted_num + 1
				local split_num
				if VMRT.RaidGroups.SplitRule == 2 then
					split_num = ((groups_opted_num - 1) % SPLIT_MAX) + 1
				else
					split_num = floor((groups_opted_num - 1) / ceil(groups_opted_max / SPLIT_MAX)) + 1
				end
				if not splits[split_num] then
					splits[split_num] = { groups = {},curr = 1 }
				end
				tinsert(splits[split_num].groups, i)
			end
		end

		--DevTools_Dump(splits)

		local pos = 1
		for i=1,groups_opted_num*5 do
			local s = splits[pos]

			local group = s.groups[ floor((s.curr - 1) / 5) + 1 ]
			local gpos = ((s.curr - 1) % 5) + 1

			local edit = module.options.edits[(group-1)*5+gpos]
			edit:SetText(roster[i] and roster[i].name or "")
			edit:SetCursorPosition(1)

			s.curr = s.curr + 1

			pos = pos + 1
			if pos > SPLIT_MAX then
				pos = 1
			end
		end

		for _,s in pairs(splits) do
			for i=s.curr,#s.groups*5 do
				local group = s.groups[ floor((i - 1) / 5) + 1 ]
				local gpos = ((i - 1) % 5) + 1

				local edit = module.options.edits[(group-1)*5+gpos]
				edit:SetText("")
				edit:SetCursorPosition(1)
			end
		end

		module.options:UpdateNotInList()
	end

	self.processRoster = ELib:Button(self,L.RaidGroupsSetGroups):Size(315,30):Point("TOPLEFT",self.updateRoster,"BOTTOMLEFT",0,-5):OnClick(function(self) 
		local list = {}
		for i=1,40 do
			list[i] = module.options.edits[i]:GetText()
		end
		module:ApplyGroups(list)
	end) 

	self.keepPosCheck = ELib:Check(self,L.RaidGroupsKeepPosInGroup,VMRT.RaidGroups.KeepPosInGroup):Point("BOTTOMLEFT",self.processRoster,"BOTTOMRIGHT",5,0):OnClick(function(self) 
		if self:GetChecked() then
			VMRT.RaidGroups.KeepPosInGroup = true
		else
			VMRT.RaidGroups.KeepPosInGroup = nil
		end
	end):Shown(ExRT.isClassic)

	self.presetList = ELib:ScrollList(self):Size(190,505-25):Point("TOPRIGHT",-10,-40):AddDrag()
	ELib:Text(self,L.RaidGroupsQuickLoad..":"):Point("BOTTOMLEFT",self.presetList,"TOPLEFT",5,3):Color():Shadow()

	self.presetList.ButtonRemove = CreateFrame("Button",nil,self.presetList)
	self.presetList.ButtonRemove:Hide()
	self.presetList.ButtonRemove:SetSize(8,8)
	self.presetList.ButtonRemove:SetScript("OnClick",function(self)
		for i=#VMRT.RaidGroups.profiles,1,-1 do
			local entry = VMRT.RaidGroups.profiles[i]
			if entry == self.data then
				tremove(VMRT.RaidGroups.profiles,i)
				break
			end
		end
		module.options:UpdateList()
		module.options:ResetCurrentSave()
	end)
	self.presetList.ButtonRemove:SetScript("OnEnter",function(self)
		GameTooltip:SetOwner(self,"ANCHOR_RIGHT")
		GameTooltip:AddLine(DELETE)
		GameTooltip:Show()
	end)
	self.presetList.ButtonRemove:SetScript("OnLeave",function(self)
		GameTooltip_Hide()
	end)
	self.presetList.ButtonRemove.i = self.presetList.ButtonRemove:CreateTexture()
	self.presetList.ButtonRemove.i:SetPoint("CENTER")
	self.presetList.ButtonRemove.i:SetSize(16,16)	
	self.presetList.ButtonRemove.i:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128")
	self.presetList.ButtonRemove.i:SetTexCoord(0.5,0.5625,0.5,0.625)
	self.presetList.ButtonRemove.i:SetVertexColor(.8,0,0,1)

	function self:ResetCurrentSave()
		module.options.CURR_SAVE = nil
		module.options.presetList.selected = nil
		module.options.presetList:Update()
	end
	function self:SaveCurrentSave()
		if not module.options.CURR_SAVE then
			return
		end
		local new = module.options:CreateCurrentData()
		for i=1,40 do
			module.options.CURR_SAVE[i] = new[i]
		end
		module.options.CURR_SAVE.time = new.time
	end
	function self:SetCurrentSave(data)
		if not data then
			self:ResetCurrentSave()
			return
		end
		module.options.CURR_SAVE = data

		module.options.presetList.selected = nil
		for i=1,#module.options.presetList.L do
			if module.options.presetList.L[i][2] == data then
				module.options.presetList.selected = i
				break
			end
		end
		if not module.options.presetList.selected then
			module.options.CURR_SAVE = nil
		end
		module.options:UpdateList()
	end

	function self.presetList:SetListValue(index)
		local data = self.L[index][2]
		for i=1,40 do 
			local edit = module.options.edits[i]
			if data[i] then
				edit:SetText(data[i])
				edit:SetCursorPosition(1)
			else
				edit:SetText("")
			end
		end
		module.options:UpdateNotInList()

		module.options.LAST_CLICK = index
		if not VMRT.RaidGroups.SaveCurrent then
			module.options.presetList.selected = nil
		else
			module.options.CURR_SAVE = data
		end
		module.options.presetList:Update()	
	end
	function self.presetList:HoverListValue(isEnter,index,obj)
		if not isEnter then
			GameTooltip_Hide()

			if not self.ButtonRemove:IsMouseOver() then
				self.ButtonRemove:Hide()
			end
		else
			local data = obj.table[2]

			GameTooltip:SetOwner(obj,"ANCHOR_CURSOR")
			GameTooltip:AddLine(data.name)
			GameTooltip:AddLine(date("%x %X",data.time))
			GameTooltip:Show()

			self.ButtonRemove.data = data
			self.ButtonRemove:SetPoint("RIGHT",obj,"RIGHT",-5,0)
			self.ButtonRemove:SetParent(obj)
			self.ButtonRemove:Show()
		end
	end
	function self.presetList:OnDragFunction(obj1,obj2)
		local data1,data2 = obj1.table[2],obj2.table[2]

		local tmp
		if obj1.index < obj2.index then
			for i=#VMRT.RaidGroups.profiles,1,-1 do
				if VMRT.RaidGroups.profiles[i] == data1 then
					tmp = VMRT.RaidGroups.profiles[i]
					VMRT.RaidGroups.profiles[i] = VMRT.RaidGroups.profiles[i-1]
				elseif VMRT.RaidGroups.profiles[i] == data2 then
					VMRT.RaidGroups.profiles[i] = tmp
					break
				elseif tmp then
					VMRT.RaidGroups.profiles[i] = VMRT.RaidGroups.profiles[i-1]
				end
			end
		else
			for i=1,#VMRT.RaidGroups.profiles do
				if VMRT.RaidGroups.profiles[i] == data1 then
					tmp = VMRT.RaidGroups.profiles[i]
					VMRT.RaidGroups.profiles[i] = VMRT.RaidGroups.profiles[i+1]
				elseif VMRT.RaidGroups.profiles[i] == data2 then
					VMRT.RaidGroups.profiles[i] = tmp
					break
				elseif tmp then
					VMRT.RaidGroups.profiles[i] = VMRT.RaidGroups.profiles[i+1]
				end
			end
		end

		module.options:UpdateList()
	end	

	function self:UpdateList()
		local list = {}

		for i=#VMRT.RaidGroups.profiles,1,-1 do
			local entry = VMRT.RaidGroups.profiles[i]
			list[#list+1] = {entry.name,entry}
		end

		module.options.presetList.L = list
		module.options.presetList:Update()
	end

	function self:CreateCurrentData()
		local new = {
			time = time(),
		}
		for i=1,40 do 
			local text = module.options.edits[i]:GetText()
			if text and string.trim(text) ~= "" then
				new[i] = text
			end
		end

		return new
	end

	local function SaveData(_,name)
		if not name or string.trim(name) == "" then
			return
		end

		local new = module.options:CreateCurrentData()
		new.name = name
		VMRT.RaidGroups.profiles[#VMRT.RaidGroups.profiles+1] = new

		module.options:UpdateList()
		module.options:SetCurrentSave(new)
	end

	local function SaveInputOnEdit(self)
		local text = self:GetText()
		if text and string.trim(text) ~= "" then
			self:GetParent().OK:Enable()
		else
			self:GetParent().OK:Disable()
		end	
	end

	self.chkSaveCurrent = ELib:Check(self,L.RaidGroupsSaveCurrent,VMRT.RaidGroups.SaveCurrent):Point("TOPLEFT",self.presetList,"BOTTOMLEFT",0,-5):Tooltip(L.RaidGroupsSaveCurrentTip):OnClick(function(self) 
		if self:GetChecked() then
			VMRT.RaidGroups.SaveCurrent = true
		else
			VMRT.RaidGroups.SaveCurrent = nil
			module.options:ResetCurrentSave()
		end
	end)  

	self.presetListSave = ELib:Button(self,L.RaidGroupsSave):Size(192,20):Point("TOPLEFT",self.chkSaveCurrent,"BOTTOMLEFT",0,-5):OnClick(function(self) 
		ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveData,nil,nil,nil,SaveInputOnEdit)		
	end)

	self.importWindow, self.exportWindow = ExRT.F.CreateImportExportWindows()

	do
		self.importWindow:SetHeight(180)
		self.importWindow.Edit:Point("TOP",0,-100)

		ELib:DecorationLine(self.importWindow):Point("TOPLEFT",0,-17):Point("BOTTOMRIGHT",'x',"TOPRIGHT",0,-18)
		ELib:DecorationLine(self.importWindow):Point("TOPLEFT",0,-95):Point("BOTTOMRIGHT",'x',"TOPRIGHT",0,-96)

		local importWindow = self.importWindow
		local function ButtonOnClick(self)
			local index = 1
			local c = importWindow["c"..index]
			while c do
				c:Enable()
				if self == c then
					importWindow.importType = index
					VMRT.RaidGroups.importType = index
				end
				index = index + 1
				c = importWindow["c"..index]
			end

			self:Disable()
		end

		local function CreateButton()
			local self = ELib:Template("ExRTButtonTransparentTemplate",importWindow)
			self:SetSize(importWindow:GetWidth()/4,77)

			self.DisabledTexture = self:CreateTexture()
			self.DisabledTexture:SetColorTexture(0.20,0.21,0.25,0.5)
			self.DisabledTexture:SetPoint("TOPLEFT")
			self.DisabledTexture:SetPoint("BOTTOMRIGHT")
			self:SetDisabledTexture(self.DisabledTexture)

			self:SetScript("OnClick",ButtonOnClick)
		  
			return self
		end

		importWindow.c1 = CreateButton()
		importWindow.c1:SetPoint("TOPLEFT",0,-18)
		importWindow.c1:SetText("G1    G2\nG3    G4\nG5    G6\nG7    G8")

		importWindow.c2 = CreateButton()
		importWindow.c2:SetPoint("LEFT",importWindow.c1,"RIGHT",0,0)
		importWindow.c2:SetText("G1   G2   G3   G4   G5")

		importWindow.c3 = CreateButton()
		importWindow.c3:SetPoint("LEFT",importWindow.c2,"RIGHT",0,0)
		importWindow.c3:SetText("G1\nG2\nG3\nG4\nG5")

		importWindow.c4 = CreateButton()
		importWindow.c4:SetPoint("LEFT",importWindow.c3,"RIGHT",0,0)
		importWindow.c4:SetText("From ExRT export string")

		local importType = VMRT.RaidGroups.importType or 4
		local c = importWindow["c"..importType]
		if c then
			c:Click()
		end
	end

	do
		self.exportWindow:SetHeight(130)
		self.exportWindow.Edit:Point("TOP",0,-100)

		ELib:DecorationLine(self.exportWindow):Point("TOPLEFT",0,-17):Point("BOTTOMRIGHT",'x',"TOPRIGHT",0,-18)
		ELib:DecorationLine(self.exportWindow):Point("TOPLEFT",0,-95):Point("BOTTOMRIGHT",'x',"TOPRIGHT",0,-96)

		local exportWindow = self.exportWindow
		local function ButtonOnClick(self)
			local index = 1
			local c = exportWindow["c"..index]
			while c do
				c:Enable()
				if self == c then
					exportWindow.exportType = index
					VMRT.RaidGroups.exportType = index
				end
				index = index + 1
				c = exportWindow["c"..index]
			end

			self:Disable()

			module.options:RecordToText()
			if exportWindow:IsShown() then
				exportWindow.Edit:SetFocus()
				exportWindow.Edit:HighlightText()
			end
		end

		local function CreateButton()
			local self = ELib:Template("ExRTButtonTransparentTemplate",exportWindow)
			self:SetSize(exportWindow:GetWidth()/4,77)

			self.DisabledTexture = self:CreateTexture()
			self.DisabledTexture:SetColorTexture(0.20,0.21,0.25,0.5)
			self.DisabledTexture:SetPoint("TOPLEFT")
			self.DisabledTexture:SetPoint("BOTTOMRIGHT")
			self:SetDisabledTexture(self.DisabledTexture)

			self:SetScript("OnClick",ButtonOnClick)
		  
			return self
		end

		exportWindow.c1 = CreateButton()
		exportWindow.c1:SetPoint("TOPLEFT",0,-18)
		exportWindow.c1:SetText("G1    G2\nG3    G4\nG5    G6\nG7    G8")

		exportWindow.c2 = CreateButton()
		exportWindow.c2:SetPoint("LEFT",exportWindow.c1,"RIGHT",0,0)
		exportWindow.c2:SetText("G1   G2   G3   G4   G5")

		exportWindow.c3 = CreateButton()
		exportWindow.c3:SetPoint("LEFT",exportWindow.c2,"RIGHT",0,0)
		exportWindow.c3:SetText("G1\nG2\nG3\nG4\nG5")

		exportWindow.c4 = CreateButton()
		exportWindow.c4:SetPoint("LEFT",exportWindow.c3,"RIGHT",0,0)
		exportWindow.c4:SetText("MRT export string")

		local exportType = VMRT.RaidGroups.exportType or 4
		local c = exportWindow["c"..exportType]
		if c then
			c:Disable()
		end
		exportWindow.exportType = exportType
	end

	local function SaveDataFromTable(rec,name)
		if not name or string.trim(name) == "" then
			return
		end
		rec.name = name
		rec.time = time()

		VMRT.RaidGroups.profiles[#VMRT.RaidGroups.profiles+1] = rec

		module.options:UpdateList()
		module.options:ResetCurrentSave()
	end

	function self.importWindow:ImportFunc(str)
		if self.importType == 4 then
			local headerLen = str:sub(1,4) == "EXRT" and 8 or 7

			local header = str:sub(1,headerLen)
			if (header:sub(1,headerLen-1) ~= "EXRTRGR" and header:sub(1,headerLen-1) ~= "MRTRGR") or (header:sub(headerLen,headerLen) ~= "0" and header:sub(headerLen,headerLen) ~= "1") then
				StaticPopupDialogs["EXRT_RAIDGROUP_IMPORT"] = {
					text = "|cffff0000"..ERROR_CAPS.."|r "..L.ProfilesFail3,
					button1 = OKAY,
					timeout = 0,
					whileDead = true,
					hideOnEscape = true,
					preferredIndex = 3,
				}
				StaticPopup_Show("EXRT_RAIDGROUP_IMPORT")
				return
			end
	
			module.options:TextToRecord(str:sub(headerLen+1),header:sub(headerLen,headerLen)=="0")
		elseif self.importType == 1 then
			local res = {}

			local group = 1
			local posInGroup = 0

			local lines = {strsplit("\n",str)}
			for i=1,#lines do
				local left,right = strsplit(" ",lines[i]:gsub(" +"," "):gsub('"',''),nil)
				if left and left:trim() ~= "" then
					left = left:trim()
				else
					left = nil
				end
				if right and right:trim() ~= "" then
					right = right:trim()
				else
					right = nil
				end
				posInGroup = posInGroup + 1
				if posInGroup > 5 and (left or right) then
					posInGroup = 1
					group = group + 2
				end
				if posInGroup <= 5 and group <= 8 then
					res[(group-1)*5+posInGroup] = left
					res[(group-0)*5+posInGroup] = right
				end
			end
			ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveDataFromTable,res,nil,nil,SaveInputOnEdit)
		elseif self.importType == 3 then
			local res = {}

			local group = 1
			local posInGroup = 0

			local lines = {strsplit("\n",str)}
			for i=1,#lines do
				local left = lines[i]
				if left and left:trim() ~= "" then
					left = left:trim():gsub('"','')
				else
					left = nil
				end
				posInGroup = posInGroup + 1
				if posInGroup > 5 and left then
					posInGroup = 1
					group = group + 1
				end
				if posInGroup <= 5 and group <= 8 then
					res[(group-1)*5+posInGroup] = left
				end
			end
			ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveDataFromTable,res,nil,nil,SaveInputOnEdit)
		elseif self.importType == 2 then
			local res = {}

			local lines = {strsplit("\n",str)}
			for i=1,#lines do
				local group = 0

				local name,line = strsplit(" ",lines[i]:gsub(" +"," "):gsub('"',''),2)
				while name do
					group = group + 1
					if i <= 5 and group <= 8 then
						res[(group-1)*5+i] = name:trim()
					end
					if line then
						name,line = strsplit(" ",line,2)
					else
						break
					end
				end
			end
			ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveDataFromTable,res,nil,nil,SaveInputOnEdit)
		end
	end


	function self:TextToRecord(str,uncompressed)
		local decoded = LibDeflate:DecodeForPrint(str)
		local decompressed
		if uncompressed then
			decompressed = decoded
		else
			decompressed = LibDeflate:DecompressDeflate(decoded)
		end
		decoded = nil

		local _,tableData = strsplit(",",decompressed,2)
		decompressed = nil
	
		local successful, res = pcall(ExRT.F.TextToTable,tableData)
		if ExRT.isDev then
			module.db.lastImportDB = res
			if module.db.exportTable and type(res)=="table" then
				module.db.diffTable = {}
				print("Compare table",ExRT.F.table_compare(res,module.db.exportTable,module.db.diffTable))
			end
		end
		if successful and res then
			ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveDataFromTable,res,nil,nil,SaveInputOnEdit)
		else
			StaticPopupDialogs["EXRT_RAIDGROUP_IMPORT"] = {
				text = L.ProfilesFail1..(res and "\nError code: "..res or ""),
				button1 = OKAY,
				timeout = 0,
				whileDead = true,
				hideOnEscape = true,
				preferredIndex = 3,
			}
			StaticPopup_Show("EXRT_RAIDGROUP_IMPORT")
		end
	end

	function self:RecordToText()
		local new = {}
		for i=1,40 do 
			local text = module.options.edits[i]:GetText()
			if text and string.trim(text) ~= "" then
				new[i] = text
			end
		end
		local exportWindow = module.options.exportWindow
		if exportWindow.exportType == 1 then
			local text = ""
			for i=1,8,2 do
				for j=1,5 do
					text = text .. (new[(i-1)*5+j] or "") .. "\t" .. (new[(i-0)*5+j] or "") .. "\n"
				end
			end
			exportWindow.Edit:SetText(text)
		elseif exportWindow.exportType == 2 then
			local text = ""
			for j=1,5 do
				for i=1,8 do
					text = text .. (new[(i-1)*5+j] or "") .. "\t"
				end
				text = text .. "\n"
			end
			exportWindow.Edit:SetText(text)
		elseif exportWindow.exportType == 3 then
			local text = ""
			for i=1,8 do
				for j=1,5 do
					text = text .. (new[(i-1)*5+j] or "") .. "\n"
				end
			end
			exportWindow.Edit:SetText(text)
		else
			local strlist = ExRT.F.TableToText(new)
			strlist[1] = "0,"..strlist[1]
			local str = table.concat(strlist)
	
			local compressed
			if #str < 1000000 then
				compressed = LibDeflate:CompressDeflate(str,{level = 5})
			end
			local encoded = "MRTRGR"..(compressed and "1" or "0")..LibDeflate:EncodeForPrint(compressed or str)
		
			ExRT.F.dprint("Str len:",#str,"Encoded len:",#encoded)
		
			if ExRT.isDev then
				module.db.exportTable = new
			end
			exportWindow.Edit:SetText(encoded)
		end
		exportWindow:Show()
	end

	self.butExport = ELib:Button(self,L.RaidGroupsExport):Size(192,20):Point("TOP",self.presetListSave,"BOTTOM",0,-5):OnClick(function(self) 
		module.options:RecordToText()
	end)
	self.butImport = ELib:Button(self,L.Import):Size(192,20):Point("TOP",self.butExport,"BOTTOM",0,-5):OnClick(function(self) 
		module.options.importWindow:NewPoint("CENTER",UIParent,0,0)
		module.options.importWindow:Show()			
	end)


	self:UpdateList()
	self.updateRoster:Click()

	function self:OnShow()
		module.options:UpdateTextColors()
		module.options:UpdateNotInList()
	end
	local GRU = CreateFrame("Frame",nil,self)
	GRU:SetPoint("TOPLEFT")
	GRU:SetSize(1,1)
	GRU:RegisterEvent("GROUP_ROSTER_UPDATE")
	GRU:SetScript("OnEvent",function()
		if not self:IsVisible() then
			return
		end
		module.options:UpdateTextColors()
		module.options:UpdateNotInList()
	end)
end

function module:ApplyGroups(list)
	if not IsInRaid() then
		return
	end
	local UnitsInCombat
	for i=1,40 do
		local unit = "raid"..i
		if UnitAffectingCombat(unit) then
			UnitsInCombat = (UnitsInCombat or "") .. (UnitsInCombat and "," or "") .. UnitName(unit)
		end
	end
	if UnitsInCombat then
		print("|cffff0000"..ERROR_CAPS..".|r "..L.RaidGroupsPlayersInCombat..": "..UnitsInCombat)
		return
	end

	local needGroup = {}
	local needPosInGroup = {}
	local lockedUnit = {}

	local RLName,_,RLGroup = GetRaidRosterInfo(1)
	local isRLfound = false
	for i=1,8 do 
		local pos = 1
		for j=1,5 do
			local name = list[(i-1)*5+j]
			if name == RLName then
				needGroup[name] = i
				needPosInGroup[name] = pos
				pos = pos + 1
				isRLfound = true
				break
			end
		end
		for j=1,5 do
			local name = list[(i-1)*5+j]
			if name and name ~= RLName and UnitName(name) then
				needGroup[name] = i
				needPosInGroup[name] = pos
				pos = pos + 1
			end
		end
	end

	--reports saying that too many swap events can trigger disconnect on classic, unconfirmed cause: other addons or pure swap events
	if ExRT.isClassic and not VMRT.RaidGroups.KeepPosInGroup then
		wipe(needPosInGroup)
	end

	module.db.needGroup = needGroup
	module.db.needPosInGroup = needPosInGroup
	module.db.lockedUnit = lockedUnit
	module.db.groupsReady = false
	module.db.groupWithRL = isRLfound and 0 or RLGroup

	if module.options.processRoster then
		module.options.processRoster:Disable()
	end

	module:ProcessRoster()
end

local function Debug(currentGroup,currentPos,needGroup,needPosInGroup)
	local s = ""
	for k,v in pairs(currentGroup) do
		s = s..k.."["..v..":"..currentPos[k]..","..(needGroup[k] or "")..":"..(needPosInGroup[k] or "").."]"
	end
	print(s)
end

function module:ProcessRoster()
	local UnitsInCombat
	for i=1,40 do
		local unit = "raid"..i
		if UnitAffectingCombat(unit) then
			UnitsInCombat = (UnitsInCombat or "") .. (UnitsInCombat and "," or "") .. UnitName(unit)
		end
	end
	if UnitsInCombat then
		print("|cffff0000"..ERROR_CAPS..".|r "..L.RaidGroupsCombatStarted..": "..UnitsInCombat)

		module.db.needGroup = nil
	
		if module.options.processRoster then
			module.options.processRoster:Enable()
		end
		module:UnregisterEvents('GROUP_ROSTER_UPDATE')
		return
	end

	local needGroup = module.db.needGroup
	local needPosInGroup = module.db.needPosInGroup
	local lockedUnit = module.db.lockedUnit
	if not needGroup then
		return
	end
	module:RegisterEvents('GROUP_ROSTER_UPDATE')

	local currentGroup = {}
	local currentPos = {}
	local nameToID = {}
	local groupSize = {}

	wipe(currentGroup)
	for i=1,8 do groupSize[i] = 0 end
	for i=1,GetNumGroupMembers() do
		local name, rank, subgroup = GetRaidRosterInfo(i)
		if not needGroup[name] and name:find("%-") and needGroup[strsplit("-",name)] then
			name = strsplit("-",name)
		end
		currentGroup[name] = subgroup
		nameToID[name] = i
		groupSize[subgroup] = groupSize[subgroup] + 1
		currentPos[name] = groupSize[subgroup]
	end

	--Debug(currentGroup,currentPos,needGroup,needPosInGroup)

	if not module.db.groupsReady then
		local WaitForGroup = false
		for unit,group in pairs(needGroup) do
			if currentGroup[unit] and currentGroup[unit] ~= needGroup[unit] then
				local currGroupUnit = currentGroup[unit]
				local needGroupUnit = needGroup[unit]
				if groupSize[ needGroupUnit ] < 5 then
					SetRaidSubgroup(nameToID[unit],needGroupUnit)
	
					groupSize[ currGroupUnit ] = groupSize[ currGroupUnit ] - 1
					groupSize[ needGroupUnit ] = groupSize[ needGroupUnit ] + 1
	
					WaitForGroup = true
				end
			end
		end
		if WaitForGroup then
			return
		end
	
		local SetToSwap = {}
		local WaitForSwap = false
		for unit,group in pairs(needGroup) do
			if not SetToSwap[unit] and currentGroup[unit] and currentGroup[unit] ~= group then
				local currGroupUnit = currentGroup[unit]
	
				local unitToSwap
				for unit2,group2 in pairs(currentGroup) do
					if not SetToSwap[unit2] and group2 == group and needGroup[unit2] ~= group2 then
						unitToSwap = unit2
						break
					end
				end
	
				if unitToSwap then
					SwapRaidSubgroup(nameToID[unit], nameToID[unitToSwap])
					
					WaitForSwap = true
					SetToSwap[unit] = true
					SetToSwap[unitToSwap] = true
				end
			end
		end
		if WaitForSwap then
			return
		end

		module.db.groupsReady = true
	end

	do
		local SetToSwap = {}
		local WaitForSwap = false
		for unit,pos in pairs(needPosInGroup) do
			if currentGroup[unit] == module.db.groupWithRL then
				pos = pos + 1
			end
			if not lockedUnit[unit] and currentPos[unit] and currentPos[unit] ~= pos and nameToID[unit] ~= 1 and not SetToSwap[unit] then
				local currGroupUnit = currentGroup[unit]
	
				local unitToSwapBridge
				for unit2,group2 in pairs(currentGroup) do
					if group2 ~= currentGroup[unit] and nameToID[unit2] ~= 1 and not SetToSwap[unit2] then
						unitToSwapBridge = unit2
						break
					end
				end
	
				local unitToSwap
				for unit2,pos2 in pairs(currentPos) do
					if currentGroup[unit2] == currentGroup[unit] and pos2 == pos and nameToID[unit2] ~= 1 and not SetToSwap[unit2] then
						unitToSwap = unit2
						break
					end
				end
	
				if unitToSwap and unitToSwapBridge then
					lockedUnit[unit] = true
					SwapRaidSubgroup(nameToID[unit], nameToID[unitToSwapBridge])
					SwapRaidSubgroup(nameToID[unitToSwapBridge], nameToID[unitToSwap])
					SwapRaidSubgroup(nameToID[unit], nameToID[unitToSwapBridge])
					
					WaitForSwap = true
					SetToSwap[unit] = true
					SetToSwap[unitToSwap] = true
					SetToSwap[unitToSwapBridge] = true
				end
			end
		end
		if WaitForSwap then
			return
		end
	end

	module.db.needGroup = nil

	if module.options.processRoster then
		module.options.processRoster:Enable()
	end
	module:UnregisterEvents('GROUP_ROSTER_UPDATE')
end

function module.main:GROUP_ROSTER_UPDATE()
	if self.timer then
		self.timer:Cancel()
	end
	self.timer = C_Timer.NewTimer(0.5,function()
		self.timer = nil
		module:ProcessRoster()
	end)
end

function module:slash(arg)
	if arg and arg:find("^raidgroup ") then
		local name = arg:match("^raidgroup (.-)$")
		if name then
			for i=#VMRT.RaidGroups.profiles,1,-1 do
				local entry = VMRT.RaidGroups.profiles[i]
				if entry.name == name then
					module:ApplyGroups(entry)
					return
				end
			end
		end
	elseif arg == "help" then
		print("|cff00ff00/rt raidgroup |cffffff00GROUPNAME|r|r - apply raid groups with name GROUPNAME")
	end
end