package SVG2::Attribute::Path;

use base "XML2::Attribute";

use strict;
use warnings;
use Carp;

my %imap = (
		m => [qw/x y/],              # move to
		z => [],                     # close path
		l => [qw/x y/],              # line to
		h => ['x'],                  # horz line
		v => ['y'],                  # vert line
		c => [qw/x1 y1 x2 y2 x y/],  # Curveto Cubic Bezier
		s => [qw/x1 y1 x y/],        # Smooth Cubic Bezier
		q => [qw/x1 y1 x y/],        # Curceto Quadratic Bézier
		t => [qw/x y/],              # Smooth Quadratic Bézier
		a => [qw/rx ry xr l s x y/], # Eliptical Arc Curve
);

sub new
{
	my ($proto, %opts) = @_;
	return $proto->SUPER::new(%opts);
}

sub serialise
{
	my ($self) = @_;

	my $lastmode = '';
	my $result = '';
	foreach my $inst ($self->instructions) {
		my %inst = %{$inst};
		$result .= ' ' if length($result);
		my $rel = $inst{'rel'};
		my $mode = $inst{'mode'};
		my @c = @inst{@{$imap{$mode}}};
		$mode = uc($mode) if not $rel;
		$result .= $mode.' ' if $mode ne $lastmode;
		$result .= join(' ', @c) if @c;
		$lastmode = $mode;
	}
	$result =~ s/\s+$//;
	return $result;
}

sub deserialise
{
	my ($self, $path) = @_;

	$path =~ s/(\+|-)/ $1/g;
	$path =~ s/([MmZzLlHhVvCcSsQqTtAa])/ $1 /g;
	$path =~ s/,/ /g;
	$path =~ s/^\s+//;
	$path =~ s/\s+$//;

	my @path = split(/\s+/, $path);
	my @inst;
	my $mode;
	for(my $i = 0; $i <= $#path; $i++) {
		my $s = $path[$i];
		next if not defined($s);
		if(defined($imap{lc($s)})) {
			$mode = $s;
			if(not @{$imap{lc($mode)}}) {
				# Make sure blind instructions are added
				push @inst, _instruction($mode);
			} else {
				next;
			}
		} elsif($s =~ /^\-*\d+\.*\d*$/) {
			# Data for current mode
			my $length = @{$imap{lc($mode)}};
			my $end = $i + $length;
			# Next i if no co-ords to gather
			next if not $end;
			# Add the instruction
			push @inst, _instruction($mode, @path[$i..$end]);
			# Inplicit lineto after moveto
			$mode = 'l' if($mode eq 'm');
			$mode = 'L' if($mode eq 'M');
			$i += $length - 1;
		} else {
			die "Error in path, unexpected instruction '$s' - ".join(', ', keys(%imap))."\n";
		}
	}
	$self->{'path'} = \@inst;
	return $self;
}

sub _instruction
{
    my ($mode, @c) = @_;
    my $reletive = ($mode =~ /A-Z/) ? 1 : 0;
    $mode = lc($mode);
    my %inst = ( mode => $mode, rel => $reletive );
    @inst{@{$imap{$mode}}} = @c if @c;
    return \%inst;
}

sub instructions
{
	my ($self) = @_;
	return @{$self->{'path'}};
}

return 1;
