function LeoTrainer.ShowUI()
    SCENE_MANAGER:ShowTopLevel(LeoTrainerWindow)
    LeoTrainerWindow:SetHidden(false)
    LeoTrainer.hidden = false;
end

function LeoTrainer.HideUI()
    SCENE_MANAGER:HideTopLevel(LeoTrainerWindow)
    LeoTrainerWindow:SetHidden(true)
    LeoTrainer.hidden = true;
end

function LeoTrainer.ToggleUI()
    SCENE_MANAGER:ToggleTopLevel(LeoTrainerWindow)
    if LeoTrainer.hidden then
        LeoTrainer.ShowUI()
    else
        LeoTrainer.HideUI()
    end
end

function LeoTrainer:OnMouseEnterTrait(control)
    InitializeTooltip(ItemTooltip, control, LEFT, 5, 0)
    ItemTooltip:SetLink(ZO_LinkHandler_CreateLink("",nil,ITEM_LINK_TYPE,control.materialItemID,30,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
    ItemTooltip:SetHidden(false)
end

local function copy(obj, seen)
    if type(obj) ~= 'table' then return obj end
    if seen and seen[obj] then return seen[obj] end
    local s = seen or {}
    local res = setmetatable({}, getmetatable(obj))
    s[obj] = res
    for k, v in pairs(obj) do res[copy(k, s)] = copy(v, s) end
    return res
end

LeoTrainerQueueList = ZO_SortFilterList:Subclass()
function LeoTrainerQueueList:New(control)

    ZO_SortFilterList.InitializeSortFilterList(self, control)

    local sorterKeys =
    {
        ["trainer"] = {},
        ["trainee"] = { tiebreaker = "trainer"},
        ["researchName"] = { tiebreaker = "trainee"},
        ["item"] = { tiebreaker = "researchName"},
    }

    self.masterList = {}
    self.currentSortKey = "researchName"
    ZO_ScrollList_AddDataType(self.list, 1, "LeoTrainerQueueListTemplate", 32, function(control, data) self:SetupEntry(control, data) end)

    self.sortFunction = function(listEntry1, listEntry2)
        return ZO_TableOrderingFunction(listEntry1.data, listEntry2.data, self.currentSortKey, sorterKeys, self.currentSortOrder)
    end

    return self
end

function LeoTrainerQueueList:SetupEntry(control, data)

    control.data = data

    control.trainer = GetControl(control, "Trainer")
    control.trainer:SetText(data.trainer)

    control.trainee = GetControl(control, "Trainee")
    control.trainee:SetText(data.trainee)

    control.researchName = GetControl(control, "Trait")
    control.researchName:SetText(data.researchName)

    control.item = GetControl(control, "Item")
    control.item:SetText(data.itemLink)
    control.item:SetHandler('OnMouseUp', function(control, button, upInside)
        if upInside == false then return end
        if button == MOUSE_BUTTON_INDEX_RIGHT then
            LeoTrainer.RemoveFromQueue(data.queueIndex)
        end
    end)

    ZO_SortFilterList.SetupRow(self, control, data)
end

function LeoTrainerQueueList:BuildMasterList()
    self.masterList = {}
    local list = LeoTrainer.GetQueue()
    if list then
        for k, v in ipairs(list) do
            local data = copy(v)
            data.queueIndex = k
            table.insert(self.masterList, data)
        end
    end
end

function LeoTrainerQueueList:SortScrollList()
    local scrollData = ZO_ScrollList_GetDataList(self.list)
    table.sort(scrollData, self.sortFunction)
end

function LeoTrainerQueueList:FilterScrollList()

    local scrollData = ZO_ScrollList_GetDataList(self.list)
    ZO_ClearNumericallyIndexedTable(scrollData)

    for i = 1, #self.masterList do
        local data = self.masterList[i]
        local canShow = true
        if LeoTrainer.filterKnown == true and LeoTrainer.IKnowTrait(data.craft, data.line, data.trait) == false then
            canShow = false
        end
        if canShow == true and LeoTrainer.inStation > 0 and data.craft ~= LeoTrainer.inStation then
            canShow = false
        end
        if canShow then
            table.insert(scrollData, ZO_ScrollList_CreateDataEntry(1, data))
        end
    end
end

local function addLine(tooltip, text, color, alignment)
    if not color then color = ZO_TOOLTIP_DEFAULT_COLOR end
    local r, g, b = color:UnpackRGB()
    tooltip:AddLine(text, "", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, alignment, alignment ~= TEXT_ALIGN_LEFT)
end

local function addLineCenter(tooltip, text, color)
    if not color then color = ZO_TOOLTIP_DEFAULT_COLOR end
    addLine(tooltip, text, color, TEXT_ALIGN_CENTER)
end

local function addLineTitle(tooltip, text, color)
    if not color then color = ZO_SELECTED_TEXT end
    local r, g, b = color:UnpackRGB()
    tooltip:AddLine(text, "ZoFontHeader3", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, TEXT_ALIGN_CENTER, true)
end

local function addLineSubTitle(tooltip, text, color, alignment)
    if not color then color = ZO_SELECTED_TEXT end
    if not alignment then alignment = TEXT_ALIGN_CENTER end
    local r, g, b = color:UnpackRGB()
    tooltip:AddLine(text, "ZoFontWinH3", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, alignment, true)
end

function LeoTrainer.CreateUI()
    local traitIcons = {}
    for traitItemIndex = 1, GetNumSmithingTraitItems() do
        local traitType, _, icon, _, _, _, _ = GetSmithingTraitItemInfo(traitItemIndex)
        if traitType and traitType ~= ITEM_TRAIT_TYPE_NONE then
            traitIcons[traitType] = icon
        end
    end

    for _,craft in pairs(LeoAltholic.craftResearch) do
        local panel = WINDOW_MANAGER:GetControlByName('LeoTrainerWindowCraft'..craft.."Panel")
        if craft == CRAFTING_TYPE_JEWELRYCRAFTING then
            panel = WINDOW_MANAGER:GetControlByName('LeoTrainerWindowCraft6PanelCraft7Panel')
        end
        for line = 1, GetNumSmithingResearchLines(craft) do
            local lineName, lineIcon, numTraits = GetSmithingResearchLineInfo(craft, line)
            local labelLine = panel:GetNamedChild('Line' .. line)
            labelLine.tooltip = lineName
            labelLine:SetText("|t30:30:"..lineIcon.."|t")
            for trait = 1, numTraits do
                local traitType = GetSmithingResearchLineTraitInfo(craft, line, trait)
                local traitName = GetString('SI_ITEMTRAITTYPE', traitType)
                local i = trait
                local posY = 14 + (trait * 28)
                if craft == CRAFTING_TYPE_BLACKSMITHING and line <= 7 then
                    i = i + 10
                elseif craft == CRAFTING_TYPE_WOODWORKING and line <= 5 then
                    i = i + 10
                end
                local labelTrait = panel:GetNamedChild('Trait' .. i)
                labelTrait:SetText(traitName .. " |t28:28:"..traitIcons[traitType].."|t")
                labelTrait.materialItemID = LeoTrainer.const.traitMaterials[traitType]

                local t = WINDOW_MANAGER:CreateControl('LeoTrainerWindowCraft'..craft..'Line'..line..'Trait'..trait..'Texture', labelLine, CT_TEXTURE)
                t:SetAnchor(CENTER, labelLine, CENTER, 0, posY)
                t:SetDimensions(25,25)

                local myself, known, unknown, all = LeoTrainer.GetCharKnowsTrait(craft, line, trait)
                local icon
                local color
                if myself == true then
                    icon = 'esoui/art/buttons/accept_up.dds'
                    if #unknown == 0 then
                        color = {0,1,0,1}
                    else
                        color = {1,1,0,1}
                        --icon = 'esoui/art/buttons/pointsplus_up.dds'
                    end
                else
                    icon = 'esoui/art/buttons/decline_up.dds'
                    if #known == 0 then
                        color = {1,0,0,1}
                    else
                        color = {1,0.7,0,1}
                    end
                end

                t:SetColor(unpack(color))
                t:SetMouseEnabled(true)
                t:SetTexture(icon)
                t:SetHandler('OnMouseEnter', function(self)
                    InitializeTooltip(InformationTooltip, self, LEFT, 5, 0)
                    addLineTitle(InformationTooltip, lineName .." - ".. traitName, ZO_TOOLTIP_DEFAULT_COLOR)
                    ZO_Tooltip_AddDivider(InformationTooltip)

                    addLineSubTitle(InformationTooltip, "Trainers", ZO_TOOLTIP_DEFAULT_COLOR, TEXT_ALIGN_LEFT)
                    if #known > 0 then
                        for _, charName in pairs(known) do
                            addLine(InformationTooltip, charName, LeoTrainer.const.colors.green)
                        end
                    else
                        addLine(InformationTooltip, "None", LeoTrainer.const.colors.red)
                    end

                    addLineSubTitle(InformationTooltip, "Trainees", ZO_TOOLTIP_DEFAULT_COLOR, TEXT_ALIGN_LEFT)
                    if #unknown > 0 then
                        for _, charName in pairs(unknown) do
                            addLine(InformationTooltip, charName, LeoTrainer.const.colors.red)
                        end
                    else
                        addLine(InformationTooltip, "None", LeoTrainer.const.colors.green)
                    end
                    if #known > 0 and #unknown > 0 then
                        text = "Queue for EACH trainee"
                        addLine(InformationTooltip, zo_iconTextFormat("esoui/art/icons/icon_lmb.dds", 26, 26, text))
                        --addLine(InformationTooltip, zo_iconTextFormat("esoui/art/icons/icon_rmb.dds", 26, 26, "Choose trainees"))
                    end
                    InformationTooltip:SetHidden(false)
                end)
                t:SetHandler('OnMouseExit', function(self)
                    ClearTooltip(InformationTooltip)
                    InformationTooltip:SetHidden(true)
                end)
                if #known > 0 and #unknown > 0 then
                    t:SetHandler('OnMouseUp', function(control, button, upInside)
                        if not upInside then return end
                        if button == MOUSE_BUTTON_INDEX_RIGHT then
                            return
                        end
                        if button == MOUSE_BUTTON_INDEX_LEFT and #unknown > 0 then
                            --local matReq = LeoTrainer.const.materialRequirements[craft][line]
                            local styleId = LeoTrainer.maxStyle(line)
                            for _, charName in pairs(unknown) do
                                LeoTrainer.AddToQueue({
                                    trainer = "Anyone",
                                    trainee = charName,
                                    craft = craft,
                                    line = line,
                                    trait = trait,
                                    patternIndex = -1,
                                    materialIndex = 1,
                                    materialQuantity = -1,
                                    itemStyleId = styleId,
                                    traitIndex = traitType,
                                    useUniversalStyleItem = false,
                                    researchName = lineName .. " " .. traitName,
                                    itemLink = LeoTrainer.const.craftItems[craft][line][trait]
                                })
                            end
                            LeoTrainer.queueScroll:RefreshData()
                        end
                    end)
                end
            end
        end
    end
    LeoTrainer.queueScroll = LeoTrainerQueueList:New(LeoTrainerWindowQueuePanelQueueScroll)
    LeoTrainer.queueScroll:RefreshData()
end

function LeoTrainer.GetQueue()
    return LeoTrainer.savedVariables.queue
end

function LeoTrainer.ClearQueue()
    LeoTrainer.savedVariables.queue = {}
    LeoTrainer.queueScroll:RefreshData()
end

function LeoTrainer.AddToQueue(data)
    table.insert(LeoTrainer.savedVariables.queue, data)
end

function LeoTrainer.RemoveFromQueue(pos)
    table.remove(LeoTrainer.savedVariables.queue, pos)
    LeoTrainer.queueScroll:RefreshData()
end

function LeoTrainer.FillSlots()
    local knownCount = {}
    local traitsKnown = {}
    for _, char in pairs(LeoAltholic.GetCharacters()) do
        knownCount[char.bio.name] = {}
        for _,craft in pairs(LeoAltholic.craftResearch) do
            if LeoTrainer.isTrackingSkill(char.bio.name, craft) and LeoTrainer.canFillSlotWithSkill(char.bio.name, craft) then
                local researching = 0
                knownCount[char.bio.name][craft] = {}
                if traitsKnown[craft] == nil then traitsKnown[craft] = {} end
                local lower = {}
                for line = 1, GetNumSmithingResearchLines(craft) do
                    local lineName, lineIcon = GetSmithingResearchLineInfo(craft, line)
                    knownCount[char.bio.name][craft][line] = 0
                    local firstUnknownTrait = {
                        id = 0,
                        name = ''
                    }
                    if traitsKnown[craft][line] == nil then traitsKnown[craft][line] = {} end
                    for trait = 1, LeoAltholic.maxTraits do
                        local traitType = GetSmithingResearchLineTraitInfo(craft, line, trait)
                        local traitData = char.research[craft][line][trait] or false
                        local traitName = GetString('SI_ITEMTRAITTYPE', traitType)

                        if traitsKnown[craft][line][trait] == nil then
                            local _, known, unknown, all = LeoTrainer.GetCharKnowsTrait(craft, line, trait)
                            traitsKnown[craft][line][trait] = known
                        end
                        if type(traitData) == 'number' and GetDiffBetweenTimeStamps(traitData, GetTimeStamp()) > 0 then
                            researching = researching + 1
                        elseif (type(traitData) == 'number' and GetDiffBetweenTimeStamps(traitData, GetTimeStamp()) <= 0) or traitData == true then
                            knownCount[char.bio.name][craft][line] = knownCount[char.bio.name][craft][line] + 1
                        elseif traitData == false and firstUnknownTrait.id == 0 then
                            firstUnknownTrait.id = trait
                            firstUnknownTrait.name = traitName
                        end
                    end
                    if firstUnknownTrait.id > 0 and #lower < char.research[craft].max then
                        table.insert(lower, {
                            line = line,
                            lineName = lineName,
                            count = knownCount[char.bio.name][craft][line],
                            trait = firstUnknownTrait.id,
                            traitName = firstUnknownTrait.name
                        })
                    elseif firstUnknownTrait.id > 0 then
                        local changed = false
                        for i = 1, #lower do
                            if changed == false and lower[i].count > knownCount[char.bio.name][craft][line] then
                                lower[i].line = line
                                lower[i].lineName = lineName
                                lower[i].count = knownCount[char.bio.name][craft][line]
                                lower[i].trait = firstUnknownTrait.id
                                lower[i].traitName = firstUnknownTrait.name
                                changed = true
                            end
                        end
                    end
                end
                if #lower > 0 and researching < char.research[craft].max then
                    table.sort(lower, function(a, b)
                        return a.count < b.count
                    end)
                    local styleId = LeoTrainer.maxStyle(lower.line)
                    local max = math.min(#lower, char.research[craft].max-researching)
                    for i = 1, max do
                        if #traitsKnown[craft][lower[i].line][lower[i].trait] == 0 then
                            LeoTrainer.log("No one knows "..lower[i].lineName .. " " .. lower[i].traitName.." for " ..char.bio.name.."'s training.")
                        else
                            local traitType = GetSmithingResearchLineTraitInfo(craft, lower[i].line, lower[i].trait)
                            LeoTrainer.AddToQueue({
                                trainer = "Anyone",
                                trainee = char.bio.name,
                                craft = craft,
                                line = lower[i].line,
                                trait = lower[i].trait,
                                patternIndex = -1,
                                materialIndex = 1,
                                materialQuantity = -1,
                                itemStyleId = styleId,
                                traitIndex = traitType,
                                useUniversalStyleItem = false,
                                researchName = lower[i].lineName .. " " .. lower[i].traitName,
                                itemLink = LeoTrainer.const.craftItems[craft][lower[i].line][lower[i].trait]
                            })
                        end
                    end
                    LeoTrainer.queueScroll:RefreshData()
                end
            end
        end
    end
    LeoTrainer.log("Done filling empty research slots.")
end