---------------------------------------------------------------------------------
--
-- Prat - A framework for World of Warcraft chat mods
--
-- Copyright (C) 2006-2007  Prat Development Team
--
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- as published by the Free Software Foundation; either version 2
-- of the License, or (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to:
--
-- Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor,
-- Boston, MA  02110-1301, USA.
--
--
-------------------------------------------------------------------------------


--[[
Name: Prat_Alias
Revision: $Revision: 25304$
Author(s): Fin (fin@instinct.org)
Website: http://files.wowace.com/Prat/
Documentation: http://www.wowace.com/wiki/Prat/Integrated_Modules#Alias
Subversion: http://svn.wowace.com/wowace/trunk/Prat/
Discussions: http://groups.google.com/group/wow-prat
Issues and feature requests: http://code.google.com/p/prat/issues/list
Description: Adds the command /alias, which can be used to alias slash commands in a similar way to the Unix alias command (default=off).
Dependencies: Prat
]]

-- crappy code, one of the first things I started fiddling with. should clean it up and stuff.

-- set prat module name
local PRAT_MODULE = Prat:RequestModuleName("Alias")

-- define localized strings
local L = AceLibrary("AceLocale-2.2"):new(PRAT_MODULE)

L:RegisterTranslations("enUS", function() return {
	["Alias"] = true,
	["Adds the command /alias, which can be used to alias slash commands in a similar way to the Unix alias command."] = true,
	['<command>[ <value>] - alias <command> to be executed as <value>, or return the value of the currently defined alias for <command> if <command> has not been assigned a value. eg: "/alias /examplehello /say hello there" - typing "/examplehello" will now cause your character to say "hello there"; "/alias examplehello" - prints "/examplehello is aliased to /say hello there"'] = true,
} end)

-- L:RegisterTranslations("deDE", function() return {
--	["Alias"] = "",
-- } end)

-- get prat module categories
local cat = Prat.Categories

-- create prat module
Prat_Alias = Prat:NewModule(PRAT_MODULE)
Prat_Alias.revision = tonumber(string.sub("$Revision:  25304 $", 12, -3))

-- define key module values
Prat_Alias.moduleName	= L["Alias"]
Prat_Alias.moduleDesc	= L["Adds the command /alias, which can be used to alias slash commands in a similar way to the Unix alias command."]

Prat_Alias.consoleName	= string.lower(Prat_Alias.moduleName)
Prat_Alias.guiName	= Prat_Alias.moduleName

-- list here: http://groups-beta.google.com/group/wow-prat/web/prat-categories
Prat_Alias.Categories	= { cat.TEXT, cat.BEHAVIOR, cat.ACTION }

-- init moduleOoptions (which gets populated by the other various options tables below
Prat_Alias.moduleOptions = {}

-- defined aliases
Prat_Alias.Aliases	= {}

-- default values for any settings that need them
Prat_Alias.defaultDB = {
	on	= false,
	aliases	= {},

	-- things we won't alias
	wontalias = {
		unalias	= 1,
		alias	= 1,
		prat	= 1,
		script	= 1,
		},
	}

-- any boolean options
Prat_Alias.toggleOptions = {
	on,
	}

function Prat_Alias:GetModuleOptions()
	self.moduleOptions = {
		name	= self.moduleName,
		desc	= self.moduleDesc,
		type	= "group",
		args = {
			add = {
				type	= "text",
				name	= "add",
				desc	= "add an alias",
				usage	= L['<command>[ <value>] - alias <command> to be executed as <value>, or return the value of the currently defined alias for <command> if <command> has not been assigned a value. eg: "/alias /examplehello /say hello there" - typing "/examplehello" will now cause your character to say "hello there"; "/alias examplehello" - prints "/examplehello is aliased to /say hello there"'],

				get	= false,
				set	= function(argstr) return self:setAlias(argstr) end,
				},

			del = {
				name	= "unalias",
				desc	= "remove an alias",
				type	= "text",
				usage	= "/unalias <alias> - remove the alias <alias>",
				get	= false,
				set	= function(aliastoremove) return self:delAlias(aliastoremove) end,
				},
			list = {
				name	= "listaliases",
				desc	= "list aliases",
				type	= "execute",
				usage	= "/listaliases",
				func	= function() return self:listAliases() end
				}
			}
		}

	return self.moduleOptions
end

--[[------------------------------------------------
    Module Event Functions
------------------------------------------------]]--

-- things to do when the module is enabled
function Prat_Alias:OnModuleEnable()
	for k, v in pairs(self.db.profile.aliases) do
		self.Aliases[k] = v
	end

	self.WontAlias = self.db.profile.wontalias
	for naughtyalias, jutstrue in pairs(self.WontAlias) do
		self.WontAlias[string.lower(naughtyalias)] = 1
	end

	self:Hook('ChatEdit_HandleChatType', true)
	self:SecureHook('ChatEdit_ParseText')
	-- self:SecureHook('ChatEdit_HandleChatType')

	Prat:RegisterChatCommand({ '/alias', '/addalias', }, self.moduleOptions.args.add, 'PRATALIAS')
	Prat:RegisterChatCommand({ '/unalias' }, self.moduleOptions.args.del, 'PRATUNALIAS')
	Prat:RegisterChatCommand({ '/listaliases' }, self.moduleOptions.args.list, 'PRATLISTALIASES')
end

-- things to do when the module is disabled
function Prat_Alias:OnModuleDisable()
	-- unregister events
	self:UnregisterAllEvents()

	self.db.profile['on'] = false
	self.Aliases = {}
end

--[[------------------------------------------------
    Core Functions
------------------------------------------------]]--

function Prat_Alias:splitAliasArgs(str)
	-- str should be "<command>[=value| value]"
	local name, value
	local args = {
		name	= "",
		value	= "",
		}

	-- if it doesn't match, args is left with default blank strings for values
	--for alias, command in str:find("(%w+)%s*=?%s*(.-)%s*$") do
	for alias, command in str:gmatch("/?(%w+)%s*[%s=]%s*/?(.-)$") do
		-- either matches both alias and command (may match command as a blank string)
		args['name']	= string.lower(alias)
		args['value']	= command or ""
		-- util:print('name ' .. args['name'])
		-- util:print('value ' .. args['value'])
	end

	return args
end

function Prat_Alias:checkArgStr(funcname, argstr)
	if argstr == nil then
		self:warnUser(string.format("%s() called with nil argument!", funcname))
		return false
	end

	if argstr == "" then
		self:warnUser(string.format("%s() called with blank string!", funcname))
		return false
	end

	return true
end

function Prat_Alias:setAlias(argstr)
	-- argstr should be "<command>[ <value]"
	if not self:checkArgStr('setAlias', argstr) then
		return false
	end

	local alias = self:splitAliasArgs(argstr)
	-- local aliasname, value = alias[name], alias[value]

	-- check to see if the user is defining an alias or not
	if not alias['value'] or (alias['value'] == "") then
		local name = argstr

		-- called as: /alias <command> - check for alias called <command> to display
		if self.Aliases[name] then
			-- alias found; show it :)
			self:showAlias(name)
			return true
		else
			-- no alias found called <command>; tell user
			self:reportUndefinedAlias(name)
		end
	elseif self.WontAlias[string.lower(alias['name'])] then
		-- user is defining an alias called <command>, but it's potentially bad
		self:warnUser(string.format('refusing to alias "%s" to anything in the interests of Not Buggering Everything Up', alias['name']))
		return false
	else
		-- it's not listed as bad, so create or update the aliases tables
		-- called as /alias <command> <value> - define alias <command> as <value>
		if self.Aliases[alias['name']] then
			-- specified alias already exists, warn user and print old setting
			self:warnUser(string.format('overwriting existing alias "%s" (was aliased to "%s")', alias['name'], self.Aliases[alias['name']]))
		end

		-- now (re?)define the alias <command> to <value>
		self.Aliases[alias['name']] = alias['value']
		self.db.profile.aliases[alias['name']] = alias['value']

		self:warnUser(string.format("/%s aliased to: /%s", alias['name'], alias['value']))
	end
end

function Prat_Alias:delAlias(aliasname)
	if not self:checkArgStr('delAlias', aliasname) then
		return false
	end

	if not self.Aliases[aliasname] then
		self:warnUser(string.format('alias "%s" does not exist', aliasname))
		return false
	end

	local oldalias = self.Aliases[aliasname]

	self:warnUser(string.format('deleting alias "%s" (previously aliased as "%s")', aliasname, oldalias))

	self.Aliases[aliasname]			= nil
	self.db.profile.aliases[aliasname]	= nil

	return oldalias
end

function Prat_Alias:showAlias(aliasname)
	if not self:checkArgStr('showAlias', aliasname) then
		return false
	end

	-- check for undefined alias called aliasname
	if not self.Aliases[aliasname] then
		self:warnUser(string.format('tried to show value for alias "%s" but undefined in Prat_Alias.Aliases!', aliasname))
		return false
	end

	-- everything OK; display value of alias "aliasname"
	self:warnUser(string.format('alias %s = %s', aliasname, self.Aliases[aliasname]))

	return true
end

function Prat_Alias:listAliases()
	if self.Aliases == {} then
		self:warnUser("No aliases have been defined")
		return false
	end

	for name, alias in pairs(self.Aliases) do
		self:showAlias(name)
	end
end


function Prat_Alias:reportUndefinedAlias(name)
	return self:warnUser(string.format('There is no alias current defined for "%s"', name))
end

function Prat_Alias:warnUser(str)
	if str == nil then
		str = "warnUser() called with nil argument!"
	elseif not str then
		str = "warnUser() called with zero length string!"
	end

	DEFAULT_CHAT_FRAME:AddMessage(string.format("%s: %s", self.moduleName, str))

	return true
end


function Prat_Alias:ChatEdit_HandleChatType(editBox, msg, command, send, passthru)
	-- thanks to sylvanaar for helping me get this working
	-- don't ping Tem!
	local alias	= self.Aliases[string.lower(strsub(command, 2))]

	if alias and alias ~= "" then
		local newcmd	= strmatch(alias, "^([^%s]+)") or ""
		local premsg	= strsub(alias, strlen(newcmd) + 2)

		msg	= premsg .. ' ' .. msg
		command	= '/' .. string.upper(newcmd)
	end

	return self.hooks["ChatEdit_HandleChatType"](editBox, msg, command, send)
end

function Prat_Alias:ChatEdit_ParseText(editBox, send)
	local text	= editBox:GetText()
	text		= self:parseForAliases(text)

	editBox:SetText(text)
end

function Prat_Alias:parseForAliases(text)
	if (text and type(text) == 'string' and strsub(text, 1, 1) == "/") then
		local command	= string.match(text, "/(%w+)%s", 1) or string.match(text, "/(%w+)$", 1)

		if self.Aliases[command] and not self.WontAlias[command] then
			local alias	= util:escpat(self.Aliases[command])

			local pat	= string.format('/(%s)', command)
			local replace	= string.format('/%s', alias)

			text	= text:gsub(pat, replace, 1)
		end
	end

	return text
end
