package Lire::UI::StoreWindow;

use strict;

use base qw/ Curses::UI::Window /;

use Carp;
use Curses::UI::Common;
use Locale::TextDomain 'lire';
use Time::Local;

use Lire::Utils qw/ check_param check_object_param max text_for_width /;
use Lire::UI::Utils qw/ button_box_width /;

use POSIX qw/strftime/;

use vars qw/ @CARP_NOT /;

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

my %routines = ( 'configure-stream' => \&configure_stream,
                 'clean-stream' => \&clean_stream,
               );
sub new {
    my $class = shift;
    my %userargs = @_;
    keys_to_lowercase( \%userargs );

    check_object_param( $userargs{'store'}, 'store',
                        'Lire::DlfStore' );

    my $self = $class->SUPER::new( %userargs,
                                   '-titlereverse' => 0,
                                   '-border' => 1,
                                   '-routines' => \%routines,
                                   '-ipad' => 1,
                                   '-ipadbottom' => 0,
                                 );

    $self->add( 'import_jobs', 'Lire::UI::Widget',
                'value' => $self->{'store'}->config()->get( 'import_jobs' ),
                '-title' => __( 'Import Jobs' ),
                '-titlereverse' => 0 );

    $self->add( 'report_jobs', 'Lire::UI::Widget',
                'value' => $self->{'store'}->config()->get( 'report_jobs' ),
                '-title' => __( 'Report Jobs' ),
                '-titlereverse' => 0 );

    $self->getobj( 'import_jobs' )->onValueChanged( sub { $self->_update_streams(); $self->getobj( 'streams' )->intellidraw() } );

    $self->add( 'reports', 'Lire::UI::ReportConfigListWidget',
                'value' => $self->{'store'}->config()->get( 'reports' ),
                '-title' => __( 'Report Configurations' ),
                '-titlereverse' => 0 );

    $self->_add_stream_widgets();

    $self->layout();

    return $self;
}

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

    # Contained widgets weren't added yet.
    return $self unless $self->getobj( 'import_jobs' );

    # Window contains 4 quadrants
    my $win_w = $self->canvaswidth();
    my $w = int( ( $win_w - 1) / 2 );
    my $h = int( ($self->canvasheight() - 1) / 2 );

    my $store_label = __( 'Store: ' );
    $self->title( $store_label
                  . text_for_width( $self->{'store'}->path(),
                                    $win_w - ( 4 + length( $store_label ) ) ));

    my $import_jobs = $self->getobj( 'import_jobs' );
    $import_jobs->{'-width'} = $w;
    $import_jobs->{'-height'} = $h;

    my $report_jobs = $self->getobj( 'report_jobs' );
    $report_jobs->{'-x'} = $w + 1;
    $report_jobs->{'-width'} = $w;
    $report_jobs->{'-height'} = $h;

    my $reports = $self->getobj( 'reports' );
    $reports->{'-y'} = $h + 1;
    $reports->{'-width'} = $w;
    $reports->{'-height'} = $h;

    $self->_set_stream_widgets_coor( $h, $w );

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

sub _set_stream_widgets_coor {
    my ($self, $h, $w ) = @_;

    my $streams = $self->getobj( 'streams' );
    $streams->{'-width'} = $w - 1; # To align with ListWidget
    $streams->{'-height'} = $h - 4;
    $streams->{'-y'} = $h + 1;
    $streams->{'-x'} = $w + 1;

    my $y = $h + ( $h - 4 ) + 1;
    my $buttons = $self->getobj( 'streams_buttons' );
    $buttons->{'-x'} = $w + 2;
    $buttons->{'-y'} = $y++;

    $y++;
    my $max_label_w = max( $self->getobj( 'stream_start_label' )->width(),
                           $self->getobj( 'stream_end_label' )->width(),
                           $self->getobj( 'nrecords_label' )->width() );
    foreach my $name ( qw/stream_start stream_end nrecords/ )
    {
        my $label = $self->getobj( "${name}_label" );
        my $value = $self->getobj( "${name}_value" );
        $label->{'-x'} = $w + 2;
        $value->{'-x'} = $w + $max_label_w + 2;
        $label->{'-y'} = $value->{'-y'} = $y++;
    }

    return;
}

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

    my $streams = $self->add( 'streams', 'Listbox',
                              '-onchange' => \&_change_cb,
                              '-onselchange' => \&_sel_change_cb,
                              '-values' => [],
                              '-title' => __( 'DLF Streams' ),
                              '-titlereverse' => 0,
                              '-selected' => undef,
                              '-ypos' => undef,
                              '-vscrollbar' => 1,
                              '-focusable' => 0,
                              '-border' => 1,
                            );
    $streams->set_binding( sub { $self->do_routine( 'configure-stream' ) },
                           Curses::KEY_ENTER );
    $streams->set_binding( sub { $self->do_routine( 'clean-stream' ) },
                           Curses::KEY_DC, Curses::KEY_BACKSPACE );

    $self->add( 'stream_start_label', 'Label',
                '-text' => __( 'Starts:' ) );
    $self->add( 'stream_end_label', 'Label',
                '-text' => __( 'Ends:' ) );
    $self->add( 'nrecords_label', 'Label',
                '-text' => __( 'Records:' ) );

    $self->add( 'stream_start_value', 'Label' );
    $self->add( 'stream_end_value', 'Label' );
    $self->add( 'nrecords_value', 'Label' );

    $self->add( 'streams_buttons', 'Buttonbox',
                '-buttons' => [ { '-label' => __( '[Configure]' ),
                                  '-onpress' => sub { $self->do_routine( 'configure-stream' ) } },
                                { '-label' => __( '[Clean]' ),
                                  '-onpress' => sub { $self->do_routine( 'clean-stream' ) } } ],
              );

    $self->_update_streams();

    return;
}

sub _clean_ok_cb_helper {
    my ( $self, $time_field ) = @_;

    my $dialog = $self->getobj( 'stream_clean' );
    my $streams = $self->getobj( 'streams' );
    my $stream_name = $streams->get();
    my $stream = $self->{'store'}->open_dlf_stream( $stream_name, 'w' );
    my $time = _date_to_epoch( $time_field->text() );
    $stream->clean( $time );
    $stream->close();
    $self->_update_labels();

    return;
}

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

    my $streams = $self->getobj( 'streams' );
    return unless $streams->{'-focusable'};

    my $title = __x( "Cleaning stream '{stream}'",
                     'stream' => $streams->get() );
    my $dialog = $self->add( 'clean_stream_win', 'Window',
                             '-title' => $title, '-ipad' => 1,
                             '-x' => 7, '-y' => 8,
                             '-height' => 9, '-width' => 60,
                             '-border' => 1 );
    $dialog->set_binding( sub { $dialog->loose_focus() }, CUI_ESCAPE );

    $dialog->add( 'question', 'Label',
                  '-text' => __( 'Enter the cleaning date in the form "YYYY-MM-DD":' ),
                  '-x' => 0, '-y' => 0 );
    my $time_field = $dialog->add( 'time_field', 'TextEntry',
                                   '-regexp' => '/^[0-9-]*$/',
                                   '-sbborder' => 1, '-x' => 20,
                                   '-y' => 2, '-width' => 14 );

    my $buttons = [ { '-label' => __( '< Cancel >' ),
                      '-onpress' => sub {
                          $dialog->loose_focus()
                      } },
                    { '-label' => __( '< OK >' ),
                      '-onpress' => sub {
                          if ( _test_date ( $time_field->text() ) ) {
                              $self->_clean_ok_cb_helper( $time_field );
                              $dialog->loose_focus();
                          }
                      } } ];
    my $bb_width = button_box_width( $buttons );
    $dialog->add( 'buttons', 'Buttonbox',
                  '-buttons' => $buttons,
                  '-x' => -20, '-y' => 4,
                  '-height' => 1, '-width' => $bb_width );

    $dialog->modalfocus();
    $self->delete( 'clean_stream_win' );
    $self->draw( 1 );

    return;
}

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

    my $streams = $self->getobj( 'streams' );
    return unless $streams->{'-focusable'};

    my $dialog = $self->root->add( 'configure_stream_win', 'Window',
                                   '-title' => __x( "Configuring stream '{stream}'",
                                                    'stream' => $streams->get() ),
                                   '-ipadleft' => 1, '-ipadright' => 1,
                                   '-ipadtop' => 1, '-pad' => 2,
                                   '-border' => 1 );
    $dialog->set_binding( sub { $dialog->loose_focus() }, CUI_ESCAPE );

    my $config_value = $self->{'store'}->get_stream_config( $streams->get() )->clone();
    $dialog->add( 'stream_config', 'Lire::UI::Widget',
                  'value' => $config_value,
                  '-releasefocus' => 1,
                  '-ipadbottom' => 2 );

    my $buttons = [ { '-label' => __( '< Cancel >' ),
                      '-onpress' => sub { $dialog->loose_focus() } },
                    { '-label' => __( '< OK >' ),
                      '-onpress' => sub {
                          $self->_configure_ok_cb_helper( $config_value );
                          $dialog->loose_focus();
                      } } ];
    my $bb_width = button_box_width( $buttons );
    $dialog->add( 'buttons', 'Buttonbox',
                  '-buttons' => $buttons,
                  '-x' => -$bb_width, '-y' => -1,
                  '-width' => $bb_width );

    $dialog->modalfocus();
    $self->root()->delete( 'configure_stream_win' );
    $self->draw( 1 );

    return;
}

sub _configure_ok_cb_helper {
    my ( $self, $new_config_value ) = @_;

     my $old = $self->{'store'}->get_stream_config( $self->getobj( 'streams' )->get() );
    # Since we do not have access to the stream config list container, we
    # copy the content.
    foreach my $name ( $old->spec()->component_names() ) {
        $old->set( $new_config_value->get( $name ) );
     }

    $self->_update_streams();

    return;
}

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

    my @streams = $self->{'store'}->configured_dlf_streams();
    my $values;
    my $selected;
    my $focusable;

    if ( @streams ) {
        $values = \@streams;
        $selected = 0;
        $focusable = 1;
    } else {
        $values = [ __( '-- no stream --' ) ];
        $selected = undef;
        $focusable = 0;
    }

    my $stream_widget = $self->getobj( 'streams' );
    $stream_widget->{'-values'}= $values;
    $stream_widget->{'-selected'} = $selected;
    $stream_widget->{'-ypos'} = 0;
    $stream_widget->focusable( $focusable );

    $self->_update_labels();

    return;
}

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

    my $stream_name = $self->getobj( 'streams' )->get();

    my $start_value;
    my $end_value;
    my $nrecords;
    if ( defined $stream_name 
         && $self->{'store'}->has_dlf_stream( $stream_name ) )
    {
        my $stream = $self->{'store'}->open_dlf_stream( $stream_name );
        $start_value = _epoch_to_date( $stream->start_time() );
        $end_value = _epoch_to_date( $stream->end_time() );
        $nrecords = $stream->nrecords();
        $stream->close();
    } else {
        $start_value = '';
        $end_value = '';
        $nrecords = defined $stream_name ? 0 : '';
    }

    $self->getobj( 'stream_start_value' )->text( $start_value );
    $self->getobj( 'stream_end_value' )->text( $end_value );
    $self->getobj( 'nrecords_value' )->text( $nrecords );

    return;
}

sub store {
    return $_[0]->{'store'};
}

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

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

    return;
}

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

    croak "'-selected' can never be undefined"
      unless defined $list_widget->{'-selected'};

    my $self = $list_widget->parent();
    $self->_update_labels();

    return;
}

# helper functions
sub _test_date {
    my $text = $_[0];

    return 0
      unless ( $text =~ m/^(\d{2}|\d{4})-(\d{2})-(\d{2})$/ );
    my $month = $2;
    my $day = $3;
    return 0
      if ( $month < 1 || $month > 12 );
    return 0
      if ( $day < 1 || $day > 31 );

    return 1;
}

sub _date_to_epoch {
    my $date = $_[0];

    check_param( $date, 'date', \&_test_date,
                 "wrong format for 'date' (yyyy-mm-dd)" );

    $date =~ m/^(\d{2}|\d{4})-(\d{2})-(\d{2})$/;
    my $year = $1;
    my $month = $2 - 1;
    my $day = $3;

    return timelocal( 0, 0, 0, $day, $month, $year );
}

sub _epoch_to_date {
    my $epoch = $_[0];

    return __( '-- no date --' )
      unless $epoch;
    check_param( $epoch, 'epoch', qr/^\d+$/,
                 "'epoch' should be a positive integer" );

    return strftime( '%Y-%m-%d %H:%S', localtime $epoch );
}

1;
