Quantcast
local AceGUI = LibStub('AceGUI-3.0');
local lsm = LibStub('AceGUISharedMediaWidgets-1.0');
local media = LibStub('LibSharedMedia-3.0');

MaxDps = LibStub('AceAddon-3.0'):NewAddon('MaxDps', 'AceConsole-3.0', 'AceEvent-3.0', 'AceTimer-3.0');

MaxDps.Textures = {
	['Ping'] = 'Interface\\Cooldown\\ping4',
	['Star'] = 'Interface\\Cooldown\\star4',
	['Starburst'] = 'Interface\\Cooldown\\starburst',
};
MaxDps.FinalTexture = nil;

MaxDps.Colors = {
	Info = '|cFF1394CC',
	Error = '|cFFF0563D',
	Success = '|cFFBCCF02',
}

MaxDps.Classes = {
	[1] = 'Warrior',
	[2] = 'Paladin',
	[3] = 'Hunter',
	[4] = 'Rogue',
	[5] = 'Priest',
	[6] = 'DeathKnight',
	[7] = 'Shaman',
	[8] = 'Mage',
	[9] = 'Warlock',
	[10] = 'Monk',
	[11] = 'Druid',
	[12] = 'DemonHunter',
}

local defaultOptions = {
	global = {
		enabled = true,
		disabledInfo = false,
		debugMode = false,
		disableButtonGlow = false,
		onCombatEnter = true,
		texture = 'Interface\\Cooldown\\ping4',
		customTexture = '',
		highlightColor = {
			r = 1,
			g = 1,
			b = 1,
			a = 1
		},
		cooldownColor = {
			r = 0,
			g = 1,
			b = 0,
			a = 1
		},
		interval = 0.15,
		sizeMult = 1.4
	}
}

local options = {
	type = 'group',
	name = 'MaxDps Options',
	inline = false,
	args = {
		general = {
			order = 10,
			name = 'General',
			type = 'group',
			args = {
				enable = {
					order = 10,
					name = 'Enable',
					desc = 'Enables / disables the addon',
					type = 'toggle',
					width = 'full',
					set = function(info, val)
						MaxDps.db.global.enabled = val;
					end,
					get = function(info) return MaxDps.db.global.enabled end
				},
				onCombatEnter = {
					order = 20,
					name = 'Enable upon entering combat',
					desc = 'Automatically enables helper upon entering combat',
					type = 'toggle',
					width = 'full',
					set = function(info, val)
						MaxDps.db.global.onCombatEnter = val;
					end,
					get = function(info) return MaxDps.db.global.onCombatEnter end
				},
				disableButtonGlow = {
					order = 30,
					name = 'Dissable blizzard button glow (experimental)',
					desc = 'Disables original blizzard button glow',
					type = 'toggle',
					width = 'full',
					set = function(info, val)
						MaxDps.db.global.disableButtonGlow = val;
						MaxDps:UpdateButtonGlow();
					end,
					get = function(info) return MaxDps.db.global.disableButtonGlow end
				},
				interval = {
					order = 40,
					name = 'Interval in seconds',
					desc = 'Sets how frequent rotation updates will be. Low value will result in fps drops.',
					type = 'range',
					min = 0.01,
					max = 2,
					set = function(info, val) MaxDps.db.global.interval = val end,
					get = function(info) return MaxDps.db.global.interval end
				},
			}
		},
		debug = {
			order = 30,
			name = 'Debug options',
			type = 'group',
			args = {
				debugMode = {
					order = 10,
					name = 'Enable debug mode',
					desc = 'Enables spammy chat messages (use this when addon does not work for you)',
					type = 'toggle',
					width = 'full',
					set = function(info, val)
						MaxDps.db.global.debugMode = val;
					end,
					get = function(info) return MaxDps.db.global.debugMode end
				},
				disabledInfo = {
					order = 20,
					name = 'Disable info messages',
					desc = 'Enables / disables info messages, if you have issues with addon, make sure to deselect this.',
					type = 'toggle',
					width = 'full',
					set = function(info, val)
						MaxDps.db.global.disabledInfo = val;
					end,
					get = function(info) return MaxDps.db.global.disabledInfo end
				},
			}
		},
		overlay = {
			order = 20,
			name = 'Overlay settings',
			type = 'group',
			args = {
				texture = {
					order = 10,
					type = 'select',
					dialogControl = 'LSM30_Background',
					name = 'Texture',
					width = 'normal',
					desc = 'Sets Highlight texture (changing this requires UI Reload)',
					values = function()
						return MaxDps.Textures;
					end,
					get = function()
						return MaxDps.db.global.texture;
					end,
					set = function(self, val)
						MaxDps.db.global.texture = val;
						MaxDps:ApplyOverlayChanges();
					end,
				},
				customTexture = {
					order = 20,
					name = 'Custom Texture',
					desc = 'Sets Highlight texture, has priority over selected one (changing this requires UI Reload)',
					type = 'input',
					width = 'normal',
					set = function(info, val)
						MaxDps.db.global.customTexture = strtrim(val or '');
						MaxDps:ApplyOverlayChanges();
					end,
					get = function(info) return strtrim(MaxDps.db.global.customTexture or '') end
				},
				highlightColor = {
					order = 30,
					name = 'Highlight color',
					desc = 'Sets Highlight color',
					type = 'color',
					width = 'normal',
					set = function(info, r, g, b, a)
						local c = MaxDps.db.global.highlightColor;
						c.r, c.g, c.b, c.a = r, g, b, a;
						MaxDps:ApplyOverlayChanges();
					end,
					get = function(info)
						local c = MaxDps.db.global.highlightColor;
						return c.r, c.g, c.b, c.a;
					end,
					hasAlpha = true
				},
				cooldownColor = {
					order = 40,
					name = 'Cooldown color',
					desc = 'Sets Cooldown color',
					type = 'color',
					width = 'normal',
					set = function(info, r, g, b, a)
						local c = MaxDps.db.global.cooldownColor;
						c.r, c.g, c.b, c.a = r, g, b, a;
						MaxDps:ApplyOverlayChanges();
					end,
					get = function(info)
						local c = MaxDps.db.global.cooldownColor;
						return c.r, c.g, c.b, c.a;
					end,
					hasAlpha = true
				},
				sizeMult = {
					order = 50,
					name = 'Overlay size multiplier',
					desc = 'Sets how big will be overlay on the button. 1 = exactly the same as button',
					type = 'range',
					width = 'full',
					min = 0.5,
					max = 2,
					set = function(info, val)
						MaxDps.db.global.sizeMult = val;
						MaxDps:ApplyOverlayChanges();
					end,
					get = function(info) return MaxDps.db.global.sizeMult or 1.4 end
				},
			}
		},
		reset = {
			name = 'Reset settings',
			desc = 'Resets settings to default values',
			type = 'execute',
			func = function()
				MaxDps:ResetSettings();
				MaxDps:ApplyOverlayChanges();
			end
		}
	},
}

function MaxDps:ResetSettings()
	self.db:ResetDB();
end

function MaxDps:GetTexture()
	if self.db.global.customTexture ~= '' and self.db.global.customTexture ~= nil then
		self.FinalTexture = self.db.global.customTexture;
		return self.FinalTexture;
	end

	self.FinalTexture = self.Textures[self.db.global.texture];
	if self.FinalTexture == '' or self.FinalTexture == nil then
		self.FinalTexture = 'Interface\\Cooldown\\ping4';
	end

	return self.FinalTexture;
end

function MaxDps:OnInitialize()
	LibStub('AceConfig-3.0'):RegisterOptionsTable('MaxDps', options, { '/maxdpsopts' });
	self.db = LibStub('AceDB-3.0'):New('MaxDpsOptions', defaultOptions);
	self.optionsFrame = LibStub('AceConfigDialog-3.0'):AddToBlizOptions('MaxDps', 'MaxDps');
	self:RegisterChatCommand('maxdps', 'ShowCustomWindow');

	if not self.db.global.customRotations then
		self.db.global.customRotations = {};
	end
end

MaxDps.DefaultPrint = MaxDps.Print;
function MaxDps:Print(...)
	if self.db.global.disabledInfo then
		return;
	end
	MaxDps:DefaultPrint(...);
end

function MaxDps:EnableRotation()
	self:Print(self.Colors.Info .. 'Enabling');

	if self.NextSpell == nil or self.rotationEnabled then
		self:Print(self.Colors.Error .. 'Failed to enable addon!');
		return;
	end
	self:Print(self.Colors.Info .. 'Fetching');
	self.Fetch();

	MaxDps:CheckTalents();
	if self.ModuleOnEnable then
		self.ModuleOnEnable();
	end

	self:EnableRotationTimer();

	self.rotationEnabled = true;
	self:Print(self.Colors.Success .. 'Enabled');
end

function MaxDps:EnableRotationTimer()
	self.RotationTimer = self:ScheduleRepeatingTimer('InvokeNextSpell', self.db.global.interval);
end

function MaxDps:DisableRotation()
	if not self.rotationEnabled then
		return;
	end

	self:DisableRotationTimer();

	self:DestroyAllOverlays();
	self:Print(self.Colors.Info .. 'Disabling');

	self.Spell = nil;
	self.rotationEnabled = false;
end

function MaxDps:DisableRotationTimer()
	if self.RotationTimer then
		self:CancelTimer(self.RotationTimer);
	end
end

function MaxDps:OnEnable()
	self:RegisterEvent('PLAYER_TARGET_CHANGED');
	self:RegisterEvent('PLAYER_TALENT_UPDATE');
	self:RegisterEvent('ACTIONBAR_SLOT_CHANGED');
	self:RegisterEvent('PLAYER_REGEN_DISABLED');
	self:RegisterEvent('PLAYER_ENTERING_WORLD');

	self:RegisterEvent('ACTIONBAR_HIDEGRID');
	self:RegisterEvent('ACTIONBAR_PAGE_CHANGED');
	self:RegisterEvent('LEARNED_SPELL_IN_TAB');
	self:RegisterEvent('CHARACTER_POINTS_CHANGED');
	self:RegisterEvent('ACTIVE_TALENT_GROUP_CHANGED');
	self:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED');
	self:RegisterEvent('UPDATE_MACROS');
	self:RegisterEvent('VEHICLE_UPDATE');

	self:RegisterEvent('UNIT_ENTERED_VEHICLE');
	self:RegisterEvent('UNIT_EXITED_VEHICLE');
	--	self:RegisterEvent('PLAYER_REGEN_ENABLED');

	self:Print(self.Colors.Info .. 'Initialized');
end

function MaxDps:PLAYER_TALENT_UPDATE()
	self:DisableRotation();
end

function MaxDps:UNIT_ENTERED_VEHICLE(event, unit)
	if unit == 'player' and self.rotationEnabled then
		self:DisableRotation();
	end
end

function MaxDps:UNIT_EXITED_VEHICLE(event, unit)
	if unit == 'player' then
		self:InitRotations();
		self:EnableRotation();
	end
end

function MaxDps:PLAYER_ENTERING_WORLD()
	self:UpdateButtonGlow();
end

function MaxDps:PLAYER_TARGET_CHANGED()
	if self.rotationEnabled then
		if (UnitIsFriend('player', 'target')) then
			return;
		else
			self:InvokeNextSpell();
		end
	end
end

function MaxDps:PLAYER_REGEN_DISABLED()
	if self.db.global.onCombatEnter and not self.rotationEnabled then
		self:Print(self.Colors.Success .. 'Auto enable on combat!');
		self:InitRotations();
		self:EnableRotation();
	end
end

function MaxDps:ButtonFetch()
	if self.rotationEnabled then
		if self.fetchTimer then
			self:CancelTimer(self.fetchTimer);
		end
		self.fetchTimer = self:ScheduleTimer('Fetch', 0.5);
	end
end

MaxDps.ACTIONBAR_SLOT_CHANGED = MaxDps.ButtonFetch;
MaxDps.ACTIONBAR_HIDEGRID = MaxDps.ButtonFetch;
MaxDps.ACTIONBAR_PAGE_CHANGED = MaxDps.ButtonFetch;
MaxDps.LEARNED_SPELL_IN_TAB = MaxDps.ButtonFetch;
MaxDps.CHARACTER_POINTS_CHANGED = MaxDps.ButtonFetch;
MaxDps.ACTIVE_TALENT_GROUP_CHANGED = MaxDps.ButtonFetch;
MaxDps.PLAYER_SPECIALIZATION_CHANGED = MaxDps.ButtonFetch;
MaxDps.UPDATE_MACROS = MaxDps.ButtonFetch;
MaxDps.VEHICLE_UPDATE = MaxDps.ButtonFetch;

function MaxDps:InvokeNextSpell()
	-- invoke spell check
	local oldSkill = self.Spell;

	local timeShift, currentSpell, gcd = MaxDps:EndCast();
	self.Spell = self:NextSpell(timeShift, currentSpell, gcd, self.PlayerTalents);

	if (oldSkill ~= self.Spell or oldSkill == nil) and self.Spell ~= nil then
		self:GlowNextSpellId(self.Spell);
		if WeakAuras then
			WeakAuras.ScanEvents('MAXDPS_SPELL_UPDATE', self.Spell);
		end
	end
	if self.Spell == nil and oldSkill ~= nil then
		self:GlowClear();
		if WeakAuras then
			WeakAuras.ScanEvents('MAXDPS_SPELL_UPDATE', nil);
		end
	end
end

function MaxDps:InitRotations()
	self:Print(self.Colors.Info .. 'Initializing rotations');

	local _, _, classId = UnitClass('player');
	local spec = GetSpecialization();
	self.ClassId = classId;
	self.Spec = spec;

	self:LoadCustomRotations();
	if self.CustomRotations[classId] and self.CustomRotations[classId][spec] then
		self.CurrentRotation = self.CustomRotations[classId][spec];
		self.NextSpell = self.CurrentRotation.fn;
		self:Print(self.Colors.Success .. 'Loaded Custom Rotation: ' .. self.CurrentRotation.name);
	else
		self:LoadModule();
	end
end

function MaxDps:LoadModule()
	if self.Classes[self.ClassId] == nil then
		self:Print(self.Colors.Error .. 'Invalid player class, please contact author of addon.');
		return;
	end

	local module = 'MaxDps_' .. self.Classes[self.ClassId];
	local _, _, _, loadable, reason = GetAddOnInfo(module);

	if IsAddOnLoaded(module) then
		self:Print(self.Colors.Info .. self.Description);
		self:EnableRotationModule(self.Spec);
		self:Print(self.Colors.Info .. 'Finished Loading class module');
		return;
	end

	if reason == 'MISSING' or reason == 'DISABLED' then
		self:Print(self.Colors.Error .. 'Could not find class module ' .. module .. ' or it was disabled.');
		return;
	end

	LoadAddOn(module);

	self:EnableRotationModule(self.Spec);
	self:Print(self.Colors.Info .. self.Description);
	self:Print(self.Colors.Info .. 'Finished Loading class module');
end