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 Number::Format qw( format_number );
use List::Util qw( max );
use Games::Lacuna::Client ();
use Getopt::Long qw(GetOptions);
use File::Basename;


##----------------------------------------------------------------------
##----------------------------------------------------------------------
## no critic (RequireArgUnpacking)
sub accumlate_hash
{
  my $lh_hash_ref  = shift;
  my @rh_hash_refs = @_;

  foreach my $rh_hash_ref (@rh_hash_refs)
  {
    while (my ($key, $value) = each(%{$rh_hash_ref}))
    {
      if (is_number($value))
      {
        if (!exists($lh_hash_ref->{$key}))
        {
          $lh_hash_ref->{$key} = $value;
        }
        else
        {
          $lh_hash_ref->{$key} += $value;
        }
      }
    }
  }
  return;
}
## use critic
##----------------------------------------------------------------------
##----------------------------------------------------------------------
sub is_number
{
  my $value = shift;
  if (defined($value))
  {
    return ((ref($value) eq qq{}) && ($value =~ /^\d+$/x));
  }
  return;
}

##----------------------------------------------------------------------
##----------------------------------------------------------------------
sub print_hash
{
  my $hash_ref = shift;

  my $column = 0;

  foreach my $key (sort(keys(%{$hash_ref})))
  {
    printf("  %12s %10s",
      $key, (is_number($hash_ref->{$key}) ? $hash_ref->{$key} : qq{-}));
    if (++$column >= 3)
    {
      print qq{\n};
      $column = 0;
    }
  }
  if ($column)
  {
    print qq{\n};
  }
  return;
}

##----------------------------------------------------------------------
##----------------------------------------------------------------------
sub show_usage
{
  my $script = basename($0);

  print << "_END_USAGE_";
Usage:  perl $script {--food} {--ore} {--planet="Planet Name"} account_file

This script will show the inventory of each planet in the user's Empire.

Valid options:
  --food                  Show only Food Reservers
  --ore                   Show only Ore Storage
  --planet="PLANET NAME"  Show information for specified planet
  account_file            Configuration file DEFAULT: lacuna.yml

_END_USAGE_

  return;
}
##----------------------------------------------------------------------
## MAIN script body
##----------------------------------------------------------------------
my $show_usage;
my $target_planet;
my $show_ore;
my $show_food;
my $debug_level = 0;
my $cfg_file;

## Pass through unknown parameters in @ARGV
Getopt::Long::Configure(qw(pass_through ));

GetOptions(
    'help'      => \$show_usage,
    'food!'     => \$show_food,
    'ore!'      => \$show_ore,
    'planet=s'  => \$target_planet,
    'debug+'    => \$debug_level,
    'config=s'  => \$cfg_file,
);

if ($show_usage)
{
  show_usage();
  exit(0);
}

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";
  }
}

if (!$cfg_file)
{
  $cfg_file = shift(@ARGV) || 'lacuna.yml';
}

if (! -e $cfg_file)
{
  die(qq{Could not find the config file "$cfg_file"\n});
}

## See if there are any unknown args
if (scalar(@ARGV))
{
  print qq{ERROR: Unknown argument(s): "}, join(qq{", "}, @ARGV), qq{"\n\n};
  show_usage();
  exit(1);
}

## Create the Client object
my $client = Games::Lacuna::Client->new(
  cfg_file => $cfg_file,
  debug    => ($debug_level ? 1 : 0),
);

## List for types of resources to check
my @types = ();

## See if user specified food
if ($show_food)
{
  push(@types, qq{food});
}

## See if user specified ore
if ($show_ore)
{
  push(@types, qq{ore});
}

## Default to both, if neither is given
if (scalar(@types) == 0)
{
  @types = qw(food ore);
}


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

## Hash to hold storage by planet
my $stores = {};

## Scan each planet
PLANET_LOOP:
foreach my $planet_id (sort keys %$planets)
{
  my $planet_name = $planets->{$planet_id};

  ## If we are looking for only one planet
  if ($target_planet && (uc($planet_name) ne uc($target_planet)))
  {
    ## This isn't the planet, next
    next PLANET_LOOP;
  }

  ## Load planet data
  my $planet    = $client->body(id => $planet_id);
  my $result    = $planet->get_buildings;
  ## Extract body from the results
  my $body      = $result->{status}->{body};
  ## Create reference for easier to read code
  my $buildings = $result->{buildings};

  ## List for resource storage buildings found on planet
  my @storage_buildings = ();

  ## Iterate through the types
  for my $type (@types)
  {
    ## initialize hash
    $stores->{$planet_name}->{$type} = {};

    ## Determine name of building, based on resource type
    my $building = {
      food => 'Food Reserve',
      ore  => 'Ore Storage Tanks',
    }->{$type};

    ## Iterate through buildings
    while (my ($building_id, $building_ref) = each(%{$buildings}))
    {
      ## See if it is what we are looking for
      if ($building_ref->{name} eq $building)
      {
        ## Store it in the list
        push(
          @storage_buildings,
          {
            id   => $building_id,
            type => $type,
            building_type =>
              {food => qq{FoodReserve}, ore => qq{OreStorage}}->{$type},
          }
        );
      }
    }
  }

  ## Iterate through the list of storage buildings
  foreach my $info_ref (@storage_buildings)
  {
    ## Get the view info, which has the food_stored or ore_stored key
    my $view = $client->building(
      id   => $info_ref->{id},
      type => $info_ref->{building_type}
    )->view;

    ## Set the key by adding _stored to the type
    my $key = $info_ref->{type} . qq{_stored};
    ## Accumulate the totals into the planet hash
    accumlate_hash($stores->{$planet_name}->{$info_ref->{type}}, $view->{$key});
  }
}

## Hash for the empire totals
my $empire_stores = {food => {}, ore => {}};

## Iterate through the planet stores
foreach my $planet_name (sort(keys(%{$stores})))
{
  print "$planet_name\n";
  print "=" x length $planet_name;
  print "\n";

  ## Iterate through the resource types
  foreach my $type (@types)
  {
    print qq{$type\n}, qq{-} x length($type), qq{\n};
    print_hash($stores->{$planet_name}->{$type});
    accumlate_hash($empire_stores->{$type}, $stores->{$planet_name}->{$type});
    print qq{\n};
  }

}

## Display the empire wide totals
if (!$target_planet)
{
  print qq{Empire Totals\n=============\n};
  foreach my $type (@types)
  {
    print qq{$type\n}, qq{-} x length($type), qq{\n};
    print_hash($empire_stores->{$type});
    print qq{\n};
  }
}