local Research = {
    queue = {},
    isResearching = false,
    keybindStripDescriptor =
    {
        {
            name = "Research",
            keybind = "LEOTRAINER_RESEARCH",
            callback = function() LeoTrainer.research.ResearchNext() end--,
        },
        alignment = KEYBIND_STRIP_ALIGN_CENTER,
    }
}

function Research.GetResearchData(link)
    if not link then return false end

    local craftSkill, line
    local trait = GetItemLinkTraitInfo(link)
    local equipType = GetItemLinkEquipType(link)

    if trait == ITEM_TRAIT_TYPE_NONE or trait == ITEM_TRAIT_TYPE_ARMOR_INTRICATE or trait == ITEM_TRAIT_TYPE_ARMOR_ORNATE
        or trait == ITEM_TRAIT_TYPE_WEAPON_INTRICATE or trait == ITEM_TRAIT_TYPE_WEAPON_ORNATE
        or trait == ITEM_TRAIT_TYPE_JEWELRY_INTRICATE or trait == ITEM_TRAIT_TYPE_JEWELRY_ORNATE then return false end

    local armorType = GetItemLinkArmorType(link)
    local weaponType = GetItemLinkWeaponType(link)
    if trait == ITEM_TRAIT_TYPE_ARMOR_NIRNHONED then trait = 19 end
    if trait == ITEM_TRAIT_TYPE_WEAPON_NIRNHONED then trait = 9 end
    if weaponType == WEAPONTYPE_AXE then craftSkill = CRAFTING_TYPE_BLACKSMITHING; line = 1;
    elseif weaponType == WEAPONTYPE_HAMMER then craftSkill = CRAFTING_TYPE_BLACKSMITHING; line = 2;
    elseif weaponType == WEAPONTYPE_SWORD then craftSkill = CRAFTING_TYPE_BLACKSMITHING; line = 3
    elseif weaponType == WEAPONTYPE_TWO_HANDED_AXE then craftSkill = CRAFTING_TYPE_BLACKSMITHING; line = 4;
    elseif weaponType == WEAPONTYPE_TWO_HANDED_HAMMER then craftSkill = CRAFTING_TYPE_BLACKSMITHING; line = 5;
    elseif weaponType == WEAPONTYPE_TWO_HANDED_SWORD then craftSkill = CRAFTING_TYPE_BLACKSMITHING; line = 6;
    elseif weaponType == WEAPONTYPE_DAGGER then craftSkill = CRAFTING_TYPE_BLACKSMITHING; line = 7;
    elseif weaponType == WEAPONTYPE_BOW then craftSkill = CRAFTING_TYPE_WOODWORKING; line = 1;
    elseif weaponType == WEAPONTYPE_FIRE_STAFF then craftSkill = CRAFTING_TYPE_WOODWORKING; line = 2;
    elseif weaponType == WEAPONTYPE_FROST_STAFF then craftSkill = CRAFTING_TYPE_WOODWORKING; line = 3;
    elseif weaponType == WEAPONTYPE_LIGHTNING_STAFF then craftSkill = CRAFTING_TYPE_WOODWORKING; line = 4;
    elseif weaponType == WEAPONTYPE_HEALING_STAFF then craftSkill = CRAFTING_TYPE_WOODWORKING; line = 5;
    elseif weaponType == WEAPONTYPE_SHIELD then craftSkill = CRAFTING_TYPE_WOODWORKING; line = 6;trait=trait-10;
    elseif equipType == EQUIP_TYPE_CHEST then line = 1
    elseif equipType == EQUIP_TYPE_FEET then line = 2
    elseif equipType == EQUIP_TYPE_HAND then line = 3
    elseif equipType == EQUIP_TYPE_HEAD then line = 4
    elseif equipType == EQUIP_TYPE_LEGS then line = 5
    elseif equipType == EQUIP_TYPE_SHOULDERS then line = 6
    elseif equipType == EQUIP_TYPE_WAIST then line = 7
    end

    if equipType == EQUIP_TYPE_NECK or equipType == EQUIP_TYPE_RING then
        craftSkill = CRAFTING_TYPE_JEWELRYCRAFTING
        line = equipType == EQUIP_TYPE_NECK and 1 or 2
        if trait == ITEM_TRAIT_TYPE_JEWELRY_ARCANE then trait = 1
        elseif trait == ITEM_TRAIT_TYPE_JEWELRY_HEALTHY then trait = 2
        elseif trait == ITEM_TRAIT_TYPE_JEWELRY_ROBUST then trait = 3
        elseif trait == ITEM_TRAIT_TYPE_JEWELRY_TRIUNE then trait = 4
        elseif trait == ITEM_TRAIT_TYPE_JEWELRY_INFUSED then trait = 5
        elseif trait == ITEM_TRAIT_TYPE_JEWELRY_PROTECTIVE then trait = 6
        elseif trait == ITEM_TRAIT_TYPE_JEWELRY_SWIFT then trait = 7
        elseif trait == ITEM_TRAIT_TYPE_JEWELRY_HARMONY then trait = 8
        elseif trait == ITEM_TRAIT_TYPE_JEWELRY_BLOODTHIRSTY then trait = 9
        end
    else
        if armorType == ARMORTYPE_HEAVY then craftSkill = CRAFTING_TYPE_BLACKSMITHING; line = line + 7; trait = trait - 10; end
        if armorType == ARMORTYPE_MEDIUM then craftSkill = CRAFTING_TYPE_CLOTHIER; line = line + 7; trait = trait - 10; end
        if armorType == ARMORTYPE_LIGHT then craftSkill = CRAFTING_TYPE_CLOTHIER; trait = trait - 10; end
    end
    if craftSkill and line and trait then return craftSkill, line, trait
    else return false end
end

local function canIResearchItem(itemLink)
    local craftSkill, line, trait = Research.GetResearchData(itemLink)

    if craftSkill == nil then return false end

    local isKnown, isResearching = LeoAltholic.ResearchStatus(craftSkill, line, trait)
    if isKnown then
        -- LeoTrainer.debug('already known')
        return false
    end

    if isResearching then
        -- LeoTrainer.debug('is researching')
        return false
    end

    return true
end

function Research.CanItemBeResearched(bagId, slotIndex, itemLink, craftSkill)
    if not FCOIS then return false end

    if itemLink == nil then itemLink = GetItemLink(bagId, slotIndex) end

    local type = GetItemType(bagId, slotIndex)
    if type ~= ITEMTYPE_ARMOR and type ~= ITEMTYPE_WEAPON then return false end

    if FCOIS.IsIconEnabled(FCOIS_CON_ICON_RESEARCH) and not FCOIS.IsMarked(bagId, slotIndex, FCOIS_CON_ICON_RESEARCH) then
        -- LeoTrainer.debug(itemLink .. " not marked for research")
        return false
    end

    if LeoTrainer.settings.research.allowCrafted and GetItemCreatorName(bagId, slotIndex) == nil then
        -- LeoTrainer.debug(itemLink .. " not crafted")
        return false
    end

    local hasSet, setName = GetItemLinkSetInfo(itemLink)
    if not LeoTrainer.settings.research.allowSets and hasSet then
        -- LeoTrainer.debug(itemLink .. " is from set " .. setName)
        return false
    end

    if craftSkill ~= nil and craftSkill > 0 and LeoTrainer.settings.research.maxQuality[craftSkill] < GetItemQuality(bagId, slotIndex) then
        -- LeoTrainer.debug(itemLink .. " is with higher quality")
        return false
    end

    return true
end

function Research.ScanBags()
    local list = {}
    local items = SHARED_INVENTORY:GenerateFullSlotData(nil, BAG_BACKPACK, BAG_BANK, BAG_SUBSCRIBER_BANK)
    for _, data in pairs(items) do
        local itemLink = GetItemLink(data.bagId, data.slotIndex, LINK_STYLE_BRACKETS)
        local craftSkill, line, trait = LeoTrainer.research.GetResearchData(itemLink)
        if LeoTrainer.research.CanItemBeResearched(data.bagId, data.slotIndex, itemLink, craftSkill) then
            table.insert(list, {
                bagId = data.bagId,
                slotIndex = data.slotIndex,
                itemLink = itemLink,
                craftSkill = craftSkill,
                line = line,
                trait = trait,
                selected = false
            })
        end
    end

    return list
end

function Research.HandleItem(bagId, slotIndex, itemLink, craftSkill)

    local itemCraftSkill, line, trait = Research.GetResearchData(itemLink)

    if itemCraftSkill ~= craftSkill then return false end

    if not Research.CanItemBeResearched(bagId, slotIndex, itemLink, craftSkill) then return false end

    if not canIResearchItem(itemLink) then return false end

    if LeoTrainer.settings.research.listInChat then
        LeoTrainer.log("Can be researched: " .. itemLink)
    end

    table.insert(Research.queue, {
        bagId = bagId,
        slotIndex = slotIndex,
        itemLink = itemLink,
        craftSkill = craftSkill,
        line = line,
        trait = trait,
        selected = false,
        isResearching = false
    })

    return true
end

function Research.OnResearchComplete(_, craftSkill)
    EVENT_MANAGER:UnregisterForUpdate(LeoTrainer.name .. ".ResearchTimeout")
    EVENT_MANAGER:UnregisterForEvent(LeoTrainer.name, EVENT_CRAFT_COMPLETED)

    if not Research.isResearching then
        LeoTrainer.nextStage(true)
        return
    end

    local myName = GetUnitName("player")
    local researching, total = LeoAltholic.GetResearchCounters(craftSkill)
    for j, data in pairs(Research.queue) do
        if data.isResearching == true then
            table.remove(Research.queue, j)
            break
        end
    end
    Research.isResearching = false

    if researching >= total then
        LeoTrainer.debug("No empty research slots.")
        LeoTrainer.nextStage(true)
        return
    end

    if #Research.queue > 0 then
        zo_callLater(function() Research.ResearchNext(craftSkill) end, 200)
        return
    end

    LeoTrainer.nextStage(true)
end

local function OnResearchTimeout(craftSkill)
    return function()
        ZO_Alert(UI_ALERT_CATEGORY_ALERT, nil, "Timeout during research")
        EVENT_MANAGER:UnregisterForEvent(LeoTrainer.name, EVENT_CRAFT_COMPLETED)
        LeoTrainer.nextStage()
    end
end

local function get(tbl, k, ...)
    if tbl == nil or tbl[k] == nil then return nil end
    if select('#', ...) == 0 then return tbl[k] end
    return get(tbl[k], ...)
end

local lineList

function Research.ResearchNext(craftSkill)
    Research.isResearching = false

    if craftSkill == nil then craftSkill = GetCraftingInteractionType() end

    if #Research.queue == 0 then
        LeoTrainer.debug("Nothing on research queue")
        LeoTrainer.nextStage()
        return
    end

    if not LeoTrainer.IsInUsableStation() or not LeoAltholic.HasStillResearchFor(craftSkill) then
        LeoTrainer.debug("Nothing to research here.")
        LeoTrainer.nextStage()
        return
    end

    local myName = GetUnitName("player")
    local researching, total = LeoAltholic.GetResearchCounters(craftSkill, myName)
    if researching >= total then
        LeoTrainer.debug("No empty research slots.")
        LeoTrainer.nextStage()
        return
    end
    if IsPerformingCraftProcess() == true then
        LeoTrainer.debug("Still crafting")
        Research.isResearching = true
        zo_callLater(function() Research.ResearchNext(craftSkill) end, 500)
        return
    end

    -- loop through the priority list and items to find a match
    if lineList == nil then lineList = LeoTrainer.GetPriorityLineList(myName, craftSkill) end
    for i, lineData in ipairs(lineList[myName][craftSkill]) do
        if not lineData.added and not lineData.isResearching then
            for j, data in pairs(Research.queue) do
                if lineData.line == data.line then
                    lineList[myName][craftSkill][i].added = true
                    local itemLink = GetItemLink(data.bagId, data.slotIndex, LINK_STYLE_BRACKETS)
                    local traitType = GetItemLinkTraitInfo(itemLink)
                    local traitName = GetString("SI_ITEMTRAITTYPE", traitType)
                    local message = zo_strformat("<<1>> <<2>> (<<3>>)",
                        GetString(SI_GAMEPAD_SMITHING_CURRENT_RESEARCH_HEADER),
                        itemLink,
                        traitName)
                    LeoTrainer.log(message)

                    EVENT_MANAGER:RegisterForUpdate(LeoTrainer.name .. ".ResearchTimeout", 2000, OnResearchTimeout(craftSkill))
                    EVENT_MANAGER:RegisterForEvent(LeoTrainer.name, EVENT_CRAFT_COMPLETED, Research.OnResearchComplete) -- normal craft is handled by LLC

                    Research.isResearching = true
                    Research.queue[j].isResearching = true
                    ResearchSmithingTrait(data.bagId, data.slotIndex)
                    return
                end
            end
        end
    end

    LeoTrainer.debug("No item found for research")
    LeoTrainer.nextStage()
end

function Research.ShouldResearch(craftSkill)
    return LeoAltholic.HasStillResearchFor(craftSkill)
end

function Research.OnStationEnter(craftSkill)
    lineList = nil
    if craftSkill == CRAFTING_TYPE_ENCHANTING or not LeoAltholic.HasStillResearchFor(craftSkill) then
        LeoTrainer.nextStage()
        return
    end

    if #Research.queue > 0 then
        KEYBIND_STRIP:AddKeybindButtonGroup(Research.keybindStripDescriptor)
    end

    if #Research.queue == 0 or not LeoTrainer.settings.research.auto then
        LeoTrainer.nextStage()
        return
    end

    Research.ResearchNext(craftSkill)
end

function Research.OnStationExit(craftSkill)
    EVENT_MANAGER:UnregisterForUpdate(LeoTrainer.name .. ".ResearchTimeout")
    EVENT_MANAGER:UnregisterForEvent(LeoTrainer.name, EVENT_CRAFT_COMPLETED)
    KEYBIND_STRIP:RemoveKeybindButtonGroup(Research.keybindStripDescriptor)
    Research.isResearching = false
    lineList = nil
    Research.queue = {}
end

function Research.Initialize()
    local charList = LeoAltholic.ExportCharacters()

    LeoTrainer.knowledge = {}
    LeoTrainer.missingKnowledge = {}

    for _, char in pairs(charList) do
        if LeoTrainer.data.trackedTraits[char.bio.name] == nil then
            LeoTrainer.data.trackedTraits[char.bio.name] = {}
        end
        if not LeoTrainer.data.fillSlot[char.bio.name] then
            LeoTrainer.data.fillSlot[char.bio.name] = {}
        end
        for _, craftId in pairs(LeoAltholic.craftResearch) do
            LeoTrainer.data.trackedTraits[char.bio.name][craftId] = LeoTrainer.data.trackedTraits[char.bio.name][craftId] or false
            LeoTrainer.data.fillSlot[char.bio.name][craftId] = LeoTrainer.data.fillSlot[char.bio.name][craftId] or false
        end

        for _,craft in pairs(LeoAltholic.craftResearch) do
            if LeoTrainer.knowledge[craft] == nil then LeoTrainer.knowledge[craft] = {} end
            if LeoTrainer.missingKnowledge[craft] == nil then LeoTrainer.missingKnowledge[craft] = {} end
            for line = 1, GetNumSmithingResearchLines(craft) do
                if LeoTrainer.knowledge[craft][line] == nil then LeoTrainer.knowledge[craft][line] = {} end
                if LeoTrainer.missingKnowledge[craft][line] == nil then LeoTrainer.missingKnowledge[craft][line] = {} end
                local _, _, numTraits = GetSmithingResearchLineInfo(craft, line)
                for trait = 1, numTraits do
                    if LeoTrainer.knowledge[craft][line][trait] == nil then LeoTrainer.knowledge[craft][line][trait] = {} end
                    if LeoTrainer.missingKnowledge[craft][line][trait] == nil then LeoTrainer.missingKnowledge[craft][line][trait] = {} end
                    if char.research.done[craft][line][trait] == true then
                        table.insert(LeoTrainer.knowledge[craft][line][trait], char.bio.name)
                    else
                        table.insert(LeoTrainer.missingKnowledge[craft][line][trait], char.bio.name)
                    end
                end
            end
        end
    end
end

LeoTrainer.research = Research