#!/usr/bin/perl
use strict;
my($app_svnid) = '$HeadURL$ $LastChangedRevision$';
#  Allow bare words, so &ade_err_error() calls look nicer.
no strict 'subs';
use lib substr(`ade-config ade_lib_prefix`,0,-1) . '/include';

use ADE;
use Getopt::Long qw(:config no_ignore_case);
use Geo::Gpx;

my(%gpxsplit_chunk_defined_errors) = (
    gpxsplit_chunk_err_misc         => { fmt => "%s" },
);

#  Options
my ($opt_split_mode, $opt_split_mode_parameters_ref, $opt_suffixes_ref, $opt_routepoint_renumber_flag);
my ($routepoint_index);

sub gpxsplit_chunk
{
    my($errstack_ref) = @_;
    my($rc);
    my($file, $in_handle);

    #  Register application-specific errors
    &ade_err_registerdefderrs(\%gpxsplit_chunk_defined_errors);

    #  Defaults for options
    $opt_split_mode = undef;
    $opt_suffixes_ref = [ map { sprintf("%02d", $_) } ( 0..99 ) ];
    $opt_routepoint_renumber_flag = 0;

    #  Register options
    if (($rc=&ade_opt_register($errstack_ref, "", "count:i,regex:s,suffixes:s,renumber", "main::gpxsplit_chunk_opt_handler_%s")) != $ade_err_ok) {
        return($rc);
    }

    #  Register handler functions
    if (($rc=&ade_msg_register($errstack_ref, \&gpxsplit_chunk_usage, \&gpxsplit_chunk_version, \&gpxsplit_chunk_listpaths)) != $ade_err_ok) {
        return($rc);
    }

    #  Process options
    &ade_err_debug($errstack_ref, 10, "gpxsplit_chunk: processing options ...");
    if (($rc=&ade_opt_process($errstack_ref)) != $ade_err_ok) {
        return($rc);
    }

    #  Sanity checks and derivations
    if ($opt_split_mode eq 'count') {
        if (${$opt_split_mode_parameters_ref}[0] < 2) {
            &ade_err_error($errstack_ref, ade_err_misc, "${$opt_split_mode_parameters_ref}[0]: invalid split count (hint: must be at least 2)");
            return($ade_err_fail);
        }
    } elsif ($opt_split_mode eq 'regex') {
        #  Check regex is valid
    } else {
        &ade_msg_usage($errstack_ref);
    }
    $routepoint_index = 0;

    #  Guts start here
    if ($#ARGV+1 == 0) {
        if (($rc=&process_input_file($errstack_ref, undef, $opt_split_mode, $opt_split_mode_parameters_ref, $opt_suffixes_ref)) != $ade_err_ok) {
            return($rc);
        }
    } else {
        foreach $file (@ARGV) {
            if (($rc=&process_input_file($errstack_ref, $file, $opt_split_mode, $opt_split_mode_parameters_ref, $opt_suffixes_ref)) != $ade_err_ok) {
                return($rc);
            }
        }
    }

    return($ade_err_ok);
}

sub gpxsplit_chunk_opt_handler_count
{
    my($errstack_ref, $split_mode_parameter_string) = @_;

    $opt_split_mode = 'count';
    $opt_split_mode_parameters_ref = [ int($split_mode_parameter_string) ];

    return $ade_err_ok;
}

sub gpxsplit_chunk_opt_handler_regex
{
    my($errstack_ref, $split_mode_parameter_string) = @_;

    $opt_split_mode = 'regex';
    $opt_split_mode_parameters_ref = [ $split_mode_parameter_string ];

    return $ade_err_ok;
}

sub gpxsplit_chunk_opt_handler_suffixes
{
    my($errstack_ref, $suffixes_string) = @_;

    $opt_suffixes_ref = [ split(/,/, $suffixes_string) ];

    return $ade_err_ok;
}

sub gpxsplit_chunk_opt_handler_renumber
{
    my($errstack_ref) = @_;

    $opt_routepoint_renumber_flag = 1;

    return $ade_err_ok;
}

sub gpxsplit_chunk_version
{
    my($errstack_ref, $version_text_ref) = @_;

    return(&ade_smf_extractversionfromsvnstring($errstack_ref, $app_svnid, $version_text_ref));
}

sub gpxsplit_chunk_listpaths
{
    my($errstack_ref, $pathlist_text_ref) = @_;
    my($rc);

    ${$pathlist_text_ref} = undef;

    return($ade_err_ok);
}

sub gpxsplit_chunk_usage
{
    my($errstack_ref, $usage_text_ref, $passno) = @_;

    if ($passno == 1) {
        ${$usage_text_ref} = "[ <gpx-file> ]\n";
    } elsif ($passno == 2) {
        ${$usage_text_ref} = "                     --count=<num>           split every <num> routepoints\n" .
                             "                     --regex=<regex>         split at routepoints like <regex>\n" .
                             "                     --suffixes=<suffix>,... append suffixes to routes\n" .
                             "                     --renumber              renumber routepoints\n";
    }

    return($ade_err_ok);
}

sub process_input_file
{
    my($errstack_ref, $file, $opt_split_mode, $parameter_list_ref, $suffixes_ref) = @_;
    my($in_gpx, $in_count, $in_name, $out_file_index, $i, $in_handle);
    my($rc);


    if (not defined($file)) {
       $in_handle = \*STDIN;
    } elsif (!open $in_handle, '<', $file) {
        &ade_err_error($errstack_ref, ade_err_misc, "$file: couldn't open");
        return($ade_err_fail);
    }
    &ade_err_info($errstack_ref, sprintf("%s: processing file ...", defined($file) ? $file : "stdin"));

    $in_gpx = Geo::Gpx->new(input => $in_handle);
    &ade_err_debug($errstack_ref, 10, sprintf("process_input_file: input contains %d route(s)", $#{$in_gpx->routes()}+1));
    for ($i=0; $i<$#{$in_gpx->routes()}+1; $i++) {
        if (($rc=&process_input_route($errstack_ref, ${$in_gpx->routes()}[$i], $opt_split_mode, $parameter_list_ref, $suffixes_ref)) != $ade_err_ok) {
            close $in_handle if (not defined($file));
            return($rc);
        }
    }

    close $in_handle if (not defined($file));

    return($ade_err_ok);
}

sub process_input_route
{
    my($errstack_ref, $route_ref, $opt_split_mode, $parameter_list_ref, $suffixes_ref) = @_;
    my($in_count, $in_name, $out_file_index, $in_lower_index, $in_upper_index, $out_file, $out_fh, $i, $out_name, @in_points, @out_points, $in_index, @suffixes, @in_name_components);
    my($rc);

    $in_name = ${$route_ref}{name};
    &ade_err_info($errstack_ref, "$in_name: processing route ...");
    @in_points = @{${$route_ref}{points}};
    $in_count = $#{${$route_ref}{points}}+1;
    @suffixes = @{$suffixes_ref};
    @in_name_components = split(/\b/, $in_name);

    $out_file_index = 0;
    $in_index = 0;
    while ($in_index != $#in_points+1) {
        #  Copy over a point
        push(@out_points, $in_points[$in_index]);

        #  If have just copied split point then write route and initialise the next route
        #  with the *same* routepoint (i.e. that point is to appear in *two* routes).
        if (($opt_split_mode eq 'count' and $#out_points+1 == ${$parameter_list_ref}[0]) or
            ($opt_split_mode eq 'regex' and $in_points[$in_index]{name} =~ /^${$parameter_list_ref}[0]$/)) {
            &ade_err_debug($errstack_ref, 10, "process_input_route: flushing complete route ...");
            if (not defined($suffixes[$out_file_index])) {
                &ade_err_error($errstack_ref, ade_err_misc, "ran out of suffixes! (hint: do you need to provide more?)");
                return($ade_err_fail);
            }
            $out_name = $in_name_components[0] . "." . $suffixes[$out_file_index] . join('', splice(@in_name_components,1));
            if (($rc=&write_route($errstack_ref, $out_name, \@out_points)) != $ade_err_ok) {
                return($rc);
            }
            $out_file_index++;
            undef @out_points;
            push(@out_points, $in_points[$in_index]);
        }

        #last if ($in_index >= $#in_points);
        $in_index++;
    }

    #  Flush any partially written route (which must consist of *more* than 1 routepoint;
    #  if the route consists of a *single* routepoint then that routepoint
    #  has already been written as the last routepoint in the previously written file).
    if ($#out_points+1 > 1) {
        if (not defined($suffixes[$out_file_index])) {
            &ade_err_error($errstack_ref, ade_err_misc, "ran out of suffixes! (hint: do you need to provide more?)");
            return($ade_err_fail);
        }
        &ade_err_debug($errstack_ref, 10, "process_input_route: flushing partial route ...");
        $out_name = $in_name_components[0] . "." . $suffixes[$out_file_index] . join('', splice(@in_name_components,1));
        if (($rc=&write_route($errstack_ref, $out_name, \@out_points)) != $ade_err_ok) {
            return($rc);
        }
        $out_file_index++;
    }

    return($ade_err_ok);
}

sub write_route
{
    my ($errstack_ref, $routename, $out_points_ref) = @_;
    my ($i, $out_fh, $out_gpx, $width, $fs_compliant_routename);
    my($rc);

    if ($opt_routepoint_renumber_flag) {
        $width = int(0.999+log($#{$out_points_ref}+1)/log(10));
        &ade_err_debug($errstack_ref, 10, "write_route: renumbering routepoints with width $width ...");
        for ($i=0; $i<$#{$out_points_ref}+1; $i++) {
            ${$out_points_ref}[$i]{name} = sprintf("RPT%0*d", $width, $routepoint_index++);
        }
    }
    $out_gpx = Geo::Gpx->new();
    #  Change all '/' to '-' to allow them to be written to filesystem.
    ($fs_compliant_routename = $routename) =~ s/\//-/g;
    open($out_fh, ">", "$fs_compliant_routename.gpx") || die;
    $out_gpx->routes([ { 'name' => $routename, 'points' => $out_points_ref } ]);
    printf($out_fh $out_gpx->xml('1.0'));
    close($out_fh);

    return($ade_err_ok);
}

&ade_gep_main(\&gpxsplit_chunk);
