# mark stuff inside of cages for dumping.
=begin

stripcaged
==========
For dumping items inside cages. Will mark selected items for dumping, then
a dwarf may come and actually dump them (or you can use `autodump`).

Arguments:

:list:      display the list of all cages and their item content on the console
:items:     dump items in the cage, excluding stuff worn by caged creatures
:weapons:   dump equipped weapons
:armor:     dump everything worn by caged creatures (including armor and clothing)
:all:       dump everything in the cage, on a creature or not

Without further arguments, all commands work on all cages and animal traps on
the map. With the ``here`` argument, considers only the in-game selected cage
(or the cage under the game cursor). To target only specific cages, you can
alternatively pass cage IDs as arguments::

  stripcaged weapons 25321 34228

=end

def plural(nr, name)
    # '1 cage' / '4 cages'
    "#{nr} #{name}#{'s' if nr > 1}"
end

def cage_dump_items(list)
    count = 0
    count_cage = 0
    list.each { |cage|
        pre_count = count
        cage.general_refs.each { |ref|
            next unless ref.kind_of?(DFHack::GeneralRefContainsItemst)
            next if ref.item_tg.flags.dump
            count += 1
            ref.item_tg.flags.dump = true
        }
        count_cage += 1 if pre_count != count
    }

    puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}"
end

def cage_dump_armor(list)
    count = 0
    count_cage = 0
    list.each { |cage|
        pre_count = count
        cage.general_refs.each { |ref|
            next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst)
            ref.unit_tg.inventory.each { |it|
                next if it.mode != :Worn
                next if it.item.flags.dump
                count += 1
                it.item.flags.dump = true
            }
        }
        count_cage += 1 if pre_count != count
    }

    puts "Dumped #{plural(count, 'armor piece')} in #{plural(count_cage, 'cage')}"
end

def cage_dump_weapons(list)
    count = 0
    count_cage = 0
    list.each { |cage|
        pre_count = count
        cage.general_refs.each { |ref|
            next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst)
            ref.unit_tg.inventory.each { |it|
                next if it.mode != :Weapon
                next if it.item.flags.dump
                count += 1
                it.item.flags.dump = true
            }
        }
        count_cage += 1 if pre_count != count
    }

    puts "Dumped #{plural(count, 'weapon')} in #{plural(count_cage, 'cage')}"
end

def cage_dump_all(list)
    count = 0
    count_cage = 0
    list.each { |cage|
        pre_count = count
        cage.general_refs.each { |ref|
            case ref
            when DFHack::GeneralRefContainsItemst
                next if ref.item_tg.flags.dump
                count += 1
                ref.item_tg.flags.dump = true
            when DFHack::GeneralRefContainsUnitst
                ref.unit_tg.inventory.each { |it|
                    next if it.item.flags.dump
                    count += 1
                    it.item.flags.dump = true
                }
            end
        }
        count_cage += 1 if pre_count != count
    }

    puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}"
end


def cage_dump_list(list)
    count_total = Hash.new(0)
    empty_cages = 0
    list.each { |cage|
        count = Hash.new(0)

        cage.general_refs.each { |ref|
            case ref
            when DFHack::GeneralRefContainsItemst
                count[ref.item_tg._rtti_classname] += 1
            when DFHack::GeneralRefContainsUnitst
                ref.unit_tg.inventory.each { |it|
                    count[it.item._rtti_classname] += 1
                }
            # TODO vermin ?
            else
                puts "unhandled ref #{ref.inspect}" if $DEBUG
            end
        }

        type = case cage
               when DFHack::ItemCagest; 'Cage'
               when DFHack::ItemAnimaltrapst; 'Animal trap'
               else cage._rtti_classname
               end

        if count.empty?
            empty_cages += 1
        else
            puts "#{type} ##{cage.id}: ", count.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" }
        end

        count.each { |k, v| count_total[k] += v }
    }

    if list.length > 2
        puts '', "Total: ", count_total.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" }
        puts "with #{plural(empty_cages, 'empty cage')}"
    end
end


# handle magic script arguments
here_only = $script_args.delete 'here'
if here_only
    it = df.item_find
    list = [it]
    if not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst)
        list = df.world.items.other[:ANY_CAGE_OR_TRAP].find_all { |i| df.at_cursor?(i) }
    end
    if list.empty?
        puts 'Please select a cage'
        throw :script_finished
    end

elsif ids = $script_args.find_all { |arg| arg =~ /^\d+$/ } and ids.first
    list = []
    ids.each { |id|
        $script_args.delete id
        if not it = df.item_find(id.to_i)
            puts "Invalid item id #{id}"
        elsif not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst)
            puts "Item ##{id} is not a cage"
            list << it
        else
            list << it
        end
    }
    if list.empty?
        puts 'Please use a valid cage id'
        throw :script_finished
    end

else
    list = df.world.items.other[:ANY_CAGE_OR_TRAP]
end


# act
case $script_args[0]
when /^it/i
    cage_dump_items(list)
when /^arm/i
    cage_dump_armor(list)
when /^wea/i
    cage_dump_weapons(list)
when 'all'
    cage_dump_all(list)
when 'list'
    cage_dump_list(list)
else
    puts <<EOS
Marks items inside all cages for dumping.
Add 'here' to dump stuff only for selected cage.
Add a cage id to dump stuff for this cage only.

See 'autodump' to actually dump stuff.

Usage:
  stripcaged items
    dump items directly in cages (eg seeds after training)

  stripcaged [armor|weapons] here
    dump armor or weapons of caged creatures in selected cage

  stripcaged all 28 29
    dump every item in cage id 28 and 29, along with every item worn by creatures in there too

  stripcaged list
    show content of the cages

EOS

end
