DailyAutoShare              = DailyAutoShare or {}
DAS                         = DailyAutoShare

DAS.name                    = "DailyAutoShare"
DAS.version                 = "3.4.4"
DAS.author                  = "manavortex"
DAS.settings                = {}
DAS.globalSettings          = {}

DAS.shareables              = {}
DAS.bingo                   = {}
DAS.bingoFallback           = {}
DAS.subzones                = {}
DAS.activeZoneQuests        = {}

DAS.questFinisher           = {}
DAS.questStarter            = {}
DAS.questIds                = {}
DAS.activeBingoIndices      = {}
DAS.channelTypes            = {
    [CHAT_CHANNEL_PARTY]    = true,
    [CHAT_CHANNEL_SAY ]     = false,
    [CHAT_CHANNEL_YELL]     = false,
    [CHAT_CHANNEL_ZONE]     = false,
    [CHAT_CHANNEL_WHISPER]  = true,
}

DAS.locale              = GetCVar("language.2")
DAS.autoInviting        = false
DAS.guildInviteText     = nil

DAS_STATUS_COMPLETE     = 0
DAS_STATUS_OPEN         = 1
DAS_STATUS_ACTIVE       = 2
DAS_STATUS_TRACKED      = 3

local activeInCurrentZone   = false
DAS.fullBingoString         = ""
local fullBingoString       = DAS.fullBingoString

UNITTAG_PLAYER              = UNITTAG_PLAYER or "player"

local defaults = {

    debugging                   = false,
    keepInviteUpOnDegroup       = false,

    ["DasControl"] = {
        ["x"] = 0,
        ["y"] = 0,
    },
    ["DasButton"] = {
        ["x"] = 0,
        ["y"] = 0,
    },
    ["inactiveZones"]           = {
        ["hide"]                = true,
    },
    [849] = {
        ["relic"] = {
            ["invisible"] = false,
            ["active"] = true,
        },
        ["hunt"] = {
            ["invisible"] = false,
            ["active"] = true,
        },
        ["delve"] = {
            ["invisible"] = false,
            ["active"] = true,
        },
        ["boss"] = {
            ["invisible"] = false,
            ["active"] = true,
        },
    },
    [980] = {
        ["crow"] = {
            ["invisible"] = false,
            ["active"] = true,
        },
        ["craft"] = {
            ["invisible"] = false,
            ["active"] = true,
        },
        ["delve"] = {
            ["invisible"] = false,
            ["active"] = true,
        },
        ["boss"] = {
            ["invisible"] = false,
            ["active"] = true,
        },
    },

    questShareString            = "I can give a DailyAutoShare for <<1>>, type <<2>> for an instant invite",
    autoAcceptInvite            = false,
    autoAcceptInviteInterval    = 5,

    autoAcceptQuest             = true,
    autoAcceptShared            = true,

    autoDeclineShared           = false,

    autoHide                    = false,
    autoMinimize                = false,

    minimised                   = false,
    locked                      = false,
    hidden                      = false,

    autoShare                   = true,
    autoInvite                  = false,
    autoLeave                   = false,

    useGlobalSettings           = true,

    fontScale                   = 0.8,
    tooltipRight                = false,
    upsideDown                  = false,
    hideCompleted               = false,
    startupMinimized            = true,
    resetAutoShareOnNewGroup    = true,
    lastLookingFor              = "",
    guildInviteNumber           = 1,
    groupInviteDelay            = 500,
    questShareDelay             = 500,
    guildInviteText,
    questShareString            = "I can give a DailyAutoShare for <<1>>, type <<2>> for an instant invite",
    listenInGuilds,
    whisperOnly                 = false,
    whisperString               = "whisper + for an instant invite",
    tracked = {
        [684]  = true,
        [823]  = true,
        [849]  = true,      -- Vvardenfell
        [181]  = false,
        [1011] = true,     -- Summerset
    },
}

local function pointerUpSubzones()

    local settings = DAS.GetSettings()
      -- Summerset
    settings[1012]                      = settings[1011]
    settings[1013]                      = settings[1011]
    settings[1014]                      = settings[1011]
    settings[1015]                      = settings[1011]
    settings[1016]                      = settings[1011]
    settings[1017]                      = settings[1011]
    settings[1018]                      = settings[1011]
    settings[1019]                      = settings[1011]
    settings.tracked[1012]              = settings.tracked[1011]
    settings.tracked[1013]              = settings.tracked[1011]
    settings.tracked[1014]              = settings.tracked[1011]
    settings.tracked[1015]              = settings.tracked[1011]
    settings.tracked[1016]              = settings.tracked[1011]
    settings.tracked[1017]              = settings.tracked[1011]
    settings.tracked[1018]              = settings.tracked[1011]
    settings.tracked[1019]              = settings.tracked[1011]

    -- Gold Coast
    settings[825]                       = settings[823]
    settings[826]                       = settings[823]
    settings.tracked[825]               = settings.tracked[823]
    settings.tracked[826]               = settings.tracked[823]

    -- Capitals
    settings[19]                       = settings[57]
    settings[383]                      = settings[57]
    settings.tracked[19]               = settings.tracked[57]
    settings.tracked[383]              = settings.tracked[57]

    -- Clockwork City
    settings[981]                       = settings[980]
    settings[981]                       = settings[980]
    settings[982]                       = settings[980]
    settings.tracked[982]               = settings.tracked[980]
    settings.tracked[983]               = settings.tracked[980]
    settings.tracked[983]               = settings.tracked[980]

    -- Morrowind
    settings[921]                       = settings[849]
    settings[922]                       = settings[849]
    settings[923]                       = settings[849]
    settings[924]                       = settings[849]
    settings[925]                       = settings[849]
    settings[961]                       = settings[849]
    settings.tracked[921]               = settings.tracked[849]
    settings.tracked[922]               = settings.tracked[849]
    settings.tracked[923]               = settings.tracked[849]
    settings.tracked[924]               = settings.tracked[849]
    settings.tracked[925]               = settings.tracked[849]
    settings.tracked[961]               = settings.tracked[849]

    -- Wrothgar
    settings[689]                       = settings[684]
    settings[690]                       = settings[684]
    settings[691]                       = settings[684]
    settings[692]                       = settings[684]
    settings[693]                       = settings[684]
    settings[694]                       = settings[684]
    settings.tracked[689]               = settings.tracked[684]
    settings.tracked[690]               = settings.tracked[684]
    settings.tracked[691]               = settings.tracked[684]
    settings.tracked[692]               = settings.tracked[684]
    settings.tracked[693]               = settings.tracked[684]
    settings.tracked[694]               = settings.tracked[684]
end

local characterName     = zo_strformat(GetUnitName(UNITTAG_PLAYER))

local em = EVENT_MANAGER

local function debugOut(p1, p2, p3, p4, p5, p6, p7, p8)
    if not DAS.GetDebugMode() then return end
    if (not p2) then d(p1); return; end
    if p8 then
        d(zo_strformat("<<1>> <<2>> <<3>> <<4>> <<5>> <<6>> <<7>> <<8>>", p1, p2, p3, p4, p5, p6, p7, p8))
    elseif p7 then
        d(zo_strformat("<<1>> <<2>> <<3>> <<4>> <<5>> <<6>> <<7>>", p1, p2, p3, p4, p5, p6, p7))
    elseif p6 then
        d(zo_strformat("<<1>> <<2>> <<3>> <<4>> <<5>> <<6>>", p1, p2, p3, p4, p5, p6))
    elseif p5 then
        d(zo_strformat("<<1>> <<2>> <<3>> <<4>> <<5>>", p1, p2, p3, p4, p5))
    elseif p4 then
        d(zo_strformat("<<1>> <<2>> <<3>> <<4>>", p1, p2, p3, p4))
    elseif p3 then
        d(zo_strformat("<<1>> <<2>> <<3>>", p1, p2, p3))
    else
        d(zo_strformat("<<1>> <<2>>", p1, p2))
    end
end
DAS.DebugOut = debugOut
local p = debugOut

function DAS.Report(text)
    if not DAS.GetShutUp() then d(text) end
end

--==============================
--======= Event hooks  =========
--==============================

local function OnGroupTypeChanged(eventCode, unitTag)
    if IsUnitGrouped(UNITTAG_PLAYER) then
        if not DAS.GetAutoShare() and DAS.GetResetAutoShareOnNewGroup() then
            DAS.SetAutoShare(true)
        end
        return
    end

    if DAS.GetStopInviteOnDegroup() then DAS.SetAutoInvite(false) end

end

local function forceRefreshControl()
    DAS.RefreshControl(true)
end

local function OnQuestAdded(eventCode, journalIndex, questName, objectiveName)

    local zoneId = DAS.GetZoneId()
    if not DAS.GetActiveIn(zoneId)          then return end
    if not GetIsQuestSharable(journalIndex) then return end

    local shareables = DAS.shareables[zoneId] or {}
    local bingoIndex = DAS.GetBingoIndexFromQuestName(questName) or 0
    DAS.activeBingoIndices[bingoIndex] = true

    if nil ~= shareables[questName] then
        DAS.LogQuest(questName, false)
        zo_callLater(forceRefreshControl, 700)
    end
end

local questSharePending = false
local questLoopInterval = 500
local function stopAcceptQuestLoop(eventCode, journalIndex)
    if not questSharePending then
        ShareQuest(journalIndex)
        return
    end
    questSharePending = nil ~= GetOfferedQuestShareIds()
    questLoopInterval = 500
end

local function acceptQuestLoop()
    if not questSharePending then return end
    for id, _ in pairs(GetOfferedQuestShareIds()) do
        AcceptSharedQuest(id)
    end
    questLoopInterval = questLoopInterval + questLoopInterval*0.5
    zo_callLater(acceptQuestLoop, questLoopInterval)
end

local function OnQuestShared(eventCode, questId)

    if not DAS.settings.autoAcceptShared or not questId then return end
    local questName =  GetOfferedQuestShareInfo(questId)
    p(zo_strformat("<<1>> \t <<2>>", questId, questName))
    local zoneQuestIds = DAS.questIds[DAS.GetZoneId()] or {}
    if not (zoneQuestIds[questName] or DAS_QUEST_IDS[questId]) and DAS.GetActiveIn(zoneId) then return end

    if zoneQuestIds[questId] then
        AcceptSharedQuest(questId)
        em:RegisterForEvent(DAS.name, EVENT_QUEST_ADDED, stopAcceptQuestLoop)
        zo_callLater(forceRefreshControl, 500)
    end
end

local function OnChatMessage(eventCode, channelType, fromName, messageText, _, fromDisplayName)
    return DAS.OnChatMessage(eventCode, channelType, fromName, messageText, _, fromDisplayName)
end

local function OnPlayerActivated(eventCode)
    local active        = DAS.GetActiveIn()
    DAS.SetHidden(not active)
    DAS.SetAutoInvite(DAS.GetAutoInvite()) -- disables if we aren't group lead
    DAS.SetChatListenerStatus(DAS.autoInviting)
    DAS.guildInviteText = DAS.GetGuildInviteText()
    DAS.cacheChatterData()
end

local function OnUnitCreated(eventCode, unitTag)
    local unitZone = GetZoneId(GetUnitZoneIndex(unitTag))
    if not DAS.GetActiveIn(unitZone) then return end
    if GetUnitDisplayName(unitTag) == cachedDisplayName then return end
    DAS.TryShareActiveDaily(unitZone)
end

local function OnQuestToolUpdate()
    forceRefreshControl()
end

local function OnQuestRemoved(eventCode, isCompleted, journalIndex, questName, zoneIndex, poiIndex, questId)

    local zoneId = DAS.GetZoneId()
    local zoneIds = DAS.questIds[zoneId] or {}
    -- is it a daily quest, and are we logging?
    if not ((zoneIds[questName] or DAS_QUEST_IDS[questId]) and DAS.GetActiveIn(zoneId)) then return end

    DAS.LogQuest(questName, isCompleted)

    -- set auto invite off until the questlog has refreshed
    local autoInvite = DAS.GetAutoInvite()
    DAS.SetAutoInvite(false)

    local bingoIndex = DAS.GetBingoIndexFromQuestName(questName) or 0
    DAS.activeBingoIndices[bingoIndex] = false
    if DAS.trackedIndex == journalIndex then
        DAS.trackedIndex = 99
    end

    zo_callLater(function()
        DAS.SetAutoInvite(autoInvite)
        forceRefreshControl()
        DAS.RefreshLabelsWithDelay()
    end, 5000)
end

local function deleteYesterdaysLog()
    -- kill yesterday's log, we don't need it
    local currentDate = tonumber(GetDate())
    if (nil ~= DAS.globalSettings and nil ~= DAS.globalSettings.lastLogDate) and (DAS.globalSettings.lastLogDate < currentDate) then
    if nil == DAS.Log then DAS.Log = {} end
        DAS.Log[DAS.globalSettings.lastLogDate] = nil
        DAS.globalSettings.lastLogDate = currentDate
    end
end

local alreadyRefreshing = false
local function questRefresh()
    alreadyRefreshing = false
    DAS.RefreshControl(true)
end
local function queueQuestRefresh()
    if alreadyRefreshing then return end
    alreadyRefreshing = true
    zo_callLater(questRefresh, 600)
end

local function hookQuestTracker()
    if FOCUSED_QUEST_TRACKER and FOCUSED_QUEST_TRACKER.ForceAssist then
        ZO_PreHook(FOCUSED_QUEST_TRACKER, "ForceAssist", queueQuestRefresh)
    end

end

--==============================
--= DailyAutoShare_Initialize ==
--==============================

local function RegisterEventHooks()

    DailyAutoShare.Fragment     = ZO_HUDFadeSceneFragment:New(DasControl)

    SCENE_MANAGER:GetScene("hud"  ):AddFragment(DailyAutoShare.Fragment)
    SCENE_MANAGER:GetScene("hudui"):AddFragment(DailyAutoShare.Fragment)
    hookQuestTracker()

    em:RegisterForEvent(DAS.name, EVENT_PLAYER_ACTIVATED,       OnPlayerActivated)

    em:RegisterForEvent(DAS.name, EVENT_QUEST_ADDED,            OnQuestToolUpdate)
    em:RegisterForEvent(DAS.name, EVENT_QUEST_REMOVED,          OnQuestRemoved)
    em:RegisterForEvent(DAS.name, EVENT_TRACKING_UPDATE,        OnQuestToolUpdate)

    em:RegisterForEvent(DAS.name, EVENT_QUEST_ADDED,            OnQuestAdded)
    em:RegisterForEvent(DAS.name, EVENT_QUEST_REMOVED,          OnQuestRemoved)
    em:RegisterForEvent(DAS.name, EVENT_QUEST_SHARED,           OnQuestShared)

    em:RegisterForEvent(DAS.name, EVENT_GROUP_TYPE_CHANGED,     OnGroupTypeChanged)
    em:RegisterForEvent(DAS.name, EVENT_UNIT_CREATED,           OnUnitCreated)
    em:RegisterForEvent(DAS.name, EVENT_UNIT_DESTROYED,         OnGroupTypeChanged)

    em:RegisterForEvent(DAS.name, EVENT_CHAT_MESSAGE_CHANNEL,   OnChatMessage)
    -- DasControl:OnMoveStop
    -- DailyAutoShare.SaveControlLocation(self)
end


local function resetQuests()
    local currentDate = tonumber(GetDate())
    DAS.todaysLog = {}
    DAS.globalSettings.completionLog[currentDate] = DAS.todaysLog
    forceRefreshControl()
end

local typeTable = "table"
local function isEmpty(tbl)
    if not tbl then return true end
    local ret = true
    for key, value in pairs(tbl) do
        if type(value) == typeTable then
            ret = ret and ({} == value or isEmpty(value))
        else
            ret = false
        end
    end
    return ret
end


-- has to be a local var, lua error if not
-- Keep outside of function namespace so we can overwrite it for debugging
local afterEight = tonumber(GetTimeString():sub(0, 2)) >= 08

local function handleLog(forceNoAfterEight)

    local todaysLog = DAS.GetSettingsArray()
    if {} ~= todaysLog then
        todaysLog = ZO_DeepTableCopy(todaysLog, {})
    end

    local allLogs = DAS.globalSettings.completionLog
    local currentDate = tonumber(GetDate())
    allLogs[currentDate] = allLogs[currentDate] or {}

    local logSize, lastDate = NonContiguousCount(DAS.globalSettings.completionLog)

    local counter = 0
    for dateNumber, dateLog in pairs(DAS.globalSettings.completionLog) do
        counter = counter + 1
        if nil ~= dateNumber and dateNumber < currentDate then
            lastDate = dateNumber
        end
        if counter < logSize-2 then
            DAS.globalSettings.completionLog[dateNumber] = nil
        end
    end

    local afterEight = not forceNoAfterEight and tonumber(GetTimeString():sub(0, 2)) >= 08 -- has to be a local var, lua error if not

    if (not afterEight) and isEmpty(DAS.todaysLog) and lastDate ~= currentDate then
        local lastLog = DAS.globalSettings.completionLog[lastDate]
        if null ~= lastLog then
            DAS.globalSettings.completionLog[currentDate] = ZO_DeepTableCopy(lastLog, {})
            for charName, charLog in pairs(DAS.globalSettings.completionLog[currentDate]) do
                for questName, questData in pairs(charLog) do
                    questData.afterEight = false
                end
            end
        end
        -- d(DAS.globalSettings.completionLog[currentDate])

        characterName                   = characterName or GetUnitName(UNITTAG_PLAYER)
        DAS.todaysLog                   = DAS.globalSettings.completionLog[currentDate]
    DAS.todaysCharacterLog              = DAS.todaysLog[characterName]
    end
end
DAS.handleLog = handleLog   -- expose this for debugging purpose

local function minimiseOnStartup()
    DAS.SetMinimized(DAS.GetSettings().startupMinimized)
end
--==============================
--===== Rise, my minion!  ======
--==============================

function DailyAutoShare_Initialize(eventCode, addonName)

    if addonName ~= DAS.name then return end

    DAS.settings        = ZO_SavedVars:New(             "DAS_Settings", 2, "DAS_Settings", defaults)
    DAS.globalSettings  = ZO_SavedVars:NewAccountWide(  "DAS_Globals",  2, "DAS_Globals",  defaults)
    DAS.pdn = GetUnitDisplayName(UNITTAG_PLAYER)

    pointerUpSubzones()
    RegisterEventHooks()

    DAS.CreateMenu(DAS.settings, defaults)
    DAS.CreateGui()

    -- local timetoreset = (GetTimeStamp() - 60*60*7)%86400
    -- zo_callLater(resetQuests, timetoreset)


    handleLog()
    zo_callLater(OnPlayerActivated, 5000)
    zo_callLater(minimiseOnStartup, 5500)
    EVENT_MANAGER:UnregisterForEvent("DailyAutoShare", EVENT_ADD_ON_LOADED)

end


ZO_CreateStringId("SI_BINDING_NAME_TOGGLE_DAS_GUI",  GetString(DAS_SI_TOGGLE))
ZO_CreateStringId("SI_BINDING_NAME_TOGGLE_DAS_LIST", GetString(DAS_SI_MINIMISE))
EVENT_MANAGER:RegisterForEvent("DailyAutoShare", EVENT_ADD_ON_LOADED, DailyAutoShare_Initialize)