# Helper module to parse and interpret a G-code file,
# invoking a callback for each move extracted from the G-code.
# Currently used by the automatic tests only.
package Slic3r::GCode::Reader;
use Moo;

has 'config'    => (is => 'ro', default => sub { Slic3r::Config::GCode->new });
has 'X' => (is => 'rw', default => sub {0});
has 'Y' => (is => 'rw', default => sub {0});
has 'Z' => (is => 'rw', default => sub {0});
has 'E' => (is => 'rw', default => sub {0});
has 'F' => (is => 'rw', default => sub {0});
has '_extrusion_axis' => (is => 'rw', default => sub {"E"});

our $Verbose = 0;
my @AXES = qw(X Y Z E);

sub apply_print_config {
    my ($self, $print_config) = @_;
    
    $self->config->apply_static($print_config);
    $self->_extrusion_axis($self->config->get_extrusion_axis);
}

sub clone {
    my $self = shift;
    return (ref $self)->new(
        map { $_ => $self->$_ } (@AXES, 'F', '_extrusion_axis', 'config'),
    );
}

sub parse {
    my $self = shift;
    my ($gcode, $cb) = @_;
    
    foreach my $raw_line (split /\R+/, $gcode) {
        print "$raw_line\n" if $Verbose || $ENV{SLIC3R_TESTS_GCODE};
        my $line = $raw_line;
        $line =~ s/\s*;(.*)//; # strip comment
        my %info = (comment => $1, raw => $raw_line);
        
        # parse command
        my ($command, @args) = split /\s+/, $line;
        $command //= '';
        my %args = map { /([A-Z])(.*)/; ($1 => $2) } @args;
        
        # convert extrusion axis
        if (exists $args{ $self->_extrusion_axis }) {
            $args{E} = $args{ $self->_extrusion_axis };
        }
        
        # check motion
        if ($command =~ /^G[01]$/) {
            foreach my $axis (@AXES) {
                if (exists $args{$axis}) {
                    $self->$axis(0) if $axis eq 'E' && $self->config->use_relative_e_distances;
                    $info{"dist_$axis"} = $args{$axis} - $self->$axis;
                    $info{"new_$axis"}  = $args{$axis};
                } else {
                    $info{"dist_$axis"} = 0;
                    $info{"new_$axis"}  = $self->$axis;
                }
            }
            $info{dist_XY} = sqrt(($info{dist_X}**2) + ($info{dist_Y}**2));
            if (exists $args{E}) {
                if ($info{dist_E} > 0) {
                    $info{extruding} = 1;
                } elsif ($info{dist_E} < 0) {
                    $info{retracting} = 1
                }
            } else {
                $info{travel} = 1;
            }
        }
        
        # run callback
        $cb->($self, $command, \%args, \%info);
        
        # update coordinates
        if ($command =~ /^(?:G[01]|G92)$/) {
            for my $axis (@AXES, 'F') {
                $self->$axis($args{$axis}) if exists $args{$axis};
            }
        }
        
        # TODO: update temperatures
    }
}

1;
