The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Tickit::Widget::Progressbar::Vertical;
$Tickit::Widget::Progressbar::Vertical::VERSION = '0.101';
use strict;
use warnings;
use parent qw(Tickit::Widget::Progressbar);

=head1 NAME

Tickit::Widget::Progressbar::Vertical - simple progressbar implementation for Tickit

=head1 VERSION

Version 0.101

=head1 SYNOPSIS

 my $bar = Tickit::Widget::Progressbar::Vertical->new(
 	completion	=> 0.00,
 );

=head1 DESCRIPTION

See L<Tickit::Widget::Progressbar>.

=cut

use POSIX qw(floor);
use List::Util qw(min);

# Undocumented feature for gradient support. Needs Tickit::Colour,
# since that's not on CPAN then there's little point in enabling this.
use constant ENABLE_GRADIENT => 0;

=head1 METHODS

=cut

sub render_to_rb {
	my $self = shift;
	return $self->render_gradient(@_) if ENABLE_GRADIENT && scalar $self->get_style_values('gradient');
	return $self->render_normal(@_);
}

sub render_gradient {
	my ($self, $rb, $rect) = @_;
	my $win = $self->window or return;

	$rb->clear;
	my $total_height = $win->lines;
	my $cols = $win->cols;
	my $chars = $self->chars;
	my $row = 0;

	my $complete = $self->completion * $total_height;
	my $h = floor($complete);

	my $start = $self->get_style_pen('start');
	my $src = $self->get_style_pen;
	my $dst = Tickit::Pen->new(
		fg => $src->getattr('bg'),
		bg => $src->getattr('fg')
	)->default_from($src);
	my $fg = $src;
	my $bg = $dst;
	while($row < ($total_height - $h)) {
		$rb->goto($row++, 0);
		if($self->direction) {
#			$bg = $self->pen_for_position($row, $total_height, bg => $src, $dst);
			$bg = $self->pen_for_position(
				pos => $total_height - $row,
				total => $total_height,
				from => 'fg',
				to => 'bg',
				start => $start,
				end => $src
			);
			$rb->erase($cols, $bg);
		} else {
#			$fg = $self->pen_for_position($row, $total_height, fg => $src, $dst);
			$rb->erase($cols, $fg);
		}
	}

	if(my $partial = ($complete - $h) * @$chars) {
		if($self->direction) {
			$fg = $self->pen_for_position(
				pos      => $total_height - $row,
				total    => $total_height,
				from     => 'fg',
				to       => 'bg',
				start    => $start,
				end      => $src,
				extra_fg => $dst->getattr('fg'),
			);
		} else {
			$fg = $self->pen_for_position(
				pos => $row,
				total => $total_height,
				from => 'fg',
				start => $start,
				end => $src
			);
		}
#		$bg = $self->pen_for_position($row, $total_height, bg => $src, $dst);
		$rb->char_at($row++, $_, $chars->[$partial], $fg) for 0..$cols - 1;
	}

	while($row <= $total_height) {
		$rb->goto($row, 0);
		if($self->direction) {
#			$fg = $self->pen_for_position($row, $total_height, fg => $src, $dst);
			$rb->erase($cols, $src);
		} else {
			$fg = $self->pen_for_position(
				pos => $row,
				total => $total_height,
				from => 'fg',
				to => 'bg',
				start => $start,
				end => $src
			);
#			$bg = $self->pen_for_position($row, $total_height, fg => $src, $dst);
#			$fg = $self->pen_for_position($row, $total_height, bg => $src, $start);
			$rb->erase($cols, $fg);
		}
		++$row;
	}
}

sub render_normal {
	my ($self, $rb, $rect) = @_;
	my $win = $self->window or return;

	$rb->clear;
	my $total_height = $win->lines;
	my $cols = $win->cols;
	my $chars = $self->chars;
	my $row = 0;

	my $complete = $self->completion * $total_height;
	my $h = floor($complete);

	my $fg = $self->get_style_pen;
	my $bg = Tickit::Pen->new(
		fg => $fg->getattr('bg'),
		bg => $fg->getattr('fg')
	)->default_from($fg);
	while($row < ($total_height - $h)) {
		$rb->goto($row++, 0);
		if($self->direction) {
			$rb->text(' ' x $cols, $bg);
		} else {
			$rb->erase($cols, $fg);
		}
	}

	if(my $partial = ($complete - $h) * @$chars) {
		$rb->char_at($row++, $_, $chars->[$partial], $self->direction ? $bg : $fg) for 0..$cols - 1;
	}

	while($row <= $total_height) {
		$rb->goto($row++, 0);
		if($self->direction) {
			$rb->erase($cols, $fg);
		} else {
			$rb->text(' ' x $cols, $bg);
		}
	}
}

=head2 chars

Returns a list of chars for the various styles we support.

Currently only handles 'ascii' and 'boxchar'.

TODO - this should probably be aligned with the naming
scheme used in other widgets?

=cut

sub chars {
	my $self = shift;
	return {
		ascii	=> [map ord, qw(_ x X)],
		boxchar	=> [
			0x2581,
			0x2582,
			0x2583,
			0x2584,
			0x2585,
			0x2586,
			0x2587,
			0x2588
		],
	}->{$self->style};
}

sub position_for {
	my $self = shift;
	return $self->window->lines - floor(shift() * $self->window->lines);
}

sub expose_between_values {
	my $self = shift;
	return $self unless $self->window;

	my ($prev, $next) = map $self->position_for($_), @_;
	$self->window->expose(
		Tickit::Rect->new(
			top  => min($prev, $next) - 1,
			left => 0,
			cols => $self->window->cols,
			lines => abs($next - $prev) + 1,
		)
	);
}

1;

__END__

=head1 SEE ALSO

=over 4

=item * L<Tickit::Widget::SparkLine>

=back

=head1 AUTHOR

Tom Molesworth <cpan@entitymodel.com>

=head1 LICENSE

Copyright Tom Molesworth 2011-2013. Licensed under the same terms as Perl itself.