package Games::Wormy::WormEdit;

use 5.010;
use strict;
use warnings;

use Parse::Binary::Nested qw(unpackf);

our $VERSION = '1.00';

our %MAGICID = (
	"WormEdit053\000LVL" => 53,
	"WormEdit\34195\000LVL" => 95,
	"WormEdit\34194\000LVL" => 94,
	"WormEdit\34193\000LVL" => 93,
);

sub read {
	my ($self, $input, $override) = @_;
	my ($id, $version) = (substr($input, 0, 15), ord substr($input, 15, 1));
	my $fileversion = $MAGICID{$id}
		or die "File does not match any known WormEdit level header\n";

	if ($override) {
		warn "Override version $version to $override\n";
		$version = $override;
	}
	elsif ($version != $fileversion) {
		warn "Unexpected version $version (expecting $fileversion)\n";
	}
	elsif ($version == 95) {
		# auto-detect exact variant
		if (ord substr($input, 70, 1) ~~ [1 .. 8]) {
			# valid sprite length instead of description byte
			# (which is usually a letter or nul)
			$version = 94;
		}
		elsif (ord substr($input, 147, 1) == 0) {
			# nul of finish type is 2 bytes later (unlike first char of message)
			$version = 96;
		}
		warn "Ambiguous file version 95; guessing subversion $version\n";
	};

	$fileversion += 100 if $fileversion < 90;  # 93..95 came before 50..53

	my @FORMAT = (
		magic       => 'a15',
		version     => 'C',
		name        => 'C/a32',
		description => 'C/a64x256',
		levelcount  => [1,
			single => 'C',
			multi  => 'C',
			race   => 'C',
			ctf    => 'C',
			total  => 'C',
		],
		moderef     => [1,
			map { (start => $_, end => $_) } [1,
				single     => 'C',
				peaworm    => 'C',
				tron       => 'C',
				deathmatch => 'C',
				foodmatch  => 'C',
				multifood  => 'C',
				timematch  => 'C',
				race       => 'C',
				ctf        => 'C',
				reserved   => 'x',
			],
		],
		sprite     => ['8C',
			line => 'B8',
		],
		finish      => [1,
			type    => 's',
			message => 'C/a255',
			code    => 'C/a255',
			reserved=> 'x256',
		],
		hiname      => 'a3',
		levels      => ['*', # levelcount->total actually
			id         => 'C/a22',
			name       => 'C/a22',
			size       => 'C',
			peas       => 'C',
			delay      => 'C',
			growth     => 'C',
			bsize      => 'C',
			sprite     => ['8C',
				line => 'B8',
			],
			balls      => ['32C',
				y   => 'C',
				x   => 'C',
				dir => 'C',
			],
			worms      => [4,
				d => 'C',
				y => 'C',
				x => 'C',
			],
			width      => 'C',
			height     => 'C',
			flags      => [2,
				y => 'C',
				x => 'C',
			],
			objects    => ['128C',
				type => 'C',
				x1   => 'C',
				y1   => 'C',
				x2   => 'C',
				y2   => 'C',
			],
		],
	);

	given ($fileversion) {
		when (153) { } # current @FORMAT
			$FORMAT[7] = 'C/a64'; # no reserved space after description
			splice @{ $FORMAT[15] }, -2; # finish reserve
			$FORMAT[-1]->[-1]->[0] = '32C'; # less objects
			ref $_ and pop @$_ for @{ $FORMAT[11] }; # 9 moderefs
		when ($version == 96) { }
			ref $_ and pop @$_ for @{ $FORMAT[11] }; # only 8 moderefs (no ctf)
			splice @FORMAT, 6, 2 if $version <= 94;  # earlier version without description
		when (95) { }
			splice @{ $FORMAT[7] }, 4, 2;  # no race
			splice @{ $FORMAT[13] }, 4, 2; # no enddata
			splice @{ $FORMAT[-1] }, 1, 2; # no name
		when (94) { }
			splice @FORMAT, 14, 2; # no hiname
			$FORMAT[-1]->[0] = 64; # constant amount of levels
		when (93) { }
		default {
			die "Cannot parse data for Wormedit $fileversion/$version\n";
		}
	}

	# convert to an easily accessible hash
	push @FORMAT, -trail => 'a*';
	my $data = unpackf(\@FORMAT, $input);
	warn "Trailing data left unparsed\n" if length delete $data->{-trail};
	$data->{format} = 'WormEdit';
	return $data;
}

1;

__END__

=head1 NAME

Games::Wormy::WormEdit - Read Wormy levelset from a wormedit file

=head1 SYNOPSIS

	my $levelset = Games::Wormy::WormEdit->read($filecontents);
	print $levelset->{name};

=head1 AUTHOR

Mischa POSLAWSKY <perl@shiar.org>

=head1 LICENSE

LGPL version 3.

