--------------------------------------------------
-- Throns Auto Sell
-- Core.lua
--------------------------------------------------

local ADDON_NAME = ...

--------------------------------------------------
-- Defaults
--------------------------------------------------

local Defaults = {
    autoSell = true,
    sellLimit = "",
    skipSoulbound = true,
    sellGreenItems = false,
    sellBlueItems = false,
    repairItems = true
}

--------------------------------------------------
-- Utility
--------------------------------------------------

local function CopyDefaults(source, destination)

    destination = destination or {}

    for key, value in pairs(source) do

        if type(value) == "table" then

            destination[key] =
                CopyDefaults(
                    value,
                    destination[key]
                )

        elseif destination[key] == nil then

            destination[key] = value

        end
    end

    return destination
end

--------------------------------------------------
-- Profile Functions
--------------------------------------------------

function TAS_GetCurrentProfileName()

    if not ThronsAutoSellDB then
        return "Default"
    end

    return ThronsAutoSellDB.CurrentProfile
        or "Default"
end

function TAS_SetCurrentProfile(profileName)

    if not profileName or profileName == "" then
        profileName = "Default"
    end

    if not ThronsAutoSellDB.Profiles[profileName] then

        ThronsAutoSellDB.Profiles[profileName] =
            CopyDefaults(
                ThronsAutoSellDB.Profiles["Default"],
                {}
            )
    end

    ThronsAutoSellDB.CurrentProfile =
        profileName
end

function TAS_GetSettings()

    local profileName =
        TAS_GetCurrentProfileName()

    if not ThronsAutoSellDB.Profiles[profileName] then

        ThronsAutoSellDB.Profiles[profileName] =
            CopyDefaults(
                ThronsAutoSellDB.Profiles["Default"],
                {}
            )
    end

    return ThronsAutoSellDB.Profiles[profileName]
end

function TAS_GetProfileList()

    local profiles = {}

    for profileName in pairs(
        ThronsAutoSellDB.Profiles
    ) do

        table.insert(
            profiles,
            profileName
        )
    end

    table.sort(profiles)

    return profiles
end

function TAS_CreateProfile(profileName)

    if not profileName then
        return
    end

    profileName = strtrim(profileName)

    if profileName == "" then
        return
    end

    if not ThronsAutoSellDB.Profiles[profileName] then

        ThronsAutoSellDB.Profiles[profileName] =
            CopyDefaults(
                ThronsAutoSellDB.Profiles["Default"],
                {}
            )
    end
end

function TAS_DeleteProfile(profileName)

    if profileName == "Default" then
        return
    end

    local characterName =
        UnitName("player")

    if profileName == characterName then
        return
    end

    ThronsAutoSellDB.Profiles[profileName] =
        nil

    if TAS_GetCurrentProfileName() ==
        profileName then

        ThronsAutoSellDB.CurrentProfile =
            "Default"
    end
end

--------------------------------------------------
-- Database Initialize
--------------------------------------------------

local function InitializeDatabase()

    local characterName =
        UnitName("player")

    if not ThronsAutoSellDB then

        ThronsAutoSellDB = {}

    end

    --------------------------------------------------
    -- Profiles Table
    --------------------------------------------------

    ThronsAutoSellDB.Profiles =
        ThronsAutoSellDB.Profiles or {}

    --------------------------------------------------
    -- Default Profile
    --------------------------------------------------

    if not ThronsAutoSellDB.Profiles["Default"] then

        ThronsAutoSellDB.Profiles["Default"] =
            CopyDefaults(
                Defaults,
                {}
            )

    end

    --------------------------------------------------
    -- Character Profile
    --------------------------------------------------

    if not ThronsAutoSellDB.Profiles[characterName] then

        ThronsAutoSellDB.Profiles[characterName] =
            CopyDefaults(
                ThronsAutoSellDB.Profiles["Default"],
                {}
            )

    end

    --------------------------------------------------
    -- Apply New Defaults To Existing Profiles
    --------------------------------------------------

    for _, profile in pairs(
        ThronsAutoSellDB.Profiles
    ) do

        CopyDefaults(
            Defaults,
            profile
        )

    end

    --------------------------------------------------
    -- Current Profile
    --------------------------------------------------

    if not ThronsAutoSellDB.CurrentProfile then

        ThronsAutoSellDB.CurrentProfile =
            "Default"

    end

    --------------------------------------------------
    -- Safety Check
    --------------------------------------------------

    if not ThronsAutoSellDB.Profiles[
        ThronsAutoSellDB.CurrentProfile
    ] then

        ThronsAutoSellDB.CurrentProfile =
            "Default"

    end
end

--------------------------------------------------
-- Money Formatting
--------------------------------------------------

local function MoneyToString(copper)

    local gold =
        math.floor(copper / 10000)

    local silver =
        math.floor(
            (copper % 10000) / 100
        )

    local copperOnly =
        copper % 100

    return string.format(
        "|cffffd700%dg|r |cffc7c7cf%ds|r |cffeda55f%dc|r",
        gold,
        silver,
        copperOnly
    )
end

--------------------------------------------------
-- Item Quality Check
--------------------------------------------------

--------------------------------------------------
-- Item Quality Check
--------------------------------------------------

--------------------------------------------------
-- Equipment Check
--------------------------------------------------

local function IsEquipment(itemEquipLoc)

    if not itemEquipLoc or itemEquipLoc == "" then
        return false
    end

    local validSlots = {

        -- Armor
        INVTYPE_HEAD = true,
        INVTYPE_NECK = true,
        INVTYPE_SHOULDER = true,
        INVTYPE_CHEST = true,
        INVTYPE_ROBE = true,
        INVTYPE_WAIST = true,
        INVTYPE_LEGS = true,
        INVTYPE_FEET = true,
        INVTYPE_WRIST = true,
        INVTYPE_HAND = true,
        INVTYPE_CLOAK = true,

        -- Jewelry
        INVTYPE_FINGER = true,
        INVTYPE_TRINKET = true,

        -- Weapons
        INVTYPE_WEAPON = true,
        INVTYPE_2HWEAPON = true,
        INVTYPE_WEAPONMAINHAND = true,
        INVTYPE_WEAPONOFFHAND = true,
        INVTYPE_RANGED = true,
        INVTYPE_RANGEDRIGHT = true,
        INVTYPE_THROWN = true,

        -- Shields / Offhands
        INVTYPE_SHIELD = true,
        INVTYPE_HOLDABLE = true,
    }

    return validSlots[itemEquipLoc] == true
end

--------------------------------------------------
-- Item Quality Check
--------------------------------------------------

local function ShouldSellItem(
    quality,
    itemEquipLoc,
    db
)

    if not quality then
        return false
    end

    --------------------------------------------------
    -- Grey Items
    --------------------------------------------------

    if quality == Enum.ItemQuality.Poor then
        return true
    end

    --------------------------------------------------
    -- Green Equipment Only
    --------------------------------------------------

    if quality == Enum.ItemQuality.Uncommon
        and db.sellGreenItems
        and IsEquipment(itemEquipLoc) then

        return true
    end

    --------------------------------------------------
    -- Blue Equipment Only
    --------------------------------------------------

    if quality == Enum.ItemQuality.Rare
        and db.sellBlueItems
        and IsEquipment(itemEquipLoc) then

        return true
    end

    return false
end
--------------------------------------------------
-- Auto Repair
--------------------------------------------------

local function RepairItems()

    local db =
        TAS_GetSettings()

    if not db.repairItems then
        return
    end

    if not CanMerchantRepair() then
        return
    end

    local repairCost =
        GetRepairAllCost()

    --------------------------------------------------
    -- Nothing To Repair
    --------------------------------------------------

    if not repairCost or repairCost <= 0 then

        print(
            "|cff00FF00Thron's Auto Sell: No items to repair.|r"
        )

        return
    end

    --------------------------------------------------
    -- Not Enough Money
    --------------------------------------------------

    if GetMoney() < repairCost then

        print(
            "|cffff0000Thron's Auto Sell: Unable to repair. Not enough money.|r"
        )

        return
    end

    --------------------------------------------------
    -- Perform Repair
    --------------------------------------------------

    if CanGuildBankRepair() then

        RepairAllItems(true)

    else

        RepairAllItems(false)

    end

    local gold =
        math.floor(repairCost / 10000)

    local silver =
        math.floor(
            (repairCost % 10000) / 100
        )

    local copper =
        repairCost % 100

    print(
        string.format(
            "|cff00FF00Thron's Auto Sell: Equipment repaired for |cffffd700%dg|r |cffc7c7cf%ds|r |cffeda55f%dc|r.",
            gold,
            silver,
            copper
        )
    )
end
--------------------------------------------------
-- Auto Sell
--------------------------------------------------

local function SellItems()

    local db =
        TAS_GetSettings()

    if not db.autoSell then
        return
    end

    local sellLimit =
        tonumber(
            db.sellLimit
        )

    local soldItems = 0
    local totalMoney = 0

    for bag = 0, NUM_BAG_SLOTS do

        local slotCount =
            C_Container.GetContainerNumSlots(
                bag
            )

        for slot = 1, slotCount do

            local itemInfo =
                C_Container.GetContainerItemInfo(
                    bag,
                    slot
                )

            if itemInfo then

                local itemLink =
                    C_Container.GetContainerItemLink(
                        bag,
                        slot
                    )

                if itemLink then

                    local _,
                        _,
                        quality,
                        _,
                        _,
                        _,
                        _,
                        _,
                        itemEquipLoc,
                        _,
                        sellPrice =
                            GetItemInfo(
                                itemLink
                            )

                    if ShouldSellItem(
                        quality,
                        itemEquipLoc,
                        db
                    ) then

                        local quantity =
                            itemInfo.stackCount
                            or 1

                        local itemLocation =
                            ItemLocation:CreateFromBagAndSlot(
                                bag,
                                slot
                            )

                        local isBound =
                            false

                        if itemLocation and
                            itemLocation:IsValid() then

                            isBound =
                                C_Item.IsBound(
                                    itemLocation
                                )
                        end

                        if not (
                            db.skipSoulbound
                            and isBound
                        ) then

                            totalMoney =
                                totalMoney +
                                (
                                    (sellPrice or 0)
                                    * quantity
                                )

                            C_Container.UseContainerItem(
                                bag,
                                slot
                            )

                            soldItems =
                                soldItems + 1

                            if sellLimit and
                                soldItems >=
                                sellLimit then

                                print(
                                    "|cffFF6600Throns Auto Sell|r Sold "
                                    ..
                                    soldItems
                                    ..
                                    " item(s) for "
                                    ..
                                    MoneyToString(
                                        totalMoney
                                    )
                                )

                                return
                            end
                        end
                    end
                end
            end
        end
    end

    --------------------------------------------------
    -- Results
    --------------------------------------------------

    if soldItems > 0 then

        print(
            "|cffFF6600Throns Auto Sell|r Sold "
            ..
            soldItems
            ..
            " item(s) for "
            ..
            MoneyToString(
                totalMoney
            )
        )

    else

        print(
            "|cff00FF00Throns Auto Sell: There were no items matching your sell filters.|r"
        )

    end
end

--------------------------------------------------
-- Event Handler
--------------------------------------------------

local EventFrame =
    CreateFrame("Frame")

EventFrame:RegisterEvent(
    "ADDON_LOADED"
)

EventFrame:RegisterEvent(
    "MERCHANT_SHOW"
)

EventFrame:SetScript(
    "OnEvent",

    function(
        self,
        event,
        arg1
    )

        if event ==
            "ADDON_LOADED" and
            arg1 ==
            ADDON_NAME then

            InitializeDatabase()

            print(
                "|cffFF6600Throns Auto Sell Loaded.|r"
            )

            return
        end

        if event ==
            "MERCHANT_SHOW" then

            RepairItems()

            SellItems()

        end
    end
)

--------------------------------------------------
-- Slash Commands
--------------------------------------------------

SLASH_THRONSAUTOSELL1 =
    "/tas"

SLASH_THRONSAUTOSELL2 =
    "/thronsautosell"

SlashCmdList[
    "THRONSAUTOSELL"
] = function()

    if not ThronsAutoSellFrame then
        return
    end

    if ThronsAutoSellFrame:IsShown() then

        ThronsAutoSellFrame:Hide()

    else

        ThronsAutoSellFrame:Show()

    end
end