package Lire::UI::ListWidget;

use strict;

use base qw/ Curses::UI::Container Lire::UI::Widget /;

use Carp;

use Curses;
use Curses::UI::Common;
use Locale::TextDomain 'lire';

use Lire::Utils qw/ check_object_param text_for_width /;
use Lire::UI::Utils qw/ layout_buttonbox /;
use Lire::UI::ScalarListWidget;
use Lire::UI::CompoundListWidget;
use Lire::UI::PolyListWidget;

use vars qw/@CARP_NOT/;

@CARP_NOT = qw/Curses::UI::Container/;

my %bindings = ( KEY_IC() => 'add-element',
                 "\cc" => "copy-element",
                 KEY_DC() => 'del-element',
                 "J" => 'move-element-down',
                 "K" => 'move-element-up',
               );
my %routines = ( 'add-element' => \&add_element,
                 'copy-element' => \&copy_element,
                 'move-element' => \&move_element,
                 'move-element-up' => \&move_element_up,
                 'move-element-down' => \&move_element_down,
                 'del-element' => \&del_element );
sub new {
    my $class = shift;

    my %userargs = @_;
    keys_to_lowercase(\%userargs);

    check_object_param( $userargs{'value'}, 'value', 'Lire::Config::List' );

    my $spec = $userargs{'value'}->spec();
    my @comp_names = $spec->component_names();
    my $component = @comp_names ? $spec->get( $comp_names[0] ) : undef;
    if ( $class eq __PACKAGE__) {
        croak "'" . $spec->name() . "' should contain at least one component"
          unless $component;

        if ( @comp_names > 1 ) {
            return new Lire::UI::PolyListWidget( @_ );
        } elsif ( $component->isa( 'Lire::Config::ScalarSpec' ) ) {
            return new Lire::UI::ScalarListWidget( @_ );
        } else {
            return new Lire::UI::CompoundListWidget( @_ );
        }
    }

    my %userlistargs = ();
    foreach my $p ( qw/-title -titlereverse -titlefullwidth / ) {
        if ( exists $userargs{$p} ) {
            $userlistargs{$p} = $userargs{$p};
            delete $userargs{$p};
        }
    }
    my $self = $class->Curses::UI::Container::new( %userargs,
                                                   'component' => $component,
                                                   '-bindings' => {%bindings},
                                                   '-routines' => {%routines},
                                                   '-releasefocus' => 1,
                                                 );

    my $list_widget = $self->add( 'list', 'Listbox',
                                  %userlistargs,
                                  '-onselchange' => \&_sel_change_cb,
                                  '-vscrollbar' => 1,
                                  '-border' => 1,
                                  '-selected' => 0 );
    $self->refresh_view();

    $self->add_contained_widgets();

    $self->layout();

    return $self;
}

sub extra_widget_height {
    return 0;
}

sub layout_contained_objects {
    my $self = $_[0];

    return $self unless $self->getobj( 'list' );

    my $list = $self->getobj( 'list' );
    $list->{'-width'} = $self->canvaswidth();
    $list->{'-height'} =
      $self->canvasheight() - $self->extra_widget_height() - 1;
    $list->{'-title'} = text_for_width( $self->title(),
                                        $self->canvaswidth() - 4 )
      if ( $self->title() );

    $self->layout_buttons();

    return $self->SUPER::layout_contained_objects();
}

sub layout_buttons {
    my $self = $_[0];

    my $buttons = $self->getobj( 'buttons' );
    $buttons->{'-buttons'}[0]{'-label'} = __( 'Add' );
    $buttons->{'-buttons'}[2]{'-label'} = __( 'Copy' );
    $buttons->{'-buttons'}[3]{'-label'} = __( 'Delete' );
    $buttons->{'-buttons'}[4]{'-label'} = __( 'Up' );
    $buttons->{'-buttons'}[5]{'-label'} = __( 'Down' );

    layout_buttonbox( $self, $buttons, 1, 1 );
    $buttons->{'-y'} = $self->canvasheight() - 1;

    return;
}

sub add_contained_widgets {
    my $self = $_[0];

    $self->add_buttons_widget();

    return;
}

sub add_buttons_widget {
    my $self = $_[0];

    $self->add( 'buttons', 'Buttonbox',
                '-releasefocus' => 1,
                '-buttons' =>  [ { '-label' => __( 'Add' ),
                                   '-onpress' => sub { $self->do_routine( 'add-element' ) } },
                                 { '-label' => __( 'Copy' ),
                                   '-onpress' => sub { $self->do_routine( 'copy-element' ) } },
                                 { '-label' => __( 'Delete' ),
                                   '-onpress' => sub { $self->do_routine( 'del-element' ) } },
                                 { '-label' => __( 'Up' ),
                                   '-onpress' => sub { $self->do_routine( 'move-element-up' ) } },
                                 { '-label' => __( 'Down' ),
                                   '-onpress' => sub { $self->do_routine( 'move-element-down' ) } },
                               ] );

    return;
}

sub title {
    return shift->getobj( 'list' )->title( @_ );
}

# Routines
sub add_element {
    my $self = $_[0];

    my $instance = $self->new_value();
    if ( $instance ) {
        my $list = $self->getobj( 'list' );
        $self->{'value'}->append( $instance, $list->{'-selected'} );
        $list->{'-ypos'} = ( defined $list->{'-selected'}
                             ? $list->{'-selected'} + 1 : 0 );
        $self->refresh_view();
        $self->run_event( 'onvaluechanged' );
    }

    return;
}

sub copy_element {
    my $self = $_[0];

    my $list = $self->getobj( 'list' );
    return unless defined $list->{'-selected'};

    my $instance = $self->{'value'}->get( $list->{'-selected'} )->clone();
    $self->{'value'}->append( $instance, $list->{'-selected'} );
    $list->{'-ypos'} = $list->{'-selected'} + 1;
    $self->refresh_view();
    $self->run_event( 'onvaluechanged' );

    return;
}

sub move_element_up {
    $_[0]->move_element( -1 );
    return;
}

sub move_element_down {
    $_[0]->move_element( 1 );
    return;
}

sub move_element {
    my ( $self, $offset ) = @_;

    my $list = $self->getobj( 'list' );
    return unless defined $list->{'-selected'};
    my $new_idx = $list->{'-selected'} + $offset;
    $new_idx = 0
      if $new_idx < 0;
    $new_idx = $#{$list->{'-values'}}
      if $new_idx > $#{$list->{'-values'}};

    return if $new_idx == $list->{'-selected'};
    $self->{'value'}->move( $list->{'-selected'}, $new_idx );

    $list->{'-ypos'} = $new_idx;
    $self->refresh_view();
    $self->run_event( 'onvaluechanged' );

    return;
}

sub refresh_view {
    my $self = $_[0];

    my $list_widget = $self->getobj( 'list' );
    my $values = [ $self->{'value'}->elements() ];
    my %labels = map { $_, $_->as_label() } $self->{'value'}->elements();
    if ( scalar @$values == 0 ) {
        $values = [ __( '-- empty list --' ) ];
        $list_widget->focusable( 0 );
        $list_widget->{'-selected'} = undef;
    } else {
        $list_widget->focusable( 1 );
        $list_widget->{'-selected'} = $list_widget->{'-ypos'};
    }

    $list_widget->{'-values'} = $values;
    $list_widget->{'-labels'} = \%labels;
    $list_widget->draw();

    return;
}

sub del_element {
    my $self = $_[0];

    my $list_widget = $self->getobj( 'list' );
    return unless $list_widget->focusable();

    $self->{'value'}->remove( $list_widget->{'-ypos'} );

    my $nbr = scalar $self->{'value'}->elements() - 1;
    if ( $list_widget->{'-selected'} > $nbr ) {
        $list_widget->{'-ypos'} = $nbr;
    }

    $self->refresh_view();
    $self->run_event( 'onvaluechanged' );

    return;
}

sub _sel_change_cb {
    my $list_widget = $_[0];

    $list_widget->{'-selected'} = $list_widget->{'-ypos'};
    $list_widget->run_event( '-onchange' );

    return;
}

1;
