package PerlRPG::Drawing;
use strict;
require Exporter;
use SDL;
use SDL::Event;
use SDLx::App;
use SDLx::Sprite;
use SDLx::Sprite::Animated;
use SDLx::Text;
use PerlRPG::Console;
use PerlRPG::Game;
use PerlRPG::Assets;
use PerlRPG::Math;

use vars qw/@ISA @EXPORT @EXPORT_OK %VisibleSprites/;
@ISA = qw/Exporter/;
@EXPORT = qw/ ShowSprite HideSprite MoveSprite SetBackgroundColor RenderSprites DrawSpeech/;
@EXPORT_OK = ();

# FIXME - This should be exported already?
*GetOption = \&PerlRPG::Game::GetOption;

sub DrawSpeech {
	my($sayer, $text)=@_;
	my $app = GetOption('App');
	my $speech_border = GetOption('SpeechBorderSize');
	my $speech_border_color = [split(/\s+/,GetOption('SpeechBorderColor'))];
	my $speech_bg_color = [split(/\s+/,GetOption('SpeechBGColor'))];
	my $speech_name_bg_color = [split(/\s+/,GetOption('SpeechNameBGColor'))];

	my $sprite = GetAsset($sayer->{'Sprite'});
	my $sprite_x = 0;
	my $speech_x = 0;

	my $sprite_w = $sprite->clip()->w();
	my $sprite_h = $sprite->clip()->h();

	my $sprite_y = $app->height() - $sprite_h;
	my $speech_y = $sprite_y;

	if($sayer->{'DrawLeft'}) {
		$speech_x = $sprite_w;
	} else {
		$sprite_x = $app->width() - $sprite_w;
	}
	$sprite = undef; # ShowSprite may unload this, we don't want to keep a handle to it
	ShowSprite('_SayerSprite', $sayer->{'Sprite'}, $sprite_x, $sprite_y, 254, @{ $sayer->{'SpriteOpts'} });

	my $name_obj = SDLx::Text->new();
	$name_obj->color( $sayer->{'TextColor'} );
	$name_obj->text( $sayer->{'DisplayName'} );

	my $text_obj = SDLx::Text->new();
	$text_obj->color( $sayer->{'TextColor'} );
	$text_obj->text( $text );

	my $surface = SDLx::Surface->new(	width	=> $app->width() - $sprite_w,
						height	=> $sprite_h,
						color	=> $speech_border_color,
					);
	$surface->draw_rect(	[0, 0, $surface->width(), $name_obj->h()], [@$speech_name_bg_color[0,1,2], 255] );
	$surface->draw_rect(	[	$speech_border,
					$name_obj->h() + $speech_border,
					$surface->width()-2*$speech_border,
					$surface->height()-$name_obj->h()-(2*$speech_border)	],
				[@$speech_bg_color[0,1,2], 255]
			   );

	my $text_x = ($surface->width() - $text_obj->w())/2;
	my $text_y = $name_obj->h() + ($surface->height()-$name_obj->h() - $text_obj->h())/2;
	$name_obj->write_xy( $surface, 0, 0 );
	$text_obj->write_xy( $surface, $text_x, $text_y );

	# Recreate alpha channels
	if($speech_name_bg_color->[3] != 255) {
		Surface_ChangeColor(
			$surface,
			[@$speech_name_bg_color[0,1,2],255],
			$speech_name_bg_color,
			[0, 0, $surface->width(), $name_obj->h()]
		);
	}

	if($speech_bg_color->[3] != 255) {
		Surface_ChangeColor( $surface, [@$speech_bg_color[0,1,2],255], $speech_bg_color,
					[	$speech_border,
						$name_obj->h() + $speech_border,
						$surface->width() - 2 * $speech_border,
						$surface->height() - $name_obj->h() - 2 * $speech_border
					]
					);
	}

	$VisibleSprites{'_SayerText'} = {
		'Depth'		=> 255,
		'X'		=> $speech_x,
		'Y'		=> $speech_y,
		'Sprite'	=> SDLx::Sprite->new( surface => $surface ),
	};
		
	return 1;
}

sub ShowSprite {
	my($name, $file, $x, $y, $d, @opt)=@_;
	LogData(DEBUG, "Showing sprite $file as $name");

	my $t = GetAssetType($file);
	if($t ne 'Sprite' && $t ne 'ASprite') {
		LogData(WARN, "Unable to ShowSprite on non-sprite asset '$file'");
		return;
	}

	my $a = GetAsset($file);
	if($t eq 'ASprite') {
		# Unload the asset so that future versions of the same name will be fresh copies
		# This is necessary for instances where the frame position doesn't match
		UnloadAsset($file);

		# Start animation
		$a->start();
	}

	foreach my $o (@opt) {
		my($name, $val)=$o=~/^\s*(\S+?)=(\S*)/;
		$name = $o unless($name);
		$name = lc($name);
		if($name eq 'reverse' || $name eq 'circular') {
			if($t eq 'ASprite') {
				$a->type($name);
			} else {
				LogData(WARN, "%s - Non animated sprite can't have %s property", GetScriptPosition(), $name);
			}
		} elsif($name eq 'max_loops') {
			if($t eq 'ASprite') {
				$a->max_loops($val);
			} else {
				LogData(WARN, "%s - Non animated sprite can't have %s property", GetScriptPisition(), $name);
			}
		} elsif($name eq 'flip') {
			# Don't want to flip back and forth every time
			UnloadAsset($file);
			my $s=$a->surface();
			$a->surface( Surface_Flip($s) );
		} elsif($name eq 'ticks') {
			if($t eq 'ASprite') {
				$val = 1 unless($val);
				$a->ticks_per_frame($val);
			} else {
				LogData(WARN, "%s - Non animated sprite can't have %s property", GetScriptPosition(), $name);
			}
		} else {
			LogData(WARN, "%s - Unknown sprite property '%s'", GetScriptPosition(), $name);
		}
	}

	$VisibleSprites{$name}={
		'Depth'		=> $d,
		'X'		=> $x,
		'Y'		=> $y,
		'Sprite'	=> $a,
	};
	return 1;
}

sub HideSprite {
	my($name)=@_;
	delete $VisibleSprites{$name};
}

sub MoveSprite {
	my($name, $x, $y)=@_;
	return undef unless(exists $VisibleSprites{$name});
	$VisibleSprites{$name}{'X'} = $x;
	$VisibleSprites{$name}{'Y'} = $y;
}

sub SetBackgroundColor {
	my $color;
	if(!@_) {
		$color = [0, 0, 0, 255];
	} elsif(ref $_[0]) {
		$color = $_[0];
	} else {
		$color = [@_];
	}

	# Force alpha channel?
	$color->[3]=255;

	my $app = GetOption('App');
	my $surface = SDLx::Surface->new(width => $app->width(), height => $app->height());
	$surface->draw_rect([0, 0, $surface->width(), $surface->height()], $color);
	my $sprite = SDLx::Sprite->new( surface => $surface );
	$VisibleSprites{'Background'}={
		'Depth'		=> -1,
		'X'		=> 0,
		'Y'		=> 0,
		'Sprite'	=> $sprite
	};
}

sub RenderSprites {
	my $app = GetOption('App');
	my @names = sort { $VisibleSprites{$a}{'Depth'} <=> $VisibleSprites{$b}{'Depth'} } (keys %VisibleSprites);
	foreach my $name (@names) {
		my $x = $VisibleSprites{$name}{'X'};
		my $y = $VisibleSprites{$name}{'Y'};
		$VisibleSprites{$name}{'Sprite'}->draw_xy($app, $x, $y);
	}
}

sub Surface_Flip {
	my $src=shift(@_);
	my $dst = SDLx::Surface->new(	width	=> $src->width(),
					height	=> $src->height(),
					format	=> $src->format(),
					flags	=> $src->flags(),
					color	=> $src->get_pixel( 0, 0 ),
					);
	my $w = $src->width();

	for(my $y = 0; $y < $src->height(); $y++) {
		for(my $x = 0; $x < $w; $x++) {
			my $pixel = $src->get_pixel( $y, $x );
			my $c = SDL::Video::get_RGBA( $src->format(), $pixel );
			my $c2 = SDL::Video::map_RGBA( $dst->format(), @$c );
			$dst->set_pixel($y, $w-($x+1), $c2);
		}
	}

	return $dst;
	
}

# Per pixel, search for $from and change to $to
# Searches within @$rect if provided
sub Surface_ChangeColor {
	my($surface, $from, $to, $rect)=@_;
	my($start_x, $start_y, $w, $h)=(0,0,$surface->w(),$surface->h());
	if($rect && ref $rect) {
		$start_x = Constrain( $rect->[0], 0, $surface->w());
		$start_y = Constrain( $rect->[1], 0, $surface->h());
		$w = Constrain( $rect->[2], 0, $surface->w() - $start_x);
		$h = Constrain( $rect->[3], 0, $surface->h() - $start_y);
	}

	my $frompix = SDL::Video::map_RGBA( $surface->format(), @$from);
	my $topix = SDL::Video::map_RGBA( $surface->format(), @$to);

	for(my $y=$start_y; $y<$start_y + $h; $y++) {
		for(my $x=$start_x; $x<$start_x + $w; $x++) {
			if($surface->get_pixel( $y, $x ) == $frompix) {
				$surface->set_pixel( $y, $x, $topix );
			}
		}
	}
}

1;
