local mod = get_mod("extended_weapon_customization")

-- ##### ┬─┐┌─┐┌─┐ ┬ ┬┬┬─┐┌─┐ #########################################################################################
-- ##### ├┬┘├┤ │─┼┐│ ││├┬┘├┤  #########################################################################################
-- ##### ┴└─└─┘└─┘└└─┘┴┴└─└─┘ #########################################################################################

local master_items = mod:original_require("scripts/backend/master_items")

-- ##### ┌─┐┌─┐┬─┐┌─┐┌─┐┬─┐┌┬┐┌─┐┌┐┌┌─┐┌─┐ ############################################################################
-- ##### ├─┘├┤ ├┬┘├┤ │ │├┬┘│││├─┤││││  ├┤  ############################################################################
-- ##### ┴  └─┘┴└─└  └─┘┴└─┴ ┴┴ ┴┘└┘└─┘└─┘ ############################################################################
-- #region Performance
    local unit = Unit
    local pairs = pairs
    local table = table
    local string = string
    local tostring = tostring
    local unit_node = unit.node
    local table_size = table.size
    local unit_alive = unit.alive
    local table_clear = table.clear
    local string_find = string.find
    local table_append = table.append
    local string_split = string.split
    local table_reverse = table.reverse
    local unit_set_data = unit.set_data
    local table_combine = table.combine
    local unit_has_node = unit.has_node
    local table_icombine = table.icombine
    local table_contains = table.contains
    local table_set_readonly = table.set_readonly
    local table_merge_recursive = table.merge_recursive
--#endregion

-- ##### ┌┬┐┌─┐┌┬┐┌─┐ #################################################################################################
-- #####  ││├─┤ │ ├─┤ #################################################################################################
-- ##### ─┴┘┴ ┴ ┴ ┴ ┴ #################################################################################################

local pt = mod:pt()
local empty_overrides_table = table_set_readonly({})
local temp_children = {}
local PROCESS_SLOTS = {"WEAPON_SKIN", "WEAPON_MELEE", "WEAPON_RANGED"}
local PROCESS_ITEM_TYPES = {"WEAPON_MELEE", "WEAPON_RANGED"}
local dump_nodes = true

-- ##### ┌─┐┬ ┬┌┐┌┌─┐┌┬┐┬┌─┐┌┐┌┌─┐ ####################################################################################
-- ##### ├┤ │ │││││   │ ││ ││││└─┐ ####################################################################################
-- ##### └  └─┘┘└┘└─┘ ┴ ┴└─┘┘└┘└─┘ ####################################################################################

mod.recursive_children = function(self, unit, attachment_units_by_unit, children)
    if not children then
        table_clear(temp_children)
        children = temp_children
    end
    local unit_children = attachment_units_by_unit[unit]
    for _, unit in pairs(unit_children) do
        children[#children+1] = unit
        self:recursive_children(unit, attachment_units_by_unit, children)
    end
    return children
end

-- ##### ┌─┐┬  ┌─┐┌─┐┌─┐  ┌─┐─┐ ┬┌┬┐┌─┐┌┐┌┌─┐┬┌─┐┌┐┌ ##################################################################
-- ##### │  │  ├─┤└─┐└─┐  ├┤ ┌┴┬┘ │ ├┤ │││└─┐││ ││││ ##################################################################
-- ##### └─┘┴─┘┴ ┴└─┘└─┘  └─┘┴ └─ ┴ └─┘┘└┘└─┘┴└─┘┘└┘ ##################################################################

mod:hook_require("scripts/extension_systems/visual_loadout/utilities/visual_loadout_customization", function(instance)

    mod:hook(instance, "spawn_item_attachments", function(func, item_data, override_lookup, attach_settings, item_unit, optional_map_attachment_name_to_unit, optional_extract_attachment_units_bind_poses, optional_extract_item_names, optional_mission_template, optional_equipment, ...)
        -- Modify item
        mod:modify_item(item_data)
        -- Fixes
        local fixes = mod:collect_fixes(item_data)
        mod:apply_attachment_fixes(item_data, fixes)
        -- Original function
        local attachment_units_by_unit, attachment_id_lookup, attachment_name_lookup, bind_poses_by_unit, item_name_by_unit = func(item_data, override_lookup, attach_settings, item_unit, optional_map_attachment_name_to_unit, optional_extract_attachment_units_bind_poses, optional_extract_item_names, optional_mission_template, optional_equipment, ...)

        -- local is_ui_item_preview = false
        local is_ui_item_preview = (item_data and (item_data.__is_ui_item_preview or item_data.__is_preview_item or item_data.__attachment_customization))
        if attachment_id_lookup and item_data.attachments then

            -- Collect attachment fixes
            local kitbash_fixes = mod:fetch_attachment_fixes(item_data.structure or item_data.attachments)
            if kitbash_fixes then
                fixes = table_merge_recursive(fixes, kitbash_fixes)
                -- fixes = table_append(fixes, kitbash_fixes)
            end

            for _, attachment_unit in pairs(attachment_units_by_unit[item_unit]) do

                -- Set attachment slot
                local attachment_slot = attachment_id_lookup[attachment_unit]
                unit_set_data(attachment_unit, "attachment_slot", attachment_slot)

                -- Get item path
                local item_path = mod:fetch_attachment(item_data.attachments, attachment_slot)
                local item = master_items.get_item(item_path)
                local parent_attachment_slot_data = mod:fetch_attachment_data(item_data.attachments, attachment_slot)
                local material_overrides_data = mod:gear_material_overrides(item_data, nil, attachment_slot) or (parent_attachment_slot_data and parent_attachment_slot_data.material_overrides and parent_attachment_slot_data) --or item
                if material_overrides_data then
                    instance.apply_material_overrides(material_overrides_data, attachment_unit, item_unit, attach_settings)
                end

                -- if item and item.materials_overrides then instance.apply_material_overrides(item, attachment_unit, item_unit, attach_settings) end

                -- Set attachment name
                local attachment_name = mod.settings.attachment_name_by_item_string[item_path]
                unit_set_data(attachment_unit, "attachment_name", attachment_name)

                if item_data.attachments.slot_trinket_1 and item_data.attachments.slot_trinket_1.item then
                    -- This is a preview item in attachment customization menu

                    is_ui_item_preview = true

                    -- Get item path
                    local item = item_data.attachments.slot_trinket_1.item
                    local item_path = item.name

                    -- Execute ui item init function for attachment
                    local attachment_data = mod.settings.attachment_data_by_item_string[item_path]
                    if attachment_data and attachment_data.ui_item_init then
                        local world = attach_settings.world
                        attachment_data.ui_item_init(world, attachment_unit, attachment_data, true)
                    end

                    -- Collect attachment fixes
                    if item and item.attachments then
                        local kitbash_fixes = mod:fetch_attachment_fixes(item.structure or item.attachments)
                        if kitbash_fixes then
                            fixes = table_merge_recursive(fixes, kitbash_fixes)
                            -- fixes = table_append(fixes, kitbash_fixes)
                        end

                        if attachment_units_by_unit[attachment_unit] then

                            for _, sub_attachment_unit in pairs(attachment_units_by_unit[attachment_unit]) do

                                -- Set attachment slot
                                local attachment_slot_parts = string_split(attachment_id_lookup[sub_attachment_unit], ".")
                                local attachment_slot = attachment_slot_parts and attachment_slot_parts[#attachment_slot_parts]
                                -- local attachment_slot = attachment_id_lookup[sub_attachment_unit]
                                unit_set_data(sub_attachment_unit, "attachment_slot", attachment_slot)

                                -- Get master item
                                local item_path = mod:fetch_attachment(item.attachments, attachment_slot)
                                local attachment_slot_data = mod:fetch_attachment_data(item.attachments, attachment_slot)
                                local sub_item = master_items.get_item(item_path)
                                local material_overrides_data = mod:gear_material_overrides(item_data, nil, attachment_slot) or (attachment_slot_data and attachment_slot_data.material_overrides and attachment_slot_data) or (parent_attachment_slot_data and parent_attachment_slot_data.material_overrides and parent_attachment_slot_data) or sub_item
                                if material_overrides_data then
                                    instance.apply_material_overrides(material_overrides_data, sub_attachment_unit, attachment_unit, attach_settings)
                                end

                                -- Set attachment name
                                local attachment_name = mod.settings.attachment_name_by_item_string[item_path] or attachment_name
                                unit_set_data(sub_attachment_unit, "attachment_name", attachment_name)

                                mod:print("sub attachment: "..tostring(sub_attachment_unit).." name: "..tostring(attachment_name).." slot: "..tostring(attachment_slot))

                            end
                        end

                    end

                else -- This is a normal item

                    -- Get master item
                    local item = master_items.get_item(item_path)

                    -- if item and item.materials_overrides then instance.apply_material_overrides(item, attachment_unit, item_unit, attach_settings) end

                    -- Execute ui item init function for attachment
                    -- Only execute if not from ui profile spawner or ui item preview
                    if is_ui_item_preview and not attach_settings.from_ui_profile_spawner then
                        -- Get attachment data
                        local attachment_data = mod.settings.attachment_data_by_item_string[item_path]
                        if attachment_data and attachment_data.ui_item_init then
                            local world = attach_settings.world
                            attachment_data.ui_item_init(world, attachment_unit, attachment_data)
                        end
                    end

                    if item and item.attachments then

                        -- Collect current attachment names
                        local kitbash_fixes = mod:fetch_attachment_fixes(item.structure or item.attachments)
                        if kitbash_fixes then
                            fixes = table_merge_recursive(fixes, kitbash_fixes)
                            -- fixes = table_append(fixes, kitbash_fixes)
                        end

                        -- Exlcude from vfx spawner
                        if item.is_kitbash and not item.disable_vfx_spawner_exclusion then

                            for unit, _ in pairs(pt.exclude_from_vfx_spawner) do
                                if not unit or not unit_alive(unit) then
                                    pt.exclude_from_vfx_spawner[unit] = nil
                                end
                            end

                            pt.exclude_from_vfx_spawner[attachment_unit] = true

                            local all_children = mod:recursive_children(attachment_unit, attachment_units_by_unit)
                            for _, child in pairs(all_children) do
                                pt.exclude_from_vfx_spawner[child] = true
                            end

                        elseif item.disable_vfx_spawner_exclusion then

                            mod:print("disable_vfx_spawner_exclusion: "..tostring(item.name))

                        end

                        if attachment_units_by_unit[attachment_unit] then

                            for _, sub_attachment_unit in pairs(attachment_units_by_unit[attachment_unit]) do

                                -- Set attachment slot
                                local attachment_slot_parts = string_split(attachment_id_lookup[sub_attachment_unit], ".")
                                local attachment_slot = attachment_slot_parts and attachment_slot_parts[#attachment_slot_parts]
                                -- local attachment_slot = attachment_id_lookup[sub_attachment_unit]
                                unit_set_data(sub_attachment_unit, "attachment_slot", attachment_slot)

                                -- Get master item
                                local item_path = mod:fetch_attachment(item.attachments, attachment_slot)
                                local attachment_slot_data = mod:fetch_attachment_data(item.attachments, attachment_slot)
                                local sub_item = master_items.get_item(item_path)
                                local material_overrides_data = mod:gear_material_overrides(item_data, nil, attachment_slot) or (attachment_slot_data and attachment_slot_data.material_overrides and attachment_slot_data) or (parent_attachment_slot_data and parent_attachment_slot_data.material_overrides and parent_attachment_slot_data) or sub_item
                                if material_overrides_data then
                                    instance.apply_material_overrides(material_overrides_data, sub_attachment_unit, attachment_unit, attach_settings)
                                end

                                -- Set attachment name
                                local attachment_name = mod.settings.attachment_name_by_item_string[item_path] or attachment_name
                                unit_set_data(sub_attachment_unit, "attachment_name", attachment_name)

                                mod:print("sub attachment: "..tostring(sub_attachment_unit).." name: "..tostring(attachment_name).." slot: "..tostring(attachment_slot))

                            end
                        end

                    end

                end
                
            end
        end
        -- fixes = table_reverse(fixes)
        -- Apply fixes
        mod:apply_unit_fixes(item_data, item_unit, attachment_units_by_unit, attachment_name_lookup, fixes, is_ui_item_preview)
        -- Return
        return attachment_units_by_unit, attachment_id_lookup, attachment_name_lookup, bind_poses_by_unit, item_name_by_unit
    end)

    mod:hook(instance, "spawn_item", function(func, item_data, attach_settings, parent_unit, optional_map_attachment_name_to_unit, optional_extract_attachment_units_bind_poses, optional_extract_item_names, optional_mission_template, optional_equipment, ...)
        -- Modify item
        mod:modify_item(item_data)
        -- Fixes
        local fixes = mod:collect_fixes(item_data)
        mod:apply_attachment_fixes(item_data, fixes)
        -- Original function
        local item_unit, attachment_units_by_unit, bind_pose, attachment_id_lookup, attachment_name_lookup, attachment_units_bind_poses, item_name_by_unit = func(item_data, attach_settings, parent_unit, optional_map_attachment_name_to_unit, optional_extract_attachment_units_bind_poses, optional_extract_item_names, optional_mission_template, optional_equipment, ...)

        if attachment_id_lookup and item_data.attachments then

            -- Collect current attachment names
            local kitbash_fixes = mod:fetch_attachment_fixes(item_data.structure or item_data.attachments)
            if kitbash_fixes then
                fixes = table_merge_recursive(fixes, kitbash_fixes)
                -- fixes = table_append(fixes, kitbash_fixes)
            end

            for _, attachment_unit in pairs(attachment_units_by_unit[item_unit]) do

                -- Set attachment slot
                local attachment_slot = attachment_id_lookup[attachment_unit]
                unit_set_data(attachment_unit, "attachment_slot", attachment_slot)
                
                -- Get master item
                local item_path = mod:fetch_attachment(item_data.attachments, attachment_slot)
                local item = master_items.get_item(item_path)
                local parent_attachment_slot_data = mod:fetch_attachment_data(item_data.attachments, attachment_slot)
                local material_overrides_data = mod:gear_material_overrides(item_data, nil, attachment_slot) or (parent_attachment_slot_data and parent_attachment_slot_data.material_overrides and parent_attachment_slot_data) --or item
                if material_overrides_data then
                    instance.apply_material_overrides(material_overrides_data, attachment_unit, item_unit, attach_settings)
                end

                -- if item and item.materials_overrides then instance.apply_material_overrides(item, attachment_unit, item_unit, attach_settings) end

                -- Set attachment name
                local attachment_name = mod.settings.attachment_name_by_item_string[item_path]
                unit_set_data(attachment_unit, "attachment_name", attachment_name)

                if item and item.attachments then

                    -- Collect current attachment names
                    local kitbash_fixes = mod:fetch_attachment_fixes(item.structure or item.attachments)
                    if kitbash_fixes then
                        fixes = table_merge_recursive(fixes, kitbash_fixes)
                        -- fixes = table_append(fixes, kitbash_fixes)
                    end

                    if item.is_kitbash and not item.disable_vfx_spawner_exclusion then

                        local deleted = 0
                        for unit, _ in pairs(pt.exclude_from_vfx_spawner) do
                            if not unit or not unit_alive(unit) then
                                pt.exclude_from_vfx_spawner[unit] = nil
                                deleted = deleted + 1
                            end
                        end
                        if deleted > 0 then
                            mod:print("exclude_from_vfx_spawner deleted: "..tostring(deleted))
                        end

                        pt.exclude_from_vfx_spawner[attachment_unit] = true

                        local all_children = mod:recursive_children(attachment_unit, attachment_units_by_unit)
                        for _, child in pairs(all_children) do
                            pt.exclude_from_vfx_spawner[child] = true
                        end

                    elseif item.disable_vfx_spawner_exclusion then

                        mod:print("disable_vfx_spawner_exclusion: "..tostring(item.name))

                    end

                    if attachment_units_by_unit[attachment_unit] then

                        for _, sub_attachment_unit in pairs(attachment_units_by_unit[attachment_unit]) do

                            -- Set attachment slot
                            local attachment_slot_parts = string_split(attachment_id_lookup[sub_attachment_unit], ".")
                            local attachment_slot = attachment_slot_parts and attachment_slot_parts[#attachment_slot_parts]
                            -- local attachment_slot = attachment_id_lookup[sub_attachment_unit]
                            unit_set_data(sub_attachment_unit, "attachment_slot", attachment_slot)

                            -- Get master item
                            local item_path = mod:fetch_attachment(item.attachments, attachment_slot)
                            local attachment_slot_data = mod:fetch_attachment_data(item.attachments, attachment_slot)
                            local sub_item = master_items.get_item(item_path)
                            local material_overrides_data = mod:gear_material_overrides(item_data, nil, attachment_slot) or (attachment_slot_data and attachment_slot_data.material_overrides and attachment_slot_data) or (parent_attachment_slot_data and parent_attachment_slot_data.material_overrides and parent_attachment_slot_data) or sub_item
                            if material_overrides_data then
                                instance.apply_material_overrides(material_overrides_data, sub_attachment_unit, attachment_unit, attach_settings)
                            end

                            -- if item and item.materials_overrides then instance.apply_material_overrides(item, sub_attachment_unit, attachment_unit, attach_settings) end

                            -- Set attachment name
                            local attachment_name = mod.settings.attachment_name_by_item_string[item_path] or attachment_name
                            unit_set_data(sub_attachment_unit, "attachment_name", attachment_name)

                            mod:print("sub attachment: "..tostring(sub_attachment_unit).." name: "..tostring(attachment_name).." slot: "..tostring(attachment_slot))

                        end
                    end

                end

            end

        end
        -- fixes = table_reverse(fixes)
        -- Apply fixes
        mod:apply_unit_fixes(item_data, item_unit, attachment_units_by_unit, attachment_name_lookup, fixes, true)
        -- Return
        return item_unit, attachment_units_by_unit, bind_pose, attachment_id_lookup, attachment_name_lookup, attachment_units_bind_poses, item_name_by_unit
    end)

    mod:hook(instance, "generate_attachment_overrides_lookup", function(func, item_data, override_item_data, ...)

        -- Original function
        local override_lookup = func(item_data, override_item_data, ...)
        
        if table_contains(PROCESS_ITEM_TYPES, item_data.item_type) then
            if override_lookup and table_size(override_lookup) > 0 then
                for attachment_slot, replacement_path in pairs(override_lookup) do
                    if not string_find(replacement_path, "skins") then
                        override_lookup[attachment_slot] = nil
                    end
                end
            end
        end

        -- Return
        return override_lookup
    end)

end)
