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 Clone        qw(clone);
use List::Util   qw(first max);
use Getopt::Long qw(GetOptions);
use YAML::Any    qw( LoadFile );

use FindBin;
use lib "$FindBin::Bin/../lib";
use Games::Lacuna::Client ();

$| = 1;
my $build_gap = 3; # seconds

# if there is only 1 arg, assume it's the build_plan.yml for later, not this config
my $cfg_file = @ARGV == 2 ? shift @ARGV : '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 ) {
    usage( "Did not provide a config file" );
  }
}

my $plan_file = @ARGV ? shift @ARGV : 'build_plan.yml';
unless ( $plan_file and -e $plan_file ) {
	usage( "Did not provide a plan file" );
}

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

# Load the plan
my $plan_config = LoadFile( $plan_file );
my $planet_name = $plan_config->{planet}
    or usage( "planet name missing" );

my @queue = @{ $plan_config->{queue} };

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

# reverse hash, to key by name instead of id
my %planets = reverse %{ $empire->{planets} };

# Load planet data
my $planet = $client->body( id => $planets{$planet_name} );
my $buildings;
my $dev_min;

print "Planet: $planet_name\n";

# run through our queue
for (my $i = 0; $i <= $#queue; $i++) {
    my $item = $queue[$i];
    my $return;

    # wait if there's something already in / been added to the build queue
    while ( my $pending = build_remaining() ) {
        printf "Already something building: waiting for %d seconds\n", $pending;
        sleep $pending+$build_gap;
    }

    if ( $item->{build} ) {
        printf "Building %s at %d,%d\n",
            $item->{build}{type},
            $item->{build}{x},
            $item->{build}{y};

        my $building = $client->building( type => $item->{build}{type} );
        $return = $building->build( $planet->{body_id}, $item->{build}{x}, $item->{build}{y} );
    }
    else {
        my $url = lc $item->{upgrade}{type};
        $url = "/$url";

        my ($match) =
            grep { $buildings->{$_}{url} eq $url }
            grep { defined $item->{upgrade}{x} ? ( $item->{upgrade}{x} eq $buildings->{$_}{x} ) : 1 }
            grep { defined $item->{upgrade}{y} ? ( $item->{upgrade}{y} eq $buildings->{$_}{y} ) : 1 }
            keys %$buildings;

        die "building not found: '$url'"
            if !$match;

        my $building = $client->building( id => $match, type => $item->{upgrade}{type} );

        printf  "Upgrading %s at %d,%d\n",
            $item->{upgrade}{type},
            $buildings->{$match}{x},
            $buildings->{$match}{y};

        $return = $building->upgrade;
    }

    if ( my $levels = $item->{levels} ) {
        my $copy = clone( $item );

        delete $copy->{levels};

        if ( $item->{build} ) {
            $copy->{upgrade} = delete $copy->{build};
        }
        else {
            # need to get x,y in case there's another of the same type already built
            my $building = $client->building(
                id   => $return->{building}{id},
                type => $item->{upgrade}{type},
            );

            my $return = $building->view;

            $copy->{upgrade}{x} = $return->{building}{x};
            $copy->{upgrade}{y} = $return->{building}{y};
        }

        # already handled 1
        $levels--;

        for my $level ( 1 .. $levels ) {
            splice @queue, $i+$level, 0, clone( $copy );
        }
    }

    if ( $item->{subsidize} ) {
        my $dev_min = get_dev_min();

        if ( $dev_min ) {
            print "Subsidizing build\n";

            $dev_min->subsidize_build_queue;
            sleep 1;
            next;
        }
    }

    # quit if we're at the end of the queue
    last if !$queue[$i+1];

    my $wait = $return->{building}{pending_build}{seconds_remaining};

    sleep( $wait+$build_gap );
}

sub build_remaining {
    $buildings = $planet->get_buildings->{buildings};

    return
        max
        grep { defined }
        map { $buildings->{$_}{pending_build}{seconds_remaining} }
            keys %$buildings;
}

sub get_dev_min {
    return $dev_min if $dev_min;

    my $id = first {
        $buildings->{$_}{url} eq "/development"
    } keys %$buildings;

    if ( !$id ) {
        warn "Cannot subsidize without a Development Ministry\n";
        return;
    }

    return $client->building( id => $id, type => "Development" );
}

sub usage {
    my ($message) = @_;

    $message = $message ? "$message\n\n" : '';

    die <<"END_USAGE";
${message}Usage: $0 CONFIG_FILE BUILD_CONFIG_FILE

CONFIG_FILE defaults to 'lacuna.yml' in the current directory.

BUILD_CONFIG_FILE defaults to 'build_plan.yml' in the current directory.

See the examples/build_plan.yml for an example BUILD_CONFIG_FILE.

TO-DO
=====
Adding checks for resource and waste levels.
Currently it will die if trying to build/upgrade with insufficient resources.
It will also overflow your waste stores with total disregard for hygiene.

END_USAGE

}