-- Rewrite individual choice weapons to specific types
--[====[

gui/choose-weapons
==================
Activate in the :guilabel:`Equip->View/Customize` page of the military screen.

Depending on the cursor location, it rewrites all 'individual choice weapon' entries
in the selected squad or position to use a specific weapon type matching the assigned
unit's top skill. If the cursor is in the rightmost list over a weapon entry, it rewrites
only that entry, and does it even if it is not 'individual choice'.

Rationale: individual choice seems to be unreliable when there is a weapon shortage,
and may lead to inappropriate weapons being selected.

]====]
local utils = require 'utils'
local dlg = require 'gui.dialogs'

local defs = df.global.world.raws.itemdefs
local entity = df.global.plotinfo.main.fortress_entity
local tasks = df.global.plotinfo.tasks
local equipment = df.global.plotinfo.equipment

function find_best_weapon(unit,mode)
    local best = nil
    local skill = nil
    local skill_level = nil
    local count = 0
    local function try(id,iskill)
        local slevel = dfhack.units.getNominalSkill(unit,iskill)
        -- Choose most skill
        if (skill ~= nil and slevel > skill_level)
        or (skill == nil and slevel > 0) then
            best,skill,skill_level,count = id,iskill,slevel,0
        end
        -- Then most produced within same skill
        if skill == iskill then
            local cnt = tasks.created_weapons[id]
            if cnt > count then
                best,count = id,cnt
            end
        end
    end
    for _,id in ipairs(entity.resources.weapon_type) do
        local def = defs.weapons[id]
        if def.skill_ranged >= 0 then
            if mode == nil or mode == 'ranged' then
                try(id, def.skill_ranged)
            end
        else
            if mode == nil or mode == 'melee' then
                try(id, def.skill_melee)
            end
        end
    end
    return best
end

function unassign_wrong_items(unit,position,spec,subtype)
    for i=#spec.assigned-1,0,-1 do
        local id = spec.assigned[i]
        local item = df.item.find(id)

        if item.subtype.subtype ~= subtype then
            spec.assigned:erase(i)

            -- TODO: somewhat unexplored area; maybe missing some steps
            utils.erase_sorted(position.assigned_items,id)
            if utils.erase_sorted(equipment.items_assigned.WEAPON,item,'id') then
                utils.insert_sorted(equipment.items_unassigned.WEAPON,item,'id')
            end
            equipment.update.weapon = true
            unit.uniform.pickup_flags.update = true
        end
    end
end

local count = 0

function adjust_uniform_spec(unit,position,spec,force)
    if not unit then return end
    local best
    if spec.indiv_choice.melee then
        best = find_best_weapon(unit, 'melee')
    elseif spec.indiv_choice.ranged then
        best = find_best_weapon(unit, 'ranged')
    elseif spec.indiv_choice.any or force then
        best = find_best_weapon(unit, nil)
    end
    if best then
        count = count + 1
        spec.item_filter.item_subtype = best
        spec.indiv_choice.any = false
        spec.indiv_choice.melee = false
        spec.indiv_choice.ranged = false
        unassign_wrong_items(unit, position, spec, best)
    end
end

function adjust_position(unit,position,force)
    if not unit then
        local fig = df.historical_figure.find(position.occupant)
        if not fig then return end
        unit = df.unit.find(fig.unit_id)
    end

    for _,v in ipairs(position.uniform.weapon) do
        adjust_uniform_spec(unit, position, v, force)
    end
end

function adjust_squad(squad, force)
    for _,pos in ipairs(squad.positions) do
        adjust_position(nil, pos, force)
    end
end

local args = {...}
local vs = dfhack.gui.getCurViewscreen()
local vstype = df.viewscreen_layer_militaryst
if not vstype:is_instance(vs) then
    qerror('Call this from the military screen')
end

if vs.page == vstype.T_page.Equip
and vs.equip.mode == vstype.T_equip.T_mode.Customize then
    local slist = vs.layer_objects[0]
    local squad = vs.equip.squads[slist:getListCursor()]

    if slist.active then
        print('Adjusting squad.')
        adjust_squad(squad)
    else
        local plist = vs.layer_objects[1]
        local pidx = plist:getListCursor()
        local pos = squad.positions[pidx]
        local unit = vs.equip.units[pidx]

        if plist.active then
            print('Adjusting position.')
            adjust_position(unit, pos)
        elseif unit and vs.equip.edit_mode < 0 then
            local wlist = vs.layer_objects[2]
            local idx = wlist:getListCursor()
            local cat = vs.equip.assigned.category[idx]

            if wlist.active and cat == df.uniform_category.weapon then
                print('Adjusting spec.')
                adjust_uniform_spec(unit, pos, vs.equip.assigned.spec[idx], true)
            end
        end
    end
else
    qerror('Call this from the Equip page of military screen')
end

if count > 1 then
    dlg.showMessage(
        'Choose Weapons',
        'Updated '..count..' uniform entries.', COLOR_GREEN
    )
elseif count == 0 then
    dlg.showMessage(
        'Choose Weapons',
        'Did not find any entries to update.', COLOR_YELLOW
    )
end
