Quantcast
local mod = LCD4WoW:NewModule("LCD4WoW")
mod.name = "LCD Display"
mod.toggled = true
mod.defaultOff = true
local Evaluator = LibStub("StarLibEvaluator-1.0")
local LibCore = LibStub("StarLibCore-1.0")
local LibLCDText = LibStub("StarLibLCDText-1.0")
local LibDriverQTip = LibStub("StarLibDriverQTip-1.0")
local LibDriverCharacter = LibStub("StarLibDriverCharacter-1.0")
local WidgetText = LibStub("StarLibWidgetText-1.0")
local WidgetBar = LibStub("StarLibWidgetBar-1.0")
local WidgetHistogram = LibStub("StarLibWidgetHistogram-1.0")
local WidgetKey = LibStub("StarLibWidgetKey-1.0")
local WidgetTimer = LibStub("StarLibWidgetTimer-1.0")
local LayoutOptions = LibStub("StarLibLayoutOptions-1.0")
local Resources = LibStub("StarLibPluginResourceTools-1.0")

local resources = {}
Resources:New(resources)

local _G = _G
local GameTooltip = _G.GameTooltip

local stratas = {
	"BACKGROUND",
	"LOW",
	"MEDIUM",
	"HIGH",
	"DIALOG",
	"FULLSCREEN",
	"FULLSCREEN_DIALOG",
	"TOOLTIP"
}

local anchors = {
	"TOP",
	"TOPRIGHT",
	"TOPLEFT",
	"BOTTOM",
	"BOTTOMRIGHT",
	"BOTTOMLEFT",
	"RIGHT",
	"LEFT",
	"CENTER"
}

local anchorsDict = {}

for i, v in ipairs(anchors) do
	anchorsDict[v] = i
end

local function copy(tbl)
	local new = {}
	for k, v in pairs(tbl) do
		if type(v) == "table" then
			new[k] = copy(v)
		else
			new[k] = v
		end
	end
	return new
end

local defaults = {profile= {config=copy(LCD4WoW.config)}}
local displays = {}

local options
local blankOptions = {
	restart = {
		name = "Restart Displays",
		type = "execute",
		func = function()
			mod:StopDisplays()
			mod:StartDisplays()
		end,
		order = 1
	},
	defaults = {
		name = "Restore Defaults",
		type = "execute",
		func = function()
			mod.db.profile.config = copy(LCD4WoW.config)
			LCD4WoW:RebuildOpts()
		end,
		order = 2
	},
	displays = {
		name = "Displays",
		type = "group",
		args = {},
		order = 40
	},
	layouts = {
		name = "Layouts",
		type = "group",
		args = {},
		order = 41
	},
	widgets = {
		name = "Widgets",
		type = "group",
		args = {},
		order = 42
	}
}
if resources.scriptProfile then
	blankOptions.scriptProfile = {
		name = "Turn off CPU profiling",
		type = "execute",
		func = function()
			SetCVar("scriptProfile", 0)
			ReloadUI()
		end
	}
else
	blankOptions.scriptProfile = {
		name = "Turn on CPU profiling",
		type = "execute",
		func = function()
			SetCVar("scriptProfile", 1)
			ReloadUI()
		end,
		order = 3
	}
end

function mod:RebuildOpts()
	options = copy(blankOptions)
	options.displays.args.add = {
		name = "Add Display",
		type = "input",
		set = function(info, v)
			self.db.profile.config["display_" .. v] = {name = v, layouts = {}, widgets = {}, point = {"TOPLEFT", "UiParent", "BOTTOMLEFT", 0, -50}, parent="UIParent", strata=1}
			LCD4WoW:RebuildOpts()
		end,
		order = 1
	}
	options.layouts.args.add = {
		name = "Add Layout",
		type = "input",
		set = function(info, v)
			self.db.profile.config["layout_" .. v] = {name = v}
			LCD4WoW:RebuildOpts()
		end,
		order = 1
	}
	options.widgets.args.text = {
		name = "Text Widgets",
		type = "group",
		args = {
			add = {
				name = "Add",
				desc = "Enter a name for your text widget",
				type = "input",
				set = function(info, v)
					self.db.profile.config["widget_" .. v] = {type = "text"}
					LCD4WoW:RebuildOpts()
				end,
				order = 1
			},
		}
	}
	options.widgets.args.bar = {
		name = "Bars",
		type = "group",
		args = {
			add = {
				name = "Add",
				desc = "Enter a name for your bar widget",
				type = "input",
				set = function(info, v)
					self.db.profile.config["widget_" .. v] = {type = "bar"}
					LCD4WoW:RebuildOpts()
				end,
				order = 1
			}
		}
	}
	options.widgets.args.histogram = {
		name = "Histograms",
		type = "group",
		args = {
			add = {
				name = "Add",
				desc = "Enter a name for your histogram widget",
				type = "input",
				set = function(info, v)
					self.db.profile.config["widget_" .. v] = {type = "histogram"}
					LCD4WoW:RebuildOpts()
				end,
				order = 1
			}
		}
	}
	options.widgets.args.icon = {
		name = "Icons",
		type = "group",
		args = {
			add = {
				name = "Add",
				desc = "Enter a name for your icon widget",
				type = "input",
				set = function(info, v)
					self.db.profile.config["widget_" .. v] = {type = "icon"}
					LCD4WoW:RebuildOpts()
				end,
				order = 1
			}
		}
	}
	options.widgets.args.key = {
		name = "Keys",
		type = "group",
		args = {
			add = {
				name = "Add",
				desc = "Enter a name for your key widget",
				type = "input",
				set = function(info, v)
					self.db.profile.config["widget_" .. v] = {type = "key"}
					LCD4WoW:RebuildOpts()
				end,
				order = 1
			}
		}
	}
	options.widgets.args.timers = {
		name = "Timers",
		type = "group",
		args = {
			add = {
				name = "Add",
				type = "input",
				desc = "Enter a name for your timer widget",
				set = function(info, v)
					self.db.profile.config["widget_" .. v] = {type = "timer"}
					LCD4WoW:RebuildOpts()
				end,
				order = 1
			}
		}
	}
	for k, v in pairs(self.db.profile.config) do
		local db = v
		if k:match("^display_.*") then
			options.displays.args[k:gsub(" ", "_")] = {
				name = k:gsub("display_", ""),
				type = "group",
				args = {
					enable = {
						name = "Enable",
						type = "toggle",
						get = function()
							return v.enabled
						end,
						set = function(info, val)
							v.enabled = val
						end,
						order = 1
					},
					driver = {
						name = "Driver",
						desc = "This display's driver type",
						type = "select",
						values = LibCore.driverList,
						get = function() return LibCore.driverDict[v.driver] end,
						set = function(info, val)
							v.driver = LibCore.driverList[val]
							LCD4WoW:RebuildOpts()
						end,
						order = 2

					},
					send = {
						name = "Send Display",
						desc = "Send this display to someone",
						type = "input",
						set = function(info, name)
							self:SendDisplay(k, name)
						end,
						order = 3
					},
					pixel = {
						name = "Display Pixel",
						desc = "Size of a single pixel",
						type = "input",
						pattern = "%d",
						get = function() return v.pixel or 1 end,
						set = function(info, val) v.pixel = val end,
						order = 4
					},
					strata = {
						name = "Frame Strata",
						desc = "The frame's strata",
						type = "select",
						values = stratas,
						get = function() return v.strata or #stratas end,
						set = function(info, val) v.strata = val end,
						order = 5
					},
					point = {
						name = "Anchor Points",
						desc = "This histogram's anchor point. These arguments are passed to bar:SetPoint()",
						type = "group",
						args = {
							point = {
								name = "Bar anchor",
								type = "select",
								values = anchors,
								get = function() return anchorsDict[db.point[1] or 1] end,
								set = function(info, v) db.point[1] = anchors[v];clearHistograms();createHistograms() end,
								order = 1
							},
							relativeFrame = {
								name = "Relative Frame",
								type = "input",
								get = function() return db.point[2] end,
								set = function(info, v) db.point[2] = v; clearHistograms(); createHistograms() end,
								order = 2
							},
							relativePoint = {
								name = "Relative Point",
								type = "select",
								values = anchors,
								get = function() return anchorsDict[db.point[3] or 1] end,
								set = function(info, v) db.point[3] = anchors[v]; clearHistograms(); createHistograms() end,
								order = 3
							},
							xOfs = {
								name = "X Offset",
								type = "input",
								pattern = "%d",
								get = function() return tostring(db.point[4] or 0) end,
								set = function(info, v) db.point[4] = tonumber(v); clearHistograms(); createHistograms() end,
								order = 4
							},
							yOfs = {
								name = "Y Offset",
								type = "input",
								pattern = "%d",
								get = function() return tostring(db.point[5] or 0) end,
								set = function(info, v) db.point[5] = tonumber(v); clearHistograms();createHistograms() end,
								order = 4
							},
							parent = {
								name = "Parent",
								type = "input",
								get = function() return db.parent or "UIParent" end,
								set = function(info, v) db.parent = v end,
								order = 5
							}
						},
						order = 7
					},
					delete = {
						name = "Delete",
						type = "execute",
						func = function()
							self.db.profile.config[k] = nil
							LCD4WoW:RebuildOpts()
						end,
						order = 100
					}
				}
			}
			local driverOptions = {}
			if v.driver == "qtip" then
				driverOptions = LibDriverQTip:RebuildOpts(LCD4WoW, v, k)
			elseif v.driver == "character" then
				driverOptions = LibDriverCharacter:RebuildOpts(LCD4WoW, v, k)
			end

			for kk, vv in pairs(driverOptions) do
				options.displays.args[k:gsub(" ", "_")].args[kk] = vv
			end
			if v.widgets then
				for i, widget in ipairs(v.widgets) do
					options.displays.args[k:gsub(" ", "_")].args.widgets.args[widget] = {
						name = widget,
						type = "input",
						get = function() return widget end,
						set = function() v.widgets[i] = val end,
						order = 50 + i
					}
				end
			end
		end
		if k:match("^layout_") then
			options.layouts.args[k:gsub(" ", "_")] = {
				name = k,
				type = "group",
				args = {}
			}
			options.layouts.args[k:gsub(" ", "_")].args = LayoutOptions:RebuildOpts(LCD4WoW, v, k)
			options.layouts.args[k:gsub(" ", "_")].args.delete = {
				name = "Delete",
				type = "execute",
				func = function()
					self.db.profile.config[k] = nil
					LCD4WoW:RebuildOpts()
				end
			}

		end
		if k:match("^widget_") then
			if v.type == "text" then
				options.widgets.args.text.args[k:gsub(" ", "_")] = {
					name = k:gsub("widget_", ""),
					type = "group",
					args = WidgetText:GetOptions(LCD4WoW, v, k),
					order = 1
				}
				options.widgets.args.text.args[k:gsub(" ", "_")].args.delete = {
					name = "Delete",
					type = "execute",
					func = function()
						self.db.profile.config[k] = nil
						LCD4WoW:RebuildOpts()
					end,
					order = 100
				}
			elseif v.type == "bar" then
				options.widgets.args.bar.args[k:gsub(" ", "_")] = {
					name = k,
					type = "group",
					args = WidgetBar:GetOptions(LCD4WoW, v, k),
					order = 1
				}
				options.widgets.args.bar.args[k:gsub(" ", "_")].args.delete = {
					name = "Delete",
					type = "execute",
					func = function()
						self.db.profile.config[k] = nil
						LCD4WoW:RebuildOpts()
					end,
					order = 100
				}
			elseif v.type == "histogram" then
				options.widgets.args.histogram.args[k:gsub(" ", "_")] = {
					name = k,
					type = "group",
					args = WidgetHistogram:GetOptions(LCD4WoW, v, k),
					order = 1
				}
				options.widgets.args.histogram.args[k:gsub(" ", "_")].args.delete = {
					name = "Delete",
					type = "execute",
					func = function()
						self.db.profile.config[k] = nil
						LCD4WoW:RebuildOpts()
					end,
					order = 100
				}
			elseif v.type == "icon" then
				options.widgets.args.icon.args[k:gsub(" ", "_")] = {
					name = k,
					type = "group",
					args = {}, --WidgetIcon:GetOptions(LCD4WoW, v, k)},
					order = 2
				}
				options.widgets.args.icon.args[k:gsub(" ", "_")].args.delete = {
					name = "Delete",
					type = "execute",
					func = function()
						self.db.profile.config[k] = nil
						LCD4WoW:RebuildOpts()
					end,
					order = 100
				}
			elseif v.type == "key" then
				options.widgets.args.key.args[k:gsub(" ", "_")] = {
					name = k,
					type = "group",
					args = WidgetKey:GetOptions(LCD4WoW, v, k),
					order = 3
				}
				options.widgets.args.key.args[k:gsub(" ", "_")].args.delete = {
					name = "Delete",
					type = "execute",
					func = function()
						self.db.profile.config[k] = nil
						LCD4WoW:RebuildOpts()
					end,
					order = 100
				}
			elseif v.type == "timer" then
				options.widgets.args.timers.args[k:gsub(" ", "_")] = {
					name = k,
					type = "group",
					args = WidgetTimer:GetOptions(LCD4WoW, v, k),
					order = 3
				}
				options.widgets.args.timers.args[k:gsub(" ", "_")].args.delete = {
					name = "Delete",
					type = "execute",
					func = function()
						self.db.profile.config[k] = nil
						LCD4WoW:RebuildOpts()
					end,
					order = 100
				}
			end
		end
	end
end

function mod:GetOptions()
	self:RebuildOpts()
	return options
end

function mod:OnInitialize()
	self.db = LCD4WoW.db:RegisterNamespace(self:GetName(), defaults)
	self.environment = {_G=_G}
	LCD4WoW:RegisterComm("LCD4WoWDisplayTransfer", self.OnCommReceived)
end

function mod:OnEnable()
	self:StartDisplays()
end

function mod:OnDisable()
	self:StopDisplays()
end

function mod:StartDisplays()
	for k, v in pairs(self.db.profile.config) do
		if k:match("^display_") then
			if v.driver == "qtip" and v.enabled then
				local display = LibDriverQTip:New(self, self.environment, k, self.db.profile.config, LCD4WoW.db.profile.errorLevel)
				--if ResourceServer then ResourceServer:New(display.environment) end
				display:Show()
				tinsert(displays, display)
			elseif v.driver == "character" and v.enabled then
				local display = LibDriverCharacter:New(self, self.environment, k, self.db.profile.config, LCD4WoW.db.profile.errorLevel)
				--if ResourceServer then ResourceServer:New(display.environment) end
				display:Show()
				tinsert(displays, display)
			end
		end
	end
end

function mod:StopDisplays()
	for i, v in ipairs(displays) do
		v:Hide()
		v:Del()
	end
	table.wipe(displays)
end

function mod.OnCommReceived(prefix, message, distribution, sender)
	local self = mod
	local flag, data = LCD4WoW:Deserialize(message)
	if not flag then LCD4WoW:Print(data, type(message), message); return end
	if type(data) ~= "table" then LCD4WoW:Print("Display data is not a table -- " .. type(data)); return end
	for k, v in pairs(data) do
		if k:match("^display_") then
			if v.driver == "qtip" then
				local display = LibDriverQTip:New(self, self.environment, k, data, LCD4WoW.db.profile.errorLevel)
				display:Show()
				tinsert(displays, display)
			elseif v.driver == "character" then
				local display = LibDriverCharacter:New(self, self.environment, k, data, LCD4WoW.db.profile.errorLevel)
				display:Show()
				tinsert(displays, display)
			end
		else
			LCD4WoW:Print(k)
		end
	end
end

function mod:SendDisplay(display, name)
	local tbl = {[display] = self.db.profile.config[display]}
	local widgets = self.db.profile.config[display].widgets
	local layouts = self.db.profile.config[display].layouts
	for i, w in ipairs(widgets) do
		tbl[w] = self.db.profile.config[w]
	end
	for i, layout in ipairs(layouts) do
		tbl[layout] = self.db.profile.config[layout]
		if not tbl[layout] then break end
		for layer = 1, tbl[display].layers do
			for row = 1, tbl[display].rows do
				for col = 1, tbl[display].cols do
					if tbl[layout] and tbl[layout][layer] and
						tbl[layout][layer][row] and
						tbl[layout][layer][row][col] then
						local widget = tbl[layout][layer][row][col]
						tbl[widget] = self.db.profile.config[widget]
					end
				end
			end
		end
	end
	local data = LCD4WoW:Serialize(tbl)
	LCD4WoW:SendCommMessage("LCD4WoWDisplayTransfer", data, "WHISPER", name)
end

function mod:MODIFIER_STATE_CHANGED(ev, modifier, up, ...)
	for i, display in ipairs(displays) do
		if display.KeyEvent then
			display:KeyEvent(modifier, up)
		end
	end
end