--[[
  * AutoInvite
  * Author: Sasky
]]--

if AutoInvite == nil then
    AutoInvite = {}
end
AutoInvite.AddonId = "AutoInvite"

------------------------------------------------
--- Utility functions
------------------------------------------------
local function b(v) if v then return "T" else return "F" end end
local function nn(val) if val == nil then return "NIL" else return val end end
local function dbg(msg) if AutoInvite.debug then d("|c999999" .. msg) end end

AutoInvite.isCyrodiil = function(unit)
    if unit == nil then unit = "player" end
    dbg("Current zone: '" .. GetUnitZone(unit) .. "'")
    return GetUnitZone(unit) == "Cyrodiil"
end

AutoInvite.guildLookup = function(channel, acctName)
    local guildId = 0
    if channel == CHAT_CHANNEL_GUILD_1 or channel == CHAT_CHANNEL_OFFICER_1 then guildId = GetGuildId(1) end
    if channel == CHAT_CHANNEL_GUILD_2 or channel == CHAT_CHANNEL_OFFICER_2 then guildId = GetGuildId(2) end
    if channel == CHAT_CHANNEL_GUILD_3 or channel == CHAT_CHANNEL_OFFICER_3 then guildId = GetGuildId(3) end
    if channel == CHAT_CHANNEL_GUILD_4 or channel == CHAT_CHANNEL_OFFICER_4 then guildId = GetGuildId(4) end
    if channel == CHAT_CHANNEL_GUILD_5 or channel == CHAT_CHANNEL_OFFICER_5 then guildId = GetGuildId(5) end

    if guildId == 0 then d("Error - couldn't invite on channel: " .. channel) end

    local aName
    for i=1,GetNumGuildMembers(guildId) do
        aName = GetGuildMemberInfo(guildId,i)
        if aName == acctName then
            local hasChar, charName, zone = GetGuildMemberCharacterInfo(guildId,i)
            -- Might use zone check to NOT auto-invite people outside current zone if leader in Cyrodiil

            if not hasChar then
                d("Could not find player name for " .. acctName .. ". Please manually invite.")
                return ""
            end

            charName = charName:gsub("%^.+", "")

            if AutoInvite.cfg.cyrCheck then
                dbg("In Cyrodiil? " .. b(AutoInvite.isCyrodiil()) .. " / Zone: " .. zone)

                if AutoInvite.isCyrodiil() and zone ~= "Cyrodiil" then
                    d("Player " .. charName .. " is not in Cyrodiil but in " .. zone)
                    d("Blocking invite to prevent crashes.")
                    return ""
                end
            end

            return charName
        end
    end
end


AutoInvite.kickTable = {}
function AutoInvite.checkOffline()
    local now = GetTimeStamp()
    for i=1,GetGroupSize() do
        local tag = GetGroupUnitTagByIndex(i)
        if not IsUnitOnline(tag) then
            AutoInvite.kickTable[GetUnitName(tag)] = now
        end
    end
end

--Since KickByName doesn't seem to be working
function AutoInvite.kickByName(name)
    AutoInvite.kickTable[name] = nil
    for i=1,GetGroupSize() do
        local tag = GetGroupUnitTagByIndex(i)
        if GetUnitName(tag) == name then
            GroupKick(tag)
            return
        end
    end
    d("No one named " .. name .. " found in group scan. Please manually kick.")
end

------------------------------------------------
--- Event handlers
------------------------------------------------
AutoInvite.callback = function(_, messageType, from, message)
	if not AutoInvite.enabled or not AutoInvite.listening then
		return
	end

    --TODO: Switch to queue-based with timeouts so don't send out too many invites
	if GetGroupSize() >= AutoInvite.cfg.maxSize then
		d("Group full. Disabling AutoInvite")
        AutoInvite.stopListening()
	end

	if string.lower(message) == AutoInvite.cfg.watchStr and from ~= nil and from ~= "" then
		if (messageType >= CHAT_CHANNEL_GUILD_1 and messageType <= CHAT_CHANNEL_OFFICER_5) then
			from = AutoInvite.guildLookup(messageType, from)
			if from == "" then return end
        end

        --TODO: Add friends list lookup

		from = from:gsub("%^.+", "")
		d("Sending invite to " .. from)
		GroupInvite(from)
		GroupInviteByName(from)
	end

	--d("Checking message '" .. string.lower(message) .."' ?= '" .. AutoInvite.cfg.watchStr .."'")
end

AutoInvite.playerLeave = function()
    if AutoInvite.enabled and AutoInvite.cfg.restart and GetGroupSize() < AutoInvite.cfg.maxSize then
        AutoInvite.startListening()
    end

    local unitName = GetUnitName(unitTag):gsub("%^.+", "")
    AutoInvite.kickTable[unitName] = nil
end

AutoInvite.offlineEvent = function(_, unitTag, connectStatus)
    local unitName = GetUnitName(unitTag):gsub("%^.+", "")
    if connectStatus then
        dbg(unitTag .. "/" .. unitName .. " has reconnected")
        AutoInvite.kickTable[unitName] = nil
    else
        dbg(unitTag .. "/" .. unitName .. " has disconnected")
        AutoInvite.kickTable[unitName] = GetTimeStamp()
    end
end

-- tick function: called every 15s
function AutoInvite.kickCheck()
    if not AutoInvite.cfg.autoKick then return end
    local now = GetTimeStamp()
    --d("Check kick")
    for p,t in pairs(AutoInvite.kickTable) do
        local offTime = GetDiffBetweenTimeStamps(now, t)
        if offTime > AutoInvite.cfg.kickDelay then
            dbg("  KICK: " .. p .. " offline for " .. offTime)
            AutoInvite.kickByName(p)
        else
            dbg(p .. " offline for " .. offTime .. " / " .. AutoInvite.cfg.kickDelay)
        end
    end
end

------------------------------------------------
--- Main interface
------------------------------------------------
AutoInvite.disable = function()
	AutoInvite.enabled = false
    AutoInvite.stopListening()
    EVENT_MANAGER:UnregisterForUpdate("AutoInviteKickCheck")
end

AutoInvite.stopListening = function()
    EVENT_MANAGER:UnregisterForEvent(AutoInvite.AddonId, EVENT_CHAT_MESSAGE_CHANNEL)
    AutoInvite.listening = false
end

AutoInvite.startListening = function()
    if not AutoInvite.enabled then
        AutoInvite.enabled = true
        AutoInvite.checkOffline()
        EVENT_MANAGER:RegisterForUpdate("AutoInviteKickCheck", 1000, AutoInvite.kickCheck)
    end

	if not AutoInvite.listening and GetGroupSize() < AutoInvite.cfg.maxSize then
		--Add handler
		EVENT_MANAGER:RegisterForEvent(AutoInvite.AddonId, EVENT_CHAT_MESSAGE_CHANNEL, AutoInvite.callback)
		AutoInvite.listening = true
    end

    --First check. When option, will have to check option for disable
	d("AutoInvite listening on string '" .. AutoInvite.cfg.watchStr .. "'")
end

------------------------------------------------
--- Command line
------------------------------------------------


-- print command usage
AutoInvite.help = function()
    d("AutoInvite - command '/ai <str>'. Usage")
    d("  '/ai foo' - autoInvite on 'foo' command'")
    d("  '/ai help' - show this menu")
    d("  '/ai' - turn off autoInvite (auto on group full)")
    return
end

--Main interaction switch
SLASH_COMMANDS["/ai"] = function(str)
    if not str or str == "" or str == "help" then
        if not AutoInvite.listening or str == "help" then
            AutoInvite.help()
            return
        end
        d("Disabling AutoInvite")
        AutoInvite.disable()
        return
    end
    AutoInvite.cfg.watchStr = string.lower(str)
    AutoInvite.startListening()
end


SLASH_COMMANDS["/zdb9"] = function()
    d("In Cyrodiil? " .. b(AutoInvite.isCyrodiil()))
end

------------------------------------------------
--- Initialization
------------------------------------------------
AutoInvite.init = function()
    EVENT_MANAGER:UnregisterForEvent("AutoInviteInit", EVENT_PLAYER_ACTIVATED)
    if AutoInvite.initDone then return end
    AutoInvite.initDone = true

    local def = {
        maxSize = 24,
        restart = false,
        cyrCheck = false,
        autoKick = false,
        kickDelay = 300,
        watchStr = "",
    }
    AutoInvite.cfg = ZO_SavedVars:NewAccountWide("AutoInviteSettings", 1.0, "config", def)
    EVENT_MANAGER:RegisterForEvent(AutoInvite.AddonId, EVENT_GROUP_MEMBER_LEFT, AutoInvite.playerLeave)
    EVENT_MANAGER:RegisterForEvent(AutoInvite.AddonId, EVENT_GROUP_MEMBER_CONNECTED_STATUS, AutoInvite.offlineEvent)
    AutoInvite.listening = false
    AutoInvite.enabled = false
    AutoInviteUI.init()
end

EVENT_MANAGER:RegisterForEvent("AutoInviteInit", EVENT_PLAYER_ACTIVATED, AutoInvite.init)

-- Debug commands
SLASH_COMMANDS["/aik"] = function()
    local now = GetTimeStamp()
    --d("Current timestamp: " .. GetTimeStamp())
    --d("Offline players:")
    for p,t in pairs(AutoInvite.kickTable) do
        local offTime = now - t
        if offTime > 300 then
            dbg("  KICK: " .. p .. " offline for " .. now - t)
        else
            dbg("  " .. p .. " offline for " .. now - t)
        end
    end
end

SLASH_COMMANDS["/aidebug"] = function()
    d("|cFF0000Beginning debug mode for AutoInvite.")
    AutoInvite.debug = true
end

SLASH_COMMANDS["/aikick"] = function()
    local now = GetTimeStamp()
    d("Offline players:")
    for p,t in pairs(AutoInvite.kickTable) do
        local offTime = now - t
        if offTime > AutoInvite.cfg.kickDelay then
            d("  KICK: " .. p .. " offline for " .. now - t)
            GroupKickByName(p)
        else
            d("  " .. p .. " offline for " .. now - t)
        end
    end
end