#########
# Author: rmp@sanger.ac.uk
# Maintainer: webmaster@sanger.ac.uk
# Created: 2001
#

package Sanger::Graphics::DrawableContainer;
use strict;
no warnings "uninitialized";
use Sanger::Graphics::Glyph::Line;
use Sanger::Graphics::Glyph::Rect;
use Sanger::Graphics::Glyph::Text;
use Sanger::Graphics::Glyph::Circle;
use Sanger::Graphics::Glyph::Poly;
use Sanger::Graphics::Glyph::Intron;
use Sanger::Graphics::Glyph::Composite;
use Sanger::Graphics::Renderer::png;
use Sanger::Graphics::Renderer::imagemap;
use Sanger::Graphics::Root;

our @ISA = qw(Sanger::Graphics::Root);

sub _init {
  my $class = shift;
  my $Contents = shift;
  unless(ref($Contents) eq 'ARRAY') {
    $Contents = [[ $Contents, shift ]];
  } else {
    my $T = [];
    while( @$Contents ) {
      push @$T, [splice(@$Contents,0,2)] ;
    }
    $Contents = $T;
  }
  my( $highlights, $strandedness, $Storage) = @_;
  my $self = {
    'glyphsets'     => [],
    'config'        => $Contents->[0][1],
    'storage'       => $Storage,
    'prefix'        => 'Sanger::Graphics',
    'contents'      => $Contents,
    'highlights'    => $highlights || [],
    'strandedness'  => $strandedness || 0,
    '__extra_block_spacing__'    => 0,
  };
  bless( $self, $class );
  return $self;
}
sub new {
  my $class = shift;
  my $self = $class->_init( @_ ); 
  my $T = $self->{'config'};
  my $button_width = $T->get_parameter('button_width')   || 7;
  my $show_buttons = $T->get_parameter('show_buttons')   || 'no' ;
  my $show_labels  = $T->get_parameter('show_labels')    || 'yes';
  my $label_width  = $T->get_parameter('label_width')    || 100;
  my $margin       = $T->get_parameter('margin')         || 5;
  my $trackspacing = $T->get_parameter('spacing')        || 2;
  my $inter_space  = defined($T->get_parameter('intercontainer')) ? $T->get_parameter('intercontainer') : $margin * 2;
  my $image_width  = $T->get_parameter('width')          || 700;
  my $label_start  = $margin      +
                     ( $show_buttons eq 'yes' ? $button_width + $margin : 0 );
  my $panel_start  = $label_start + 
                     ( $show_labels  eq 'yes' ? $label_width  + $margin : 0 );
  my $panel_width  = $image_width - $panel_start - $margin;
  $self->{'__extra_block_spacing__'} -= $inter_space;
  ## loop over all turned on & tuned in glyphsets
  my @strands_to_show = $self->{'strandedness'} == 1 ? (1) : (1, -1);
  my $yoffset = $margin;
  my $iteration = 0;

  foreach my $CC ( @{$self->{'contents'}} ) {
    my( $Container,$Config) = @$CC;
    my %manager_cache = ();
    if( defined( $Config->{'_managers'} ) )  {
      %manager_cache = map { $_ => 0 } keys %{$Config->{'_managers'}};
    }
    # warn ref($Container)," - ",ref($Config);
    $self->debug( 'start', ref($Config) ) if( $self->can('debug') );
    $Config->{'panel_width'} = $panel_width;
    unless(defined $Container) {
      warn ref($self).qq( No container defined);
      next;
    }
    unless(defined $Config) {
      warn ref($self).qq( No Config object defined);
      next;
    }
    my @glyphsets = ();
    $Container->{'_config_file_name_'} ||= $ENV{'ENSEMBL_SPECIES'};

    if( ($Container->{'__type__'}||"") eq 'fake' ) {
      my $classname = qq($self->{'prefix'}::GlyphSet::comparafake);
      next unless $self->dynamic_use( $classname );
      my $GlyphSet;
      eval {
        $GlyphSet = new $classname( $Container, $Config, $self->{'highlights'}, 1 );
      };
      $Config->container_width(1);
      push @glyphsets, $GlyphSet unless $@;
    } 
    else {
      for my $strand (@strands_to_show) {
	my $tmp_gs_store = {};
	for my $row ($Config->subsections( 1 )) {
	  next unless (($Config->get($row, 'on')||"") eq "on");  ## Skip if this is turned off
          next unless $Config->is_available_artefact($row);      ## Skip if not available for this species!
	  my $str_tmp = $Config->get($row, 'str');
	  next if (defined $str_tmp && $str_tmp eq "r" && $strand != -1);
	  next if (defined $str_tmp && $str_tmp eq "f" && $strand != 1);
	  if( defined $Config->get($row,'manager')) { 
	    $manager_cache{ $Config->get($row,'manager') } = 1; 
	    next;
	  }

	  ## create a new glyphset for this row
	  my $GlyphSet;
	  my $classname;
	  if( my $glyphset = $Config->get($row,'glyphset') ) {
	    $classname = qq($self->{'prefix'}::GlyphSet::$glyphset);
	    next unless $self->dynamic_use( $classname );
	    eval { # Generic glyphsets need to have the type passed as a fifth parameter...
	      $GlyphSet = new $classname( $Container, $Config, $self->{'highlights'}, $strand, { 'config_key' => $row } );
	    };
	  } else {
	    $classname = qq($self->{'prefix'}::GlyphSet::$row);
	    next unless $self->dynamic_use( $classname );
	    ## generate a set for both strands
	    eval {
	      $GlyphSet = $classname->new( $Container, $Config, $self->{'highlights'}, $strand );
	    };
	  }
	  if($@ || !$GlyphSet) {
	    my $reason = $@ || "No reason given just returns undef";
	    warn "GLYPHSET: glyphset $classname failed at ",gmtime(),"\n",
	      "GLYPHSET: $reason";
	  } 
	  else {
	    $tmp_gs_store->{$Config->get($row, 'pos')} = $GlyphSet;
	  }
	}
	my $managed_offset = $Config->{'_das_offset'} || 5500;
	## install the glyphset managers, we've just cached the ones we need...

	foreach my $manager ( reverse sort keys %manager_cache ) {
	  next unless $self->dynamic_use( $self->{'prefix'}.qq(::GlyphSet::$manager) );
	  my $classname = $self->{'prefix'}.qq(::GlyphSetManager::$manager);
	  next unless $self->dynamic_use( $classname );
	  my $gsm = new $classname( $Container, $Config, $self->{'highlights'}, $strand );

	  for my $glyphset (sort { $a->managed_name cmp $b->managed_name } $gsm->glyphsets()) {
	    my $row = $glyphset->managed_name();
	    next if     $Config->get($row, 'on')     eq "off";
	    next if     $Config->get($manager, 'on') eq 'off';
	    my $str_tmp = $Config->get($row, 'str');
	    next if (defined $str_tmp && $str_tmp eq "r" && $strand != -1);
	    next if (defined $str_tmp && $str_tmp eq "f" && $strand != 1);
	    $tmp_gs_store->{ $Config->get($row, 'pos') || $managed_offset++ } = $glyphset;
	  }
	}
	## sort out the resulting mess
	my @tmp = map { $tmp_gs_store->{$_} } 
	  sort { $a <=> $b }
	    keys %{ $tmp_gs_store };
	push @glyphsets, ($strand == 1 ? reverse @tmp : @tmp);
      }
}

   my $x_scale = $panel_width /( ($Config->container_width()|| $Container->length() || $panel_width));
  if($show_buttons eq 'yes' && $Config->get_parameter('URL') ) {
  for my $glyphset (@glyphsets) {
    next unless defined $glyphset->bumped();
    my $NAME = $glyphset->check();
      my $box_glyph = new Sanger::Graphics::Glyph::Rect({
        'x' => -$panel_start + $margin,
        'y'             => 0,
        'width'         => $button_width,
        'height'        => $button_width,
        'bordercolour'  => 'red',
        'absolutey' => 1,
        'absolutex' => 1,
        'absolutewidth' =>1, 'pixperbp' => $x_scale,
        'href'      => $Config->get( '_settings', 'URL')."$NAME%3A".
                       ($glyphset->bumped() eq 'yes' ? 'off' : 'on'),
        'id'        => $glyphset->bumped() eq 'yes' ? 'collapse' : 'expand',
      });
      my $horiz_glyph = new Sanger::Graphics::Glyph::Text({
        'text'      => $glyphset->bumped() eq 'yes' ? '-' : '+',
        'font'      => 'Small',
        'absolutey' => 1,
        'x'         => -$panel_start + $margin + 2,
        'y'         => -2,
        'width'     => 6,
        'textwidth'     => 6,
        'height'    => 6,
        'colour'    => 'red',
        'absolutex' => 1, 'absolutewidth' => 1, 'pixperbp' => $x_scale
      });
      $glyphset->bumpbutton([$horiz_glyph, $box_glyph]);
    }
    }
    ## set scaling factor for base-pairs -> pixels
    $Config->{'transform'}->{'scalex'} = $x_scale;
    $Config->{'transform'}->{'absolutescalex'} = 1;

    ## because our label starts are < 0, translate everything back onto canvas
    $Config->{'transform'}->{'translatex'} = $panel_start;

    ## set the X-locations for each of the bump buttons/labels
    for my $glyphset (@glyphsets) {
      next unless defined $glyphset->label();
      $glyphset->label->{'font'}        ||= $Config->{'_font_face'} || 'arial';
      $glyphset->label->{'ptsize'}      ||= $Config->{'_font_size'} || 100;
      $glyphset->label->{'halign'}      ||= 'left';
      $glyphset->label->{'absolutex'}    = 1;
      $glyphset->label->{'absolutewidth'}= 1;
      $glyphset->label->{'pixperbp'}     = $x_scale;
      $glyphset->label->x(-$label_width-$margin) if defined $glyphset->label;
    }

    ## pull out alternating background colours for this script
    my $white  = $Config->bgcolour() || 'white';
    my $bgcolours = [ $Config->get_parameter( 'bgcolour1') || $white,
                      $Config->get_parameter( 'bgcolour2') || $white ];

    my $bgcolour_flag;
    $bgcolour_flag = 1 if($bgcolours->[0] ne $bgcolours->[1]);

    ## go ahead and do all the database work
    for my $glyphset (@glyphsets) {
      ## load everything from the database
      my $NAME = $glyphset->check();
      my $ref_glyphset = ref($glyphset);
      eval {
        $glyphset->_init();
      };
      ## don't waste any more time on this row if there's nothing in it
      if( $@ || scalar @{$glyphset->{'glyphs'} } ==0 ) {
	if( $@ ){ warn( $@ ) }
        $glyphset->_dump('rendered' => 'no');
        next;
      };
      ## remove any whitespace at the top of this row
      my $gminy = $glyphset->miny();
      $Config->{'transform'}->{'translatey'} = -$gminy + $yoffset;

      if(defined $bgcolour_flag) {
        ## colour the area behind this strip
        my $background = new Sanger::Graphics::Glyph::Rect({
          'x'         => 0,
          'y'         => $gminy,
          'z'         => -100,
          'width'     => $panel_width,
          'height'    => $glyphset->maxy() - $gminy,
          'colour'    => $bgcolours->[$iteration % 2],
          'absolutewidth' =>1,
          'absolutex' => 1,
        });
      # this accidentally gets stuffed in twice (for gif & imagemap) so with
      # rounding errors and such we shouldn't track this for maxy & miny values
        unshift @{$glyphset->{'glyphs'}}, $background;
      }
      ## set up the "bumping button" label for this strip
      if(defined $glyphset->label() && $show_labels eq 'yes' ) {
        my $gh = $glyphset->label->height || $Config->texthelper->height($glyphset->label->font());
        $glyphset->label->y( ( ($glyphset->maxy() - $glyphset->miny() - $gh) / 2) + $gminy );
        $glyphset->label->height($gh);
        $glyphset->label()->pixelwidth( $label_width );
        $glyphset->push( $glyphset->label() );
        $glyphset->_dump('rendered' => $glyphset->label()->text());
      } else {
        $glyphset->_dump('rendered' => 'No label' );
      }
      if( $show_buttons eq 'yes' && defined $glyphset->bumpbutton()) {
        my $T = int(($glyphset->maxy() - $glyphset->miny() - 8) / 2 + $gminy );
        $glyphset->bumpbutton->[0]->y( $T     );
        $glyphset->bumpbutton->[1]->y( $T + 2 );
        $glyphset->push(@{$glyphset->bumpbutton()});
      }

      $glyphset->transform();
      ## translate the top of the next row to the bottom of this one
      $yoffset += $glyphset->height() + $trackspacing;
      $iteration ++;
    }
    push @{$self->{'glyphsets'}}, @glyphsets;
    $yoffset += $inter_space;
    $self->{'__extra_block_spacing__'}+= $inter_space;
    $Config->{'panel_width'} = undef;
    $self->debug( 'end', ref($Config) ) if( $self->can('debug') );
  }

  return $self;
}

## render does clever drawing things

sub render {
  my ($self, $type) = @_;
  ## build the name/type of render object we want
  my $renderer_type = qq(Sanger::Graphics::Renderer::$type);
  $self->dynamic_use( $renderer_type );
  ## big, shiny, rendering 'GO' button
  my $renderer = $renderer_type->new(
    $self->{'config'},
    $self->{'__extra_block_spacing__'},
    $self->{'glyphsets'}
  );
  my $canvas = $renderer->canvas();
  return $canvas;
}

sub config {
  my ($self, $Config) = @_;
  $self->{'config'} = $Config if(defined $Config);
  return $self->{'config'};
}

sub glyphsets {
  my ($self) = @_;
  return @{$self->{'glyphsets'}};
}

sub storage {
  my ($self, $Storage) = @_;
  $self->{'storage'} = $Storage if(defined $Storage);
  return $self->{'storage'};
}
1;

=head1 RELATED MODULES

See also: Sanger::Graphics::GlyphSet Sanger::Graphics::Glyph WebUserConfig

=head1 AUTHOR - Roger Pettett

Email - rmp@sanger.ac.uk

=cut