The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../lib";
use List::Util            (qw(first));
use Games::Lacuna::Client ();
use Getopt::Long          (qw(GetOptions));
use POSIX                 (qw(floor));
my $cfg_file;

if ( @ARGV && $ARGV[0] !~ /^--/) {
	$cfg_file = shift @ARGV;
}
else {
	$cfg_file = 'lacuna.yml';
}

unless ( $cfg_file and -e $cfg_file ) {
  $cfg_file = eval{
    require File::HomeDir;
    require File::Spec;
    my $dist = File::HomeDir->my_dist_config('Games-Lacuna-Client');
    File::Spec->catfile(
      $dist,
      'login.yml'
    ) if $dist;
  };
  unless ( $cfg_file and -e $cfg_file ) {
    die "Did not provide a config file";
  }
}

my $from;
my $to;
my $push_type;
my $trade_type;
my $min_push = 1;
my $fastest;

GetOptions(
    'from=s' => \$from,
    'to=s'   => \$to,
    'push_type=s'  => \$push_type,
    'trade_type=s' => \$trade_type,
    'min_push=i'   => \$min_push,
    'fastest'      => \$fastest,
);

usage() if !$from || !$to || !$push_type;


my $client = Games::Lacuna::Client->new(
	cfg_file => $cfg_file,
	 #debug    => 1,
);

my $empire  = $client->empire->get_status->{empire};
my $planets = $empire->{planets};

# reverse hash, to key by name instead of id
my %planets_by_name = reverse %$planets;

my $to_id = $planets_by_name{$to}
    or die "--to planet not found";

# Load planet data
my $body      = $client->body( id => $planets_by_name{$from} );
my $buildings = $body->get_buildings->{buildings};

# Check dock space on target planet
my $to_dock_count;
{
    my $to_body      = $client->body( id => $planets_by_name{$to} );
    my $to_buildings = $to_body->get_buildings->{buildings};

    my $space_port_id = first {
        $to_buildings->{$_}->{url} eq '/spaceport'
    }
      grep { $to_buildings->{$_}->{level} > 0 and $to_buildings->{$_}->{efficiency} == 100 }
      keys %$to_buildings;

    die "No spaceport found on target planet\n"
        if !$space_port_id;

    my $space_port = $client->building( id => $space_port_id, type => 'SpacePort' );

    $to_dock_count = $space_port->view->{docks_available};

    die "No docks available in target SpacePort\n"
        if !$to_dock_count;
}

die "Fewer free docks in target SpacePort than --min_push value\n"
    if $min_push > $to_dock_count;

# Find the TradeMin
my $trade_min_id = first {
        $buildings->{$_}->{name} eq 'Trade Ministry'
} keys %$buildings;

my $trade_min = $client->building( id => $trade_min_id, type => 'Trade' );

my @trade_ships = @{ $trade_min->get_trade_ships($to_id)->{ships} };

if ($trade_type) {

    @trade_ships = grep {
        $_->{type} =~ m/$trade_type/
    } @trade_ships;

}

my $get_ships_result = $trade_min->get_ships;

my $hold_required = $get_ships_result->{cargo_space_used_each};

@trade_ships = grep {
    $_->{hold_size} >= $min_push*$hold_required;
} @trade_ships;

die "No available ships to push with"
    if !@trade_ships;

@trade_ships = sort {
    $fastest ? $b->{speed} <=> $a->{speed} : $b->{hold_size} <=> $a->{hold_size}
} @trade_ships;

my @push_ships = @{ $get_ships_result->{ships} };

@push_ships = grep {
    $_->{type} =~ m/$push_type/;
} @push_ships;

die "No ships available to be pushed\n"
    if !@push_ships;

die "Less than --min_push ships available to be pushed\n"
    if $min_push > scalar @push_ships;

for my $trade_ship (@trade_ships) {
    last if $min_push > scalar @push_ships;
    last if $min_push > $to_dock_count;
    last if $to_dock_count < 1;

    my $max_ships = floor( $trade_ship->{hold_size} / $hold_required );

    my $ship_count = $max_ships > scalar @push_ships ? scalar @push_ships
                   :                                   $max_ships;

    $ship_count = $to_dock_count
        if $ship_count > $to_dock_count;

    my @push_ships = splice @push_ships, 0, $ship_count;

    my @items;

    for my $push_ship (@push_ships) {
        push @items, {
            type    => 'ship',
            ship_id => $push_ship->{id},
        };
    }

    my $return = $trade_min->push_items(
        $to_id,
        \@items,
        {
            ship_id => $trade_ship->{id},
        },
    );

    printf "Pushed %s\n", join ',', map {"'$_'"} map { $_->{name} } @push_ships;
    printf "Using '%s', arriving %s\n", $return->{ship}{name}, $return->{ship}{date_arrives};

    $to_dock_count -= $ship_count;
}


sub usage {
  die <<"END_USAGE";
Usage: $0 CONFIG_FILE
       --from      PLANET_NAME
       --to        PLANET_NAME
       --push_type SHIP_TYPE
       --trade_type  SHIP_TYPE
       --min_push  MIN_PUSH
       --fastest
       --largest

CONFIG_FILE  defaults to 'lacuna.yml'

--push_type SHIP_TYPE is a regex used to decide which ships to push. Required.
If this is set to a trade-type ship, then you must also set --trade_type to a
different type of ship, so that a ship doesn't try pushing itself.

--trade_type SHIP_TYPE is a regex used to decide which ships to use to push the
selected ships.
By default, is not set, so all trade ships are candidates.

MIN_PUSH is the minimum number of matching ships that are required per-push.
Each pushing-ship requires a 50K cargo-hold per ship being pushed.
Defaults to 1, so any trade ship above 50K is a candidate.

--fastest or --largest may be set, to decide which ships to use to push.
If neither is set, --largest will be the default behaviour.

END_USAGE

}