#!/usr/bin/perl -w

package Text::JavE;
use strict;

our $VERSION='0.0.2';

=head1 NAME

Text::JavE - view and manipulate ascii art and manipulation files created in JavE.

=head1 DESCRIPTION

JavE (http://www.jave.de/) is an excellent Ascii art editor and animator
written in Java.  Unfortunately it doesn't yet have a scripting interface.
This module aims to make the work of processing its standard files (.jmov)
easy within Perl.

=head1 SYNOPSIS

  use Text::JavE;

  my $j = new Text::JavE;
  while (my $file=shift @ARGV) {
    $j->open_jmov($file);
    for (@{$j->{frames}}) {
      system "cls"; # on Win32.  Try system "clear" on Unix.
      $_->display;
      my $time = $_->{msec};
      select (undef, undef, undef, $time/1000);
    }
  }

=head2 new()

Constructor.  Returns a Text::JavE object which can play or otherwise
manipulate jmov files.

=cut

sub new {
	my $class=shift;
	my $self={
		decoded => [],
	};
	return bless {}, $class;
}

=head2 decode($text)

Internal method to decode a jmov line containing a compressed ascii frame.
There are 2 submethods decode_a and decode_b implementing the various
encoding algorithms JavE currently uses.

=cut

sub decode {
	my $self=shift;
	my $text=shift;
	$self->{code}=$text;
	$text=~s/^([A-Z])(\d+) (\d+) // or die "Text::JavE: invalid format: $text\n";
	(my $alg, $self->{xsize}, $self->{ysize})=($1, $2, $3);
	$self->{algorithm} = $alg;

	if    ($alg eq "A") { $self->decode_a($text);	} 
	elsif ($alg eq "B") { $self->decode_b($text);	} 
	else { 
		warn "Unsupported algorithm: $alg\n" ;
		return;
	}
}

sub decode_a {
	my $self=shift;
	my $text=shift;
	my @decoded; my $decode_line='';
	$self->{decoded}=\@decoded;
	my ($x, $y) = ($self->{xsize}, $self->{ysize});
	while ($text) {
		# print "($text)\n\n";

		# Add normal text
		if ($text=~s/^([^\%]+)//) {
			$decode_line.=$1;
			# print "($1)\n";
		}

		# Add % signs signalled by %%
		if ($text=~s/^((?:%%)+)//) {
			$decode_line.='%' x (length($1)/2);
		}

		# Add newlines (%0)
		if ($text=~s/^%0//) { 
			if (length $decode_line > $x) {
				warn "Line longer than $x declared!\n";
			}
			push @decoded, $decode_line;
			$decode_line='';
		}

		# Add repeated number characters (%3%8 e.g. 3 x "8")
		if ($text=~s/^%([1-9]\d*)%(\d)//) {
			$decode_line.=( $2 x $1); 
		#print "($2 x $1)\n";
		}

		# Add repeated characters (%9x   %3%% etc.)
		if ($text=~s/^%([1-9]\d*)([^%0-9]|%%)//) {
			$decode_line.=( $2 x $1); 
		#print "($2 x $1)\n";
		}

	}
	if (length $decode_line > $x) {
		warn "Line longer than $x declared!\n";
	}
	if (@decoded > $y) {
		warn "More than $y lines declared!\n";
	}
	push @decoded, $decode_line;
}

sub decode_b {
	my $self=shift;
	my $text=shift;
	my ($x, $y) = ($self->{xsize}, $self->{ysize});
	#print "($x, $y)\n";
	my @decoded=unpack( ("A$x" x $y), $text); 
	$self->{decoded}=\@decoded;
}

=head2 display()

Shows the current frame.

=cut

sub display {
	my $self=shift;
	my @decoded=@{$self->{decoded}};
	my $y=$self->{ysize};
	push @decoded, ('') x ($y-@decoded);
	print join "\n", @decoded;
	print "\n";
}

=head2 open_clipart($file)

Opens a clipart file in JavE's .jcf format.
(As far as I know this isn't officially documented, but a
number of sample files are distributed with JavE).

=cut

sub open_clipart {
	my $self=shift;
	my $file=shift;
	my @clips;
	$self->{clips}=\@clips;

	open (JCF, '<', $file) or die "Couldn't open file $file: $!\n";;
	while (my $title=<JCF>) {
		my $j=Text::JavE->new;
		chomp $title;
		chomp(my $artist=<JCF>);
		chomp(my $clip=<JCF>);
		<JCF>; # discard empty line;
		if ($j->decode($clip)) {
			push @clips, {
				title  => $title,
				artist => $artist,
				clip   => $j,
			};
		}
	}
}


=head2 open_jmov($file)

Opens a file in jmov format.
This format is described in detail at 
http://www.jave.de/player/jmov_specification.html

A sample Chickenman animation is included in the
distribution as t.jmov.  More can be found at
http://osfameron.perlmonk.org/chickenman/
Other jmov files can be found at http://www.jave.de

=cut

sub open_jmov {
	my $self=shift;
	my $file=shift;
	my (@frames, %frames);
	$self->{frames}=\@frames;
	$self->{frames_dict}=\%frames;
	my $framenum=0;

	open (JMOV, '<', $file) or die "Couldn't open file $file: $!\n";;
	my $lnum=0;
	my $frame;
	$self->{curr_frame}=\$frame;
	while (my $line=<JMOV>) {
		print "$lnum.";
		#print "[$lnum]\t$line\n";
		$lnum++;
		$line=~/^(.):(.*)$/ or die "JMOV: invalid format at line $lnum: $line\n";
		my ($action, $data)=($1, $2);
		CASE: for ($action) {
			/!/ and do { $self->{filename}=$data; last CASE};
			/\*/ and do { $self->{title}   =$data; last CASE};
			/D/ and do { $self->{date}    =$data; last CASE};
			/J/ and do { 
				$frame=new Text::JavE; 
				$framenum++;
				for (qw(cursorpos cpos2 colour msec action)) {
					$frame->{$_} = $self->{$_};
				} 
				$frame->{num}=$framenum;
				$frame->{frametitle}="frame $framenum";
				$frame->decode($data);
				push @frames, $frame; 	
				last CASE};
			/\|/and do { $self->{cursorpos}=$data; $frame->{cursorpos}=$data; last CASE};
			/\^/and do { $self->{cpos2}=$data; $frame->{cpos2}=$data; last CASE};
			/\+/and do { $self->{msec}=$data; $frame->{msec}=$data; last CASE};
			/C/and do { $self->{colour}=$data; $frame->{colour}=$data; last CASE};
			/A/and do { $self->{action}=$data; $frame->{action}=$data; last CASE};
			/T/and do { $frame->{frametitle}=$data; 
					# print $data;
					last CASE};
		}
	}
	for (@frames) {
		push @{$frames{$_->{frametitle}}}, $_;
	};
	close JMOV or die;
	# print "DONE!";
}

=head1 BUGS

Code is boneheaded and documentation is poor in v0.0.2.
(In v.0.0.1 it consisted of "blah blah blah" so at least
it's improving).

=head1 AUTHOR

osfameron - text-jave@osfameron.abelgratis.co.uk

=cut

1; # return true value