package EnsEMBL::Web::Document::Image;

use strict;

use EnsEMBL::Web::TmpFile::Image;
use POSIX qw(floor ceil);
use Bio::EnsEMBL::DrawableContainer;
use Bio::EnsEMBL::VDrawableContainer;

sub new {
  my( $class, $species_defs, $panel_name ) = @_;
  my $self = {
    'panel'              => $panel_name,
    'species_defs'       => $species_defs,
    'drawable_container' => undef,
    'menu_container'     => undef,
    'imagemap'           => 'no',
    'image_type'         => 'image',
    'image_name'         => undef,

    # Deprecated
    # 'cacheable'          => 'no',

    'introduction'       => undef,
    'tailnote'           => undef,
    'caption'            => undef,
    'button_title'       => undef,
    'button_name'        => undef,
    'button_id'          => undef,
    'image_id'           => undef,
    'format'             => 'png',
    'prefix'             => 'p',

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

sub prefix {
  ### a
  my ($self, $value) = @_;
  if ($value) {
    $self->{'prefix'} = $value;
  }
  return $self->{'prefix'};
}
sub set_extra {
  my( $self, $object ) = @_;
}

#----------------------------------------------------------------------------
# FUNCTIONS FOR CONFIGURING AND CREATING KARYOTYPE IMAGES
#----------------------------------------------------------------------------
sub karyotype {
  my( $self, $object, $highs, $config_name ) = @_;
  my @highlights = ref($highs) eq 'ARRAY' ? @$highs : ($highs);
  $config_name ||= 'Vkaryotype';
  my $chr_name;

  my $image_config = $object->image_config_hash( $config_name );
  my $view_config   = $object->get_viewconfig;

  # set some dimensions based on number and size of chromosomes
  if( $image_config->get_parameter('all_chromosomes') eq 'yes' ) {
    $chr_name = 'ALL';
    my $total_chrs = @{$object->species_defs->ENSEMBL_CHROMOSOMES};
    my $rows;
    if ($view_config) {
      $rows = $view_config->get('rows');
      my $chr_length = $view_config->get('chr_length') || 200;
      my $total_length = $chr_length + 25;
      $image_config->set_parameters({
        'image_height'  => $chr_length,
        'image_width'   => $total_length,
      });
    }
    $rows = ceil($total_chrs / 18 ) unless $rows;
    $image_config->set_parameters({ 
      'container_width' => $object->species_defs->MAX_CHR_LENGTH,
      'rows'            => $rows,
      'slice_number'  => '0|1',
    });
  }
  else {
    $chr_name = $object->seq_region_name;
    $image_config->set_parameters({
      'container_width' => $object->seq_region_length,
      'slice_number'    => '0|1'
    });
    $image_config->{'_rows'} = 1;
  }

  if ($object->param('aggregate_colour')) {
    $image_config->{'_aggregate_colour'} = $object->param('aggregate_colour');
  }
  # get some adaptors for chromosome data
  my( $sa, $ka, $da);
  my $species = $object->param('species') || undef;
  eval {
    $sa = $object->database('core', $species)->get_SliceAdaptor,
    $ka = $object->database('core', $species)->get_KaryotypeBandAdaptor,
    $da = $object->database('core', $species)->get_DensityFeatureAdaptor
  };
  return $@ if $@;

  # create the container object and add it to the image
  $self->drawable_container = new Bio::EnsEMBL::VDrawableContainer(
    { 'sa'=>$sa, 'ka'=>$ka, 'da'=>$da, 'chr'=>$chr_name }, $image_config, \@highlights
  );
  return undef; ## successful...
}
sub add_tracks {
### TODO - MOVE THIS TO IMAGE CONFIG?                                                                            
  my ($self, $object, $config_name, $parser, $track_id) = @_;
  my $config   = $object->image_config_hash( $config_name );
  # SELECT APPROPRIATE FEATURE SET(S)
  my $data;
  if ($parser) { # we have use data
    # CREATE TRACKS
    my $track_data;
    my $pos = 10000;
    my $max_values = $parser->max_values();

    ## get basic configuration
    my ($colour, $track_name); 
    my $manager = 'Vbinned';
    if ($track_id) {
      $track_name = $object->param("track_name_$track_id");
      $colour = $object->param("col_$track_id");
      my $style = $object->param("style_$track_id");
      $manager .= '_'.$style unless $style eq 'line';
    }
    else {
      $track_name = "track_$track_id";
      $colour = $object->param('col') || 'purple';
    }

    if ($object->param("merge_$track_id")) { ## put all data in one track
      my %all_features;
      my $max;
      push @{$config->{'general'}{$config_name}{'_artefacts'}}, "$track_name";
      my @types = $parser->feature_types;
      my $bins = $parser->no_of_bins;
      foreach my $type (@types) {
        my %features = %{$parser->features_of_type($type)};
        foreach my $chr (keys %features) {
          my @results = @{$features{$chr}};
          for (my $i=0; $i < $bins; $i++) {
            my $score = $results[$i];
            $all_features{$chr}[$i] += $score;
          }
        }
        my $current_max = $max_values->{$type};
        $max = $current_max if $max < $current_max;
      }
      $config->{'general'}{$config_name}{$track_name} = 
        {
          'on'            => 'on',
          'pos'           => ++$pos,
          'width'         => 50,
          'col'           => $colour,
          'manager'       => $manager,
          'label'         => $track_name,
          'bins'          => $parser->no_of_bins,
          'max_value'     => $max,
          'data'          => \%all_features,
          'maxmin'        => $object->param('maxmin'),
          };
      $config->{'_group_size'} = 2;
    }
    else {
      my $track_count = 0;
      foreach my $track ( $parser->feature_types ) {
        push @{$config->{'general'}{$config_name}{'_artefacts'}}, "track_$track";
        $config->{'general'}{$config_name}{"track_$track"} = 
        {
          'on'            => 'on',
          'pos'           => ++$pos,
          'width'         => 50,
          'col'           => $colour,
          'manager'       => $manager,
          'label'         => $track,
          'bins'          => $parser->no_of_bins,
          'max_value'     => $max_values->{$track},
          'data'          => $parser->features_of_type( $track ),
          'maxmin'        => $object->param('maxmin'),
          };
        $track_count++;
      }
      $config->{'_group_size'} = 1 + $parser->feature_types();
    }

    # add selected standard tracks
    my @params = $object->param();
    my $box_value;
    foreach my $param (@params) {
      if ($param =~ /^track_/ && !($param =~ /^track_name/)) {
       $config->{'_group_size'}++;
        if ($object->param($param) ne 'on') {
          $box_value = 'off';
        }
        else {
          $box_value = 'on';
        }
        $param =~ s/^track_//;
        $config->set($param, 'on', $box_value);
      }
    } 
  } 
  else { # display standard tracks
    my %features = map { ($_->analysis->logic_name() , 1) } @{
        $object->database('core')->get_DensityTypeAdaptor->fetch_all
    };
    foreach my $art ( $config->artefacts() ) {
	  my @logicnames;
	  if (ref($config->get( $art, 'logicname' )) eq 'ARRAY') {
		  my $array_ref = $config->get( $art, 'logicname' );
		  @logicnames = join(' ',@$array_ref);
	  }
	  else {
		@logicnames = ( split /\s+/,
                          $config->get( $art, 'logicname' ) );
	  }
      my @good_lnames = grep{$features{$_}} @logicnames;
      if( @logicnames  ) {
        if( @good_lnames ) {
          $config->set( $art, 'on', 'on' );
          $config->set( $art, 'logicname', join( " ", @good_lnames ) );
        } else {
          $config->set( $art, 'on', 'off' );
        }
      } elsif( $config->is_available_artefact( $art ) ) {
        $config->set( $art, 'on', 'on' );
      }
    }
  }
  return 1;
}
sub add_pointers {
  my ($self, $object, $extra) = @_;
  my $config_name = $extra->{'config_name'};
  my $config   = $object->image_config_hash($config_name);

  # CREATE DATA ARRAY FROM APPROPRIATE FEATURE SET
  my ($data, @data, $max_label, %chrs);
  my $parser = $extra->{'parser'};

  #warn           "Parser:     $parser";
  if ($parser) { # use parsed userdata
    my $max_label = 0;
    foreach my $track ($parser->{'tracks'}) {
      #warn         "  Track:    $track ",keys %$track;
      foreach my $type (keys %{$track}) {
        #warn       "    Type:   $type";
        my @features = $parser->fetch_features_by_tracktype($type);
        foreach my $row (@features) {
          #warn     "      Row:  $row";
          my @array = @$row;
          foreach my $feature (@array) {
            #warn   "        F:  $feature";
            my $data_row = {
                           'chr'   => $feature->seqname(),
                           'start' => $feature->rawstart(),
                           'end'   => $feature->rawend(),
                           'label' => $feature->id(),
                           'gene_id' => $feature->id(),
                            };
            push (@data, $data_row);
            $chrs{$feature->seqname()}++;                                         
            # track max label length for use with 'text' option
            my $label_len = CORE::length($feature->id());
            if ($label_len > $max_label) {
              $max_label = $label_len;
            }
          }
        }
      }
    }
  }
  else { # get features for this object
    $data = $extra->{'features'};
    foreach my $row (
      map { $_->[0] }
      sort { $a->[1] <=> $b->[1] || $a->[2] cmp $b->[2] || $a->[3] <=> $b->[3] }
      map { [$_, $_->{'region'} =~ /^(\d+)/ ? $1 : 1e20 , $_->{'region'},
$_->{'start'}] }
      @$data
      ) {
      my $data_row = {
          'chr'       => $row->{'region'},
          'start'     => $row->{'start'},
          'end'       => $row->{'end'},
          'length'    => $row->{'length'},
          'label'     => $row->{'label'},
          'gene_id'   => $row->{'gene_id'},
        };
      push (@data, $data_row);
      $chrs{$row->{'region'}}++;                                         
    }
  }
  # CONFIGURE POINTERS
  # set sensible defaults
  my $zmenu   = lc($object->param('zmenu'))    || 'on';
  my $zmenu_config;
  my $color   = lc($object->param('col'))  || lc($extra->{'color'}) || 'red';
  # set style before doing chromosome layout, as layout may need 
  # tweaking for some pointer styles
  my $style   = lc($object->param('style')) || lc($extra->{'style'}) || 'rharrow'; 

  # CREATE POINTERS ('highlights')
  my %high = ('style' => $style);
  my $species = $object->species;
  foreach my $row ( @data ) {
    my $chr = $row->{'chr'};
    my $point = {
            'start' => $row->{'start'},
            'end'   => $row->{'end'},
            'id'    => $row->{'label'},
            'col'   => $color,
            };
    if ($zmenu eq 'on') {
      $zmenu_config = $extra->{'zmenu_config'};
      $point->{'zmenu'} = {
                'caption'   => $zmenu_config->{'caption'},
           };
      my $order = 0;
      foreach my $entry (@{$zmenu_config->{'entries'}}) {
        my ($text, $key, $value);
        if ($entry eq 'contigview') {
          $text = "Jump to $entry";
          $value = sprintf("/$species/contigview?c=%s:%d;w=%d", $row->{'chr'}, int(($row->{'start'}+$row->{'end'})/2), $row->{'length'}+1000);
          # AffyProbe name(s) in URL turn on tracks in contigview
          if ($object->param('type') eq 'AffyProbe') {
            my @affy_list = split(';', $row->{'label'});
            foreach my $affy (@affy_list) {
              my @affy_bits = split (':', $affy);
              my $affy_id = $affy_bits[0];
              $affy_id =~ s/\s//g;
              $affy_id = lc($affy_id);
              $affy_id =~ s/\W/_/g;
              $affy_id =~ s/Plus_/+/i;
              $value .= ';'.$affy_id.'=on';
            }
          }
          $key = sprintf('%03d', $order).':'.$text;
          $point->{'zmenu'}->{$key} = $value;
          $order++;
        }
        elsif ($entry eq 'geneview') {
          foreach my $gene (@{$row->{'gene_id'}}) {
            if (scalar(@{$row->{'gene_id'}}) > 1) {
              $text = "$entry: $gene";
            }
            else {
              $text = "Jump to $entry";
            }
            $value = "/$species/geneview?gene=$gene";
            $key = sprintf('%03d', $order).':'.$text;
            $point->{'zmenu'}->{$key} = $value;
            $order++;
          }
        }
        elsif ($entry eq 'label') {
          $text = length( $row->{'label'} ) > 25 ? ( substr($row->{'label'}, 0, 22).'...') : $row->{'label'};
          $value = '';
          $key = sprintf('%03d', $order).':'.$text;
          $point->{'zmenu'}->{$key} = $value;
          $order++;
        }
        elsif ($entry eq 'userdata') {
          my $id = $row->{'label'};
          $key = sprintf('%03d', $order).':'.$id;
          $point->{'zmenu'}->{$key} = '';
          $order++;
        }
      }
    }

    # OK, we now have a complete pointer, so add it to the hash of arrays
    if(exists $high{$chr}) {
      push @{$high{$chr}}, $point;
    } 
    else {
      $high{$chr} = [ $point ];
    }
  }
  return \%high;
}

#############################################################################
sub object             : lvalue { $_[0]->{'object'}; }
sub drawable_container : lvalue { $_[0]->{'drawable_container'}; }
sub menu_container     : lvalue { $_[0]->{'menu_container'}; }
sub imagemap           : lvalue { $_[0]->{'imagemap'}; }
sub set_button {
  my( $self, $type, %pars ) = @_;
  $self->button          = $type;
  $self->{'button_id'}   = $pars{'id'}     if exists $pars{'id'};
  $self->{'button_name'} = $pars{'id'}     if exists $pars{'id'};
  $self->{'button_name'} = $pars{'name'}   if exists $pars{'name'};
  $self->{'URL'}         = $pars{'URL'}    if exists $pars{'URL'};
  $self->{'hidden'}      = $pars{'hidden'} if exists $pars{'hidden'};
  $self->{'button_title'} = $pars{'title'} if exists $pars{'title'};
  $self->{'hidden_extra'} = $pars{'extra'} if exists $pars{'extra'};
}
sub button             : lvalue { $_[0]->{'button'}; }
sub button_id          : lvalue { $_[0]->{'button_id'}; }
sub button_name        : lvalue { $_[0]->{'button_name'}; }
sub button_title       : lvalue { $_[0]->{'button_title'}; }
sub image_type         : lvalue { $_[0]->{'image_type'}; }
sub image_name         : lvalue { $_[0]->{'image_name'}; }
sub image_id           : lvalue { $_[0]->{'image_id'}; }

# Deprecated:
# sub cacheable          : lvalue { $_[0]->{'cacheable'}; }

sub image_width        { $_[0]->drawable_container->{'config'}->get_parameter('image_width'); }
sub introduction       : lvalue { $_[0]->{'introduction'}; }
sub tailnote           : lvalue { $_[0]->{'tailnote'}; }
sub caption            : lvalue { $_[0]->{'caption'}; }
sub format             : lvalue { $_[0]->{'format'}; }
sub panel              : lvalue { $_[0]->{'panel'}; }
## TODO: Obsolete code, remove
#sub exists { 
#  my $self = shift;
#  return 0 unless $self->cacheable eq 'yes';
#  my $image = new EnsEMBL::Web::File::Image( $self->{'species_defs'} );
#  $image->set_cache_filename( $self->image_type, $self->image_name );
#  return $image->exists;
#}

####################################################################################################
##
## Renderers
##
####################################################################################################

sub extraHTML {
  my $self = shift;
  my $extra = '';

  if( $self->{'image_id'} ) {
    $extra .= qq(id="$self->{'image_id'}" )
  }

  if( $self->{'img_map'} ) {
    if( $self->{'id'} ) {
      $extra .= qq(usemap="#$self->{'id'}_map" );
    } else {
      $extra .= qq(usemap="#$self->{'token'}" );
    }
  }
  return $extra;
}

sub extraStyle {
  my $self = shift;
  my $extra = '';
  if( $self->{'border'} ) {
    $extra .= sprintf qq(border: %s %dpx %s;),
              $self->{'border_colour'} || '#000', $self->{'border'},
              $self->{'border_style'}||'solid'; 
  }
  return $extra;
}

sub render_image_tag {
  my $self  = shift;
  my $image = shift;

  my $HTML;

  if ($image->width > 5000) {
    my $url = $image->URL;
    $HTML = qq(
               <p style="text-align:left">
                 The image produced was ".$image->width." pixels wide,
                 which may be too large for some web browsers to display.
                 If you would like to see the image, please right-click (MAC: Ctrl-click)
                 on the link below and choose the 'Save Image' option from the pop-up menu.
                 Alternatively, try reconfiguring KaryoView, either merging the features
                 into a single track (step 1) or selecting one chromosome at a time (Step 3).</p>
               <p><a href="$url">Image download</a></p>
            );
  } else {
    $HTML = sprintf '<img src="%s" alt="%s" title="%s" style="width: %dpx; height: %dpx; %s display: block" %s />',
                       $image->URL,
                       $self->{'button_title'},
                       $self->{'button_title'},
                       $image->width,
                       $image->height,
                       $self->extraStyle,
                       $self->extraHTML;

    $self->{'width'}  = $image->width;
    $self->{'height'} = $image->height;
  }

  return $HTML;
} 

sub render_image_button {
  my $self = shift;
  my $image = shift;

  my $HTML = sprintf
    '<input style="width: %dpx; height: %dpx; %s display: block" type="image" name="%s" id="%s" src="%s" alt="%s" title="%s" %s />',
             $image->width,
             $image->height,
             $self->extraStyle,
             $self->{'button_name'},
             $self->{'image_id'} || $self->{'button_name'},
             $image->URL,
             $self->{'button_title'},
             $self->{'button_title'};

  return $HTML;
} 

sub render_image_map {
  my $self  = shift;
  my $image = shift;

  my $imagemap = $self->drawable_container->render('imagemap');

  my $map_name = $self->{'image_id'} ? "$self->{'image_id'}_map" : $image->token;

  return sprintf(
            qq(<map name="%s" id="%s">\n%s\n</map>),
            $map_name,
            $map_name,
            $imagemap
  );
}

sub render {
  my( $self, $format ) = @_;

  if( $format ) {
    print $self->drawable_container->render( $format );
    return;
  }

  my $HTML = $self->introduction;

  ## Here we have to do the next bit which is to draw the image itself;
  my $image   = new EnsEMBL::Web::TmpFile::Image;
  my $content = $self->drawable_container->render('png');

  $image->content($content);
  $image->save;
  if ($self->button eq 'form') {

    $self->{'image_id'} = $self->{'button_id'};
    my $image_html      = $self->render_image_button($image);
    $self->{'hidden'}{'total_height'} = $image->height;
    $image_html .= sprintf qq(<div style="text-align: center; font-weight: bold">%s</div>),
                   $self->caption
                     if $self->caption;

    $HTML .= sprintf '<form style="width: %spx" class="autocenter" action="%s" method="get"><div>%s</div><div class="autocenter">%s</div></form>',
      $image->width,
      $self->{'URL'},
      join(
        '', map {
              sprintf '<input type="hidden" name="%s" id="%s%s" value="%s" />',
                      $_,
                      $_,
                      $self->{'hidden_extra'} || $self->{'counter'},
                      $self->{'hidden'}{$_}
            } keys %{$self->{'hidden'}},
      ),
      $image_html;
    $self->{'counter'}++;

  } elsif ($self->button eq 'yes') {

      $self->{'image_id'} = $self->{'button_id'};
      $HTML .= $self->render_image_button($image);
      $HTML .= sprintf qq(<div style="text-align: center; font-weight: bold">%s</div>),
                       $self->caption
                         if $self->caption;

  } elsif( $self->button eq 'drag' ) {

    $self->{'image_id'} = $self->{'prefix'} . "_$self->{'panel_number'}_i";

    my $tag = $self->render_image_tag($image);
    ## Now we have the image dimensions, we can set the correct DIV width
    $HTML .= $self->menu_container->render_html . $self->menu_container->render_js
               if $self->menu_container;

    ## continue with tag HTML
    ### This has to have a vertical padding of 0px as it is used in a number of places
    ### butted up to another container! - if you need a vertical padding of 10px add it
    ### outside this module!
    my $URL = $ENV{'REQUEST_URI'};
       $URL =~ s/;$//;
       $URL .= $URL =~ /\?/ ? ';' : '?';
       $URL .= 'export=pdf';
    $HTML .= '<div style="text-align:center">' .
             '<div style="text-align:center;margin:auto;border:0px;padding:0px">' .
             sprintf( qq(<div class="drag_select" id="%s_%s" style="margin: 0px auto; border: solid 1px black; position: relative; width:%dpx">),
      	       $self->{'prefix'},
      	       $self->{'panel_number'},
      	       $image->width
      	     ) .
             $tag .
             ($self->imagemap eq 'yes' ? $self->render_image_map($image) : '' ) .
             '</div>' .
             ( $self->{'export'} ? qq{<div class="$self->{'export'}" style="width:$image->{'width'}px;"><a href="$URL">Export</a></div>} : '' ) .
             ( $self->caption ? sprintf(qq(<div style="text-align: center; font-weight: bold">%s</div>), $self->caption) : '' ) .
             '</div></div>';
  } else {
    my $tag = $self->render_image_tag($image);
    ## Now we have the image dimensions, we can set the correct DIV width 
    if( $self->menu_container ) { 
      $HTML .= $self->menu_container->render_html;
      $HTML .= $self->menu_container->render_js;
    } 
    ## continue with tag HTML
    ### This has to have a vertical padding of 0px as it is used in a number of places
    ### butted up to another container! - if you need a vertical padding of 10px add it
    ### outside this module!
    $HTML .= sprintf '<div class="center" style="border:0px;margin:0px;padding:0px"><div style="text-align: center">%s</div>%s%s%s</div>',
               $tag,
               $self->imagemap eq 'yes'
                 ? $self->render_image_map($image)
                 : '',
               $self->{'export'}
                 ? '<div style="text-align:right; background-color; red;">EXPORT</div>'
                 : '',
               $self->caption
                 ? sprintf('<div style="text-align: center; font-weight: bold">%s</div>', $self->caption)
                 : '';
  }

  $HTML .= $self->tailnote;
  $self->{'width'} = $image->width;
  $self->{'species_defs'}->timer_push('Image->render ending', undef, 'draw');

  return $HTML
}

1;

__END__
=head1 NAME
Document:Image
=head1 SYNOPSIS

This object creates and renders a dynamically-generated image using modules from ensembl-draw. It is called via one of the image methods in the Object module, thus:

  my $image          = $self->new_karyotype_image();

The image's parameters can then be set using the accessor methods (see below for a full list), e.g.

  $image->image_name = "feature-$species";
  $image->imagemap   = 'yes';

Finally, additional features can be added to the image where appropriate:

  my $pointers = $image->add_pointers( $object,
                            {'config_name'  => 'Vkaryotype',
                             'zmenu_config' => $zmenu_config,
                             'feature_type' => 'Gene',
                             'color'        => $object->param("color")
                             'style'        => $object->param("style"),
                            }
                        );
  $image->karyotype($object, [$pointers], 'Vkaryotype');
=head1 DESCRIPTION
=head1 METHODS
=head2 B<new>
Description:    Constructor method for the Document::Image object
Arguments:      [1] class name, [2] a SpeciesDefs object, [3] the name of the Panel object the image belongs to
Returns:        a Document::Image object

=head2 B<set_extra>
Description:    
Arguments:     
Returns:        

=head2 B<karyotype>
Description:    Creates a VDrawableContainer object configured to display a karyotype, and assigns it to the 'drawable_container' property of the Image object
Arguments:      [1] A Document::Image object, [2] a ref to an array of sequence features, [3] a ref to an array of "highlights", i.e. tracks or pointers, [4] a configuration name for retrieving the user's configuration
Returns:        undef on success

=head2 B<do_chr_layout>
Description:    Adds chromosome row and spacing parameters to image configuration. This method is called by both C<add_tracks> and C<add_pointers>, so does not need to be called separately for images that display feature highlights.
Arguments:     [1] a Document::Image object, [2] a Proxy::Object (i.e. sequence data), [3] a configuration name (see C<karyotype>), [4] a maximum label size (optional, for use mainly with text-based highlights) 
Returns:        true

=head2 B<add_tracks>
Description:    Adds feature density tracks to a karyotype image, from the user's own data and/or the user's selection from some standard Ensembl data
Arguments:     [1] a Document::Image object, [2] a Proxy::Object (i.e. sequence
data), [3] a configuration name (see C<karyotype>), [4] a parser object containing user data (optional)
Returns:        true

=head2 B<add_pointers>
Description:   Creates a hash of feature location pointers  from the user's own data and/or the user's selection from some standard Ensembl data. Note that this method does all the messy configuration of pointers and zmenus so you don't have to! 
Arguments:     [1] a Document::Image object, [2] a Proxy::Object (i.e. sequence
data), [3] a ref to a hash of other options: config_name, zmenu_config, parser, color, style (the latter two for the appearance of the pointers). Color, style and zmenu_config can be set via this hash or via a web form; if neither is used, sensible defaults are provided by the method.
Returns:        a reference to the hash of pointers

=head2 B<accessor methods>

=over 4

=item object

=item drawable_container

=item menu_container

=item imagemap

=item set_button

=item button             

=item button_id         

=item button_name      

=item button_title   

=item image_name     

=item image_id

=item image_width  

=item introduction   

=item tailnote      

=item caption      

=item format      

=item panel      

=back
=head2 B<add_image_format>
Description:    
Arguments:     
Returns:        

=head2 B<exists>
Description:    
Arguments:     
Returns:        

=head2 B<render>
Description:    
Arguments:     
Returns:        

=head1 BUGS AND LIMITATIONS
None known at present.
=head1 AUTHOR
Anne Parker, Ensembl Web Team
Support enquiries: helpdesk\@ensembl.org
=head1 COPYRIGHT
See http://www.ensembl.org/info/about/code_licence.html
=cut