The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#
# This file is part of Games-Risk
#
# This software is Copyright (c) 2008 by Jerome Quelin.
#
# This is free software, licensed under:
#
#   The GNU General Public License, Version 3, June 2007
#
use 5.010;
use strict;
use warnings;

package Games::Risk::App::Command::import;
# ABSTRACT: import a map designed for other risk games
$Games::Risk::App::Command::import::VERSION = '4.000';
use Data::Dump  qw{ dumpf };
use File::Copy  qw{ copy };
use Path::Class 0.22; # basename

use Games::Risk::App -command;
use Games::Risk::Logger qw{ debug };
use Games::Risk::Utils  qw{ $SHAREDIR };


# -- public methods

sub description { 'Import a Risk map designed for other Risk games.'; }

sub opt_spec {
    my $self = shift;
    return (
        [],
        [ "input|i=s"    => "path to the input map", {required=>1} ],
        [ "module|m=s"   => "path to the module"          ],
        [ "sharedir|s=s" => "path to the share directory" ],
    );
}

sub execute {
    my ($self, $opts, $args) = @_;
    eval "use 5.014";
    die $@ if $@;

    my $mapsdir  = dir( qw{ share maps } );
    my $template = $SHAREDIR->file( qw{ maps template } );

    my $file   = file($opts->{input});
    my $mapdir = $file->parent;
    debug( "parsing map: $file\n" );
    my $map = lc( $file->basename ); $map =~ s/\..*$//;

    # find module name if needed
    my $modname = exists $opts->{module} ? $opts->{module} : ucfirst($map);
    my $module = file( qw{ lib Games Risk Map }, $modname . ".pm" );
    debug( " - module name will be: $modname ($module)\n" );
    $module->remove;

    # find share directory if needed
    my $sharedir = exists $opts->{sharedir}
        ? dir( $opts->{sharedir} )
        : $mapsdir->subdir( $map );
    debug( " - map share directory: $sharedir\n" );
    $sharedir->rmtree;
    $sharedir->mkpath;

    # parse map file
    my ($author, $title, $greyscale, $background, $cardfile, @continents, @countries);
    {{{
    debug( "parsing map file\n" );
    my @lines = $file->slurp;
    my $content = $file->slurp =~ s/\r/\n/gr;
    $author = ( $content =~ /made\s+by\s+(.*)/i )
        ? $1
        : $lines[0] =~ s/^;\s*(.*?)[\r\n]/$1/r;
    chomp $author;

    my ($section, $noline, $id_continent);
    foreach my $line ( @lines ) {
        $noline++;
        $line =~ s/[\r\n]//g;  # remove all end of lines
        $line =~ s/^\s+//;     # trim heading whitespaces
        $line =~ s/\s+$//;     # trim trailing whitespaces
        if ( $line =~ /^\s*$/ )      { } # empty lines
        elsif ( $line=~ /^\s*[#;]/ ) { } # comments

        elsif ( $line =~ /^\[([^]]+)\]$/ ) {
            # changing [section]
            $section = $1;
        }

        #
        if ( ! defined $section ) {
            if ( $line =~ /^name\s+(.*)$/ ) {
                $title = $1;
                debug( " - map title $title\n" );
            }
            else {
                debug( "parse error [head]:$noline\t- line was: '$line'\n" );
            }
        }
        elsif ( $section eq "files" ) {
            if ( $line =~ /^map\s+(.*)$/ ) {
                $greyscale = $mapdir->file($1);
                debug( " - greyscale:  $greyscale\n" );
            }
            elsif ( $line =~ /^pic\s+(.*)$/ ) {
                $background = $mapdir->file($1);
                debug( " - background: $background\n" );
            }
            elsif ( $line =~ /^crd\s+(.+)$/ ) {
                $cardfile = $mapdir->file($1);
                debug( " - cardfile:   $cardfile\n" );
            }
            elsif ( $line =~ /^prv\s+/ ) { } # preview, not needed
            else {
                debug( "parse error [files]:$noline\t- line was: '$line'\n" );
            }
        }
        elsif ( $section eq "continents" ) {
            # get continent params
            $id_continent++;
            my ($name, $bonus, $color) = split /\s+/, $line;
            $name =~ s/[-_]/ /g;
            push @continents, [ $id_continent, $name, $bonus, $color ];
        }
        elsif ( $section eq "countries" ) {
            # get country param
            my ($greyval, $name, $idcont, $x, $y) = split /\s+/, $line;
            $name =~ s/[-_]/ /g;
            debug( "parse error [countries]:$noline\t- continent $idcont does not exist\n" ), break
                if $idcont > $id_continent;
            debug( "parse error [countries]:$noline\t- country $greyval already exists\n" ), break
                if grep { $_->[0] == $greyval } @countries;
            push @countries, [ $greyval, $name, $idcont, $x, $y ];
        }

        elsif ( $section eq "borders" ) {
            my ($id, @neighbours) = split /\s+/, $line;
            my ($country) = grep { $_->[0] == $id } @countries;
            debug( "parse error [borders]:$noline - country $id doesn't exist" ), break
                unless defined $country;
            push @$country, \@neighbours;
        }

        else {
            debug( "parse error: how to parse $section?\n" );
        }
    }
    }}}
    my $name = lc $modname;
    $title //= $modname;
    debug( " - found ". scalar(@continents). " continents\n" );
    debug( " - found ". scalar(@countries). " countries\n" );

    # parse card file
    my (@cards, @missions);
    {{{
    debug( "parsing card file\n" );
    my @lines = $cardfile->slurp;
    my ($section, $noline);
    foreach my $line ( @lines ) {
        $noline++;
        $line =~ s/[\r\n]//g;  # remove all end of lines
        $line =~ s/^\s+//;     # trim heading whitespaces
        $line =~ s/\s+$//;     # trim trailing whitespaces
        if ( $line =~ /^\s*$/)    { } # empty lines
        elsif ( $line =~ /^\s*[#;]/) { } # comments

        elsif ( $line =~ /^\[([^]]+)\]$/) {
            # changing [section]
            $section = $1;
        }

        #
        if ( ! defined $section ) {
            debug( "parse error [head]:$noline\t- line was: '$line'\n" );
        }
        elsif ( $section eq "cards" ) {
            my ($type, $id) = split /\s+/, lc $line;
            $type = 'artillery' if $type eq 'cannon';
            $type = 'joker'     if $type eq 'wildcard';
            push @cards, [ $type, $id ];
        }
        elsif ( $section eq "missions" ) {
            my ($player, $nbc, $armies, $idc1, $idc2, $idc3, $descr) =  split /\s+/, $line, 7;
            $descr =~ s/([A-Z]+)/ucfirst lc $1/ge;
            push @ missions, [$player, $nbc, $armies, $idc1, $idc2, $idc3, $descr];
        }
        default {
            debug( "parse error: how to parse $section?\n" );
        }
    }
    }}}
    debug( " - found ". scalar(@cards). " cards\n" );
    debug( " - found ". scalar(@missions). " missions\n" );

    # prepare template replacements
    my $continents = join "\n", map { _as_code($_) } @continents;
    my $countries  = join "\n", map { _as_code($_) } @countries;
    my $cards      = join "\n", map { _as_code($_) } @cards;
    my $missions   = join "\n", map { _as_code($_) } @missions;

    my $code = $template->slurp;
    $code =~ s/__MODULE_NAME__/$modname/g;
    $code =~ s/__MAP_NAME__/$name/g;
    $code =~ s/__MAP_TITLE__/$title/g;
    $code =~ s/__MAP_AUTHOR__/$author/g;
    $code =~ s/__MAP_CONTINENTS__/$continents/g;
    $code =~ s/__MAP_COUNTRIES__/$countries/g;
    $code =~ s/__MAP_CARDS__/$cards/g;
    $code =~ s/__MAP_MISSIONS__/$missions/g;

    #
    debug( "creating module for map\n" );
    my $fh = $module->openw;
    $fh->print( $code );
    $fh->close;

    my $bg = $sharedir->file( $background->basename =~ s/^([^.]+)/background/r );
    my $gs = $sharedir->file( $greyscale->basename  =~ s/^([^.]+)/greyscale/r );
    debug( "copying background to $bg\n" );
    debug( "copying greyscale  to $gs\n" );
    copy( $background, $bg->stringify );
    copy( $greyscale,  $gs->stringify );
}

sub _as_code {
    my $what = shift;
    my $code = dumpf( $what, sub {
        my($ctx, $ref) = @_;
        return unless $ctx->is_scalar;
        return unless defined $$ref;
        return unless $$ref =~ /^[A-Z]/;
        return { dump => qq{__("$$ref")} };
    } );
    $code =~ s/\n//g;
    return "$code,";
}

1;

__END__

=pod

=head1 NAME

Games::Risk::App::Command::import - import a map designed for other risk games

=head1 VERSION

version 4.000

=head1 DESCRIPTION

This command transforms a map designed for another Risk game to a Perl
module that can be used by C<prisk>.

=head1 AUTHOR

Jerome Quelin

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2008 by Jerome Quelin.

This is free software, licensed under:

  The GNU General Public License, Version 3, June 2007

=cut