package EnsEMBL::Web::ViewConfig;

use strict;
use Data::Dumper;
use EnsEMBL::Web::Form;
use CGI qw(escape unescape escapeHTML);

sub new {
  my($class,$type,$action,$adaptor) = @_;

  my $self = {
    '_db'                 => $adaptor->get_adaptor,
    '_species'            => $adaptor->get_species,
    '_species_defs'       => $adaptor->get_species_defs,
    '_r'                  => $adaptor->get_request || undef,
    'type'                => $type,
    'real'                => 0,
    'action'              => $action,
    'title'               => undef,
    '_classes'            => [],
    '_options'            => {},
    '_image_config_names' => {},
    '_default_config'     => '_page',
    '_can_upload'         => 0,
    '_form'               => undef,
    '_url'                => undef,
    'no_load'             => undef,
  };

  bless($self, $class);
  return $self;
}

sub default_config :lvalue {
### a
  $_[0]->{_default_config};
}

sub real :lvalue {
### a
  $_[0]->{'real'};
}

sub species :lvalue {
### a
  $_[0]->{'_species'};
}

sub species_defs :lvalue {
### a
  $_[0]->{'_species_defs'};
}

sub url :lvalue {
### a
  $_[0]->{'_url'};
}
sub title :lvalue {
### a
  $_[0]->{'title'};
}
sub storable :lvalue {
### a
### Set whether this ViewConfig is changeable by the User, and hence needs to
### access the database to set storable do $view_config->storable = 1; in SC code...
  $_[0]->{'storable'};
}

sub altered :lvalue {
### a
### Set to one if the configuration has been updated...
  $_[0]->{'altered'};
}

sub add_image_configs { ## Value indidates that the track can be configured for DAS (das) or not (nodas)
  my( $self, $hash_ref ) = @_;
  foreach( keys %$hash_ref ) {
    $self->{_image_config_names}{$_} = $hash_ref->{$_};
    $self->can_upload = 1 if $hash_ref->{$_} eq 'das';
    $self->has_images(1) if # $hash_ref->{$_} ne 'das' && 
                              $hash_ref->{$_} !~ /^V/
  }
}

sub can_upload :lvalue {
  $_[0]->{'_can_upload'}
}
sub has_image_config {
  my $self = shift;
  my $config = shift;
  return exists $self->{_image_config_names}{$config};
}
sub has_image_configs {
  my $self = shift;
  return keys %{$self->{_image_config_names}||{}};
}

sub image_configs {
  my $self = shift;
  return %{$self->{_image_config_names}||{}};
}

sub _set_defaults {
  my $self = shift;
  my %defs = @_;# map( { ("format_$_", 'off')} qw(svg postscript pdf) ) );

  foreach my $key (keys %defs) {
    $self->{_options}{$key}{'default'} = $defs{$key};
  }
}

sub _clear_defaults {
  my $self = shift;
  $self->{_options} = {};
}

sub _remove_defaults {
### Clears the listed default values...
  my $self = shift;
  foreach my $key (@_) {
    delete $self->{_options}{$key};
  }
}

sub options { 
  my $self = shift;
  return keys %{$self->{'_options'}};
}

sub has_form {
  my $self = shift;
  return $self->{_form};
}

sub get_form {
  my $self = shift;
  $self->{_form}||=EnsEMBL::Web::Form->new( 'configuration', $self->url,'post' );
  return $self->{_form};
}

sub add_fieldset {
  my( $self, $legend, $layout ) = @_;
  my $fieldset = $self->get_form->add_fieldset('form'=>'configuration', 'layout' => $layout );
  $fieldset->legend($legend);
}

sub add_form_element {
  my($self,$hashref) = @_;
  my @extra;
  my $value = $self->get($hashref->{'name'});
  if( $hashref->{'type'} =~ /CheckBox/ ) {
    push @extra, 'checked' => $value eq $hashref->{'value'} ? 1 : 0;
  } elsif( !exists $hashref->{'value'} ) {
    push @extra, 'value' => $value;
  }
  $self->get_form->add_element(%$hashref,@extra);
}

sub update_config_from_parameter {
### Update the configuration from a pipe separated string...
  my( $self, $string ) = @_;
  my @array = split /\|/, $string;
  return unless @array;
  foreach( @array ) {
    next unless $_;
    my( $key, $value ) = split ':';
    $self->set( $key, $value );
  }
}

sub update_from_input {
### Loop through the parameters and update the config based on the parameters passed!
  my( $self, $input ) = @_;
  my $flag = 0;
  if( $input->param('reset') ) {
    return $self->reset;
  }
  foreach my $key ( $self->options ) {
    if( defined $input->param($key) && $input->param( $key ) ne $self->{'_options'}{$key}{'user'} ) {
      $flag = 1;
      my @values = $input->param( $key );
      if( scalar(@values) > 1 ) {
        $self->set( $key, \@values );
      } else {
        $self->set( $key, $input->param( $key ) );
      }
    }
  }
  if( $flag ) {
    $self->altered = 1;
  }
  return;
}

sub update_from_config_strings {
### Loop through the parameters and update the config based on the parameters passed!
  my( $self, $session, $r ) = @_;
  my $input = $session->input;
  my $flag = 0;
  my $params_removed;
  if( $input->param('config') ) {
    foreach my $v ( split /,/, $input->param('config') ) {
      my( $k,$t ) = split /=/, $v,2;
      $self->set($k,$t);
    }
    $session->add_data(
      'type'     => 'message',
      'function' => '_info',
      'code'     => 'configuration',
      'message'  => $self->altered
                  ? 'Your configuration has changed for this page'
                  : 'The link you followed requested a change to the configuration of this page but no change occured'
                  ,
    );
    $params_removed = 1;
    $input->delete('config');
  }

  my $flag = 0;
  foreach my $name ( $self->image_configs ) {
    my $string = $input->param($name);
    my @values = split /,/, $input->param( $name );
    if(@values) {
      $input->delete($name); 
      $params_removed = 1;
    }
    if( ($name eq 'contigviewbottom'|| $name eq 'cytoview' ) ) {
      foreach my $v ( $input->param('data_URL') ) {
        push @values, "url:".escape($v).'=normal';
        $params_removed = 1; 
      }
      $input->delete('data_URL');
      foreach my $v ( $input->param('add_das_source') ) {
        my $server = $v =~ /url=(https?:[^ +]+)/  ? $1 : '';
        my $dsn    = $v =~ /dsn=(\w+)/       ? $1 : '';
# warn "$v > $server/$dsn";
        push @values, 'das:'.escape("$server/$dsn").'=labels' if $r;
        $params_removed = 1;
      }
      $input->delete('add_das_source');
    }

    if( @values ) {
      my $ic = $session->getImageConfig( $name,$name );
      next unless $ic;
      foreach my $v ( @values ) {
        my( $key,$render ) = split /=/,$v,2;
# warn ">> $v >> $key -- $render";
## Now we have to get the image_config... and modify it...
        if( $key =~ /^(\w+)[\.:](.*)$/ ) {
          my( $type, $p ) = ($1,$2);
# warn ".. $type - $p ..";
          if( $type eq 'url' ) {
            $p = unescape( $p );
## We have to create a URL upload entry in the session...
            use Digest::MD5 qw(md5_hex);
            my $code = md5_hex($ENV{'ENSEMBL_SPECIES'}.":".$p);
            my $n    =  $p =~ /\/([^\/]+)\/*$/ ? $1 : 'un-named';
            $session->set_data(
              'type'    => 'url',
              'url'     => $p,
              'species' => $ENV{'ENSEMBL_SPECIES'},
              'code'    => $code, 
              'name'    => $n
            );
            $session->add_data(
              'type'      => 'message',
              'function'  => '_info',
              'code'      => 'url_data:'.md5_hex($p),
              'message'   => sprintf( '
  Data has been attached to your display from the following URL: %s',
  escapeHTML( $p )
              )
            );
## We then have to create a node in the user_config...
            $ic->_add_flat_file_track( undef, 'url', "url_$code", $n, 
              sprintf ( '
  Data retrieved from an external webserver.
  This data is attached to the %s, and comes from URL: %s', CGI::escapeHTML( $n ), CGI::escapeHTML( $p ) ),
              'url' => $p
            );
            my $nd = $ic->get_node( "url_$code" );
## Then we have to set the renderer...
            $flag += $nd->set_user( 'display', $render ) if $nd;
          } elsif( $type eq 'das' ) {
            $p = unescape($p);
            if (my $error = $session->add_das_from_string( $p, {'ENSEMBL_IMAGE'=>$name}, {'display'=>$render} )) {
              $session->add_data(
                'type'      => 'message',
                'function'  => '_warning',
                'code'      => 'das:'.md5_hex($p),
                'message'   => sprintf( '
  You attempted to attach a DAS source with DSN: %s, unfortunately we were unable to attach this source (%s)',
  escapeHTML( $p ), escapeHTML( $error )
                )
              );
              warn $error;
            } else {
             $session->add_data(
                'type'      => 'message',
                'function'  => '_info',
                'code'      => 'das:'.md5_hex($p),
                'message'   => sprintf( '
  You have attached a DAS source with DSN: %s to this display',
  escapeHTML( $p )
                )
              );

              $flag ++;
            }
          }
        } else {
          my $nd = $ic->get_node($key);
          $flag += $nd->set_user( 'display', $render ) if $nd;
        }
      }
    }
  }
  if( $flag ) {
    $session->add_data(
      'type'      => 'message',
      'function'  => '_info',
      'code'      => 'image_config',
      'message'   => '
        The link you visited has made changes to the tracks displayed on this page'
    );
  }
  $self->altered = 1 if $flag;
  $session->store;

  return $params_removed ? $input->self_url : undef;
}

sub delete {
### Delete a key from the user settings
  my($self, $key ) = @_;
  return unless exists $self->{'_options'}{$key}{'user'};
  $self->altered = 1;
  delete $self->{'_options'}{$key}{'user'};
  return;
}

sub reset {
### Delete all keys from user settings
  my ($self) = @_;
  foreach my $key ( $self->options ) {
    next unless exists $self->{'_options'}{$key}{'user'};
    $self->altered = 1;
    delete $self->{'_options'}{$key}{'user'};
  }
  return;
}

sub push_class {
  my($self, $class) =@_;
  push @{$self->{'_classes'}}, $class;
}

sub has_images {
  my $self = shift;
  $self->{_has_images} = shift if @_;
  return $self->{_has_images};
}

sub form {
  my( $self, $object, $no_extra_bits ) = @_;
  foreach my $classname (@{$self->{'_classes'}}) {
    my $method = $classname.'::form';
    eval { no strict 'refs'; &$method( $self, $object ); };
    ## TODO: proper error exception
    warn $@ if $@;
  }
  return if $no_extra_bits;
  if( $self->has_images ) {
#       $ENV{'ENSEMBL_AJAX_VALUE'} =~ /^(en|dis)abled$/ && $self->has_form ) {
    $self->add_fieldset( 'Image width configurations' );
      $self->add_form_element({
        'type'     => 'DropDown', 'select' => 'select',
        'required' => 'yes',      'name'   => 'cookie_width',
        'values'   => [
          { 'value' => 'bestfit', 'name' => 'best fit' },
          map { { 'value' => $_, 'name' => "$_ pixels" } } map {$_*100} (5..20)
        ],
        'value'    => $ENV{'ENSEMBL_IMAGE_WIDTH'},
        'label'    => "Width of image",
      });
    }
#    if( $ENV{'ENSEMBL_AJAX_VALUE'} =~ /^(en|dis)abled$/ ) {
#      $self->add_form_element({
#        'type'     => 'DropDown', 'select' => 'select',
#        'required' => 'yes',      'name'   => 'cookie_ajax',
#        'values'   => [
#          { 'value' => 'enabled',  'name' => 'Enabled' },
#          { 'value' => 'disabled', 'name' => 'Disabled' },
#        ],
#        'value'    => $ENV{'ENSEMBL_AJAX_VALUE'},
#        'label'    => "Enable/disable use of AJAX in rendering"
#      });
#    }
  $self->add_form_element({
   'type' => 'Submit', 'value' => 'Update configuration'
  }) if $self->has_form;
}

sub set {
### Set a key for user settings
  my( $self, $key, $value, $force ) = @_;
  return unless $force || exists $self->{'_options'}{$key};
  return if $self->{'_options'}{$key}{'user'}  eq $value;
  $self->altered = 1;
  $self->{'_options'}{$key}{'user'}  = $value;
}
#sub set {
#  my( $self, $key, $value, $force ) = @_;
#  return unless $force || exists $self->{'_options'}{$key};
#  $self->{'_options'}{$key}{'user'}  = $value;
#}

sub get {
  my( $self, $key ) = @_;
  return undef unless exists $self->{'_options'}{$key};
  if( exists ($self->{'_options'}{$key}{'user'}) ) {
    if( ref($self->{'_options'}{$key}{'user'}) eq 'ARRAY' ) {
      return @{$self->{'_options'}->{$key}->{'user'}};
    }
    return $self->{'_options'}{$key}{'user'};
  }
  if( ref($self->{'_options'}{$key}{'default'}) eq 'ARRAY' ) {
    return @{$self->{'_options'}{$key}{'default'}};
  }
  return $self->{'_options'}{$key}{'default'};
}

sub is_option {
  my( $self, $key ) = @_;
  return exists $self->{'_options'}{$key};
}

sub set_user_settings {
### Set the user settings from a hash of key value pairs
  my( $self, $diffs ) = @_;
  if( $diffs ) {
    foreach my $key ( keys %$diffs ) {
      $self->{'_options'}{$key}{'user'} = $diffs->{$key};
    }
  }
}

sub get_user_settings {
  my $self = shift;
  my $diffs = {};
  foreach my $key ( $self->options ) {
    $diffs->{$key} = $self->{'_options'}{$key}{'user'} if exists($self->{'_options'}{$key}{'user'}) && $self->{'_options'}{$key}{'user'} ne $self->{'_options'}{$key}{'default'};
  }
  return $diffs;
}

sub load {
  my ($self) = @_;
  return;
}

sub save {
  my ($self) = @_;
  return;
}

sub dump {
  my ($self) = @_;
  local $Data::Dumper::Indent = 1;
  local $Data::Dumper::Terse  = 1;
  print STDERR Dumper($self)," ";;
}

sub _species_label {
  my( $self, $key ) = @_;
  return $self->species_defs->species_label( $key );
}
sub species_label {
  my( $self, $key ) = @_;
  return $self->species_defs->species_label( $key );
}
1;