=head1 LICENSE

 Copyright (c) 1999-2012 The European Bioinformatics Institute and
 Genome Research Limited.  All rights reserved.

 This software is distributed under a modified Apache license.
 For license details, please see

   http://www.ensembl.org/info/about/code_licence.html

=head1 CONTACT

 Please email comments or questions to the public Ensembl
 developers list at <dev@ensembl.org>.

 Questions may also be sent to the Ensembl help desk at
 <helpdesk@ensembl.org>.

=cut

# Ensembl module for Bio::EnsEMBL::Variation::StructuralVariationFeature
#
# Copyright (c) 2011 Ensembl
#


=head1 NAME

Bio::EnsEMBL::Variation::StructuralVariationFeature - A genomic position for a structural variation.

=head1 SYNOPSIS

    # Structural variation feature representing a CNV
    $svf = Bio::EnsEMBL::Variation::StructuralVariationFeature->new
       (-start   => 100,
        -end     => 200,
        -strand  => 1,
        -slice   => $slice,
        -variation_name => 'esv1001',
				-class_so_term => 'copy_number_variation',
				-source => 'DGVa',
				-source_description => 'Database of Genomic Variants Archive',
			 );

    ...

    print $svf->start(), "-", $svf->end(), '(', $svf->strand(), ')', "\n";

    print $svf->variation_name(), ":", $svf->var_class();

=head1 DESCRIPTION

This is a class representing the genomic position of a structural variant
from the ensembl-variation database.  A StructuralVariationFeature behaves as any other
Ensembl feature. See B<Bio::EnsEMBL::Feature> and
B<Bio::EnsEMBL::Variation::Variation>.

=head1 METHODS

=cut

use strict;
use warnings;

package Bio::EnsEMBL::Variation::StructuralVariationFeature;

use Scalar::Util qw(weaken isweak);

use Bio::EnsEMBL::Variation::BaseVariationFeature;
use Bio::EnsEMBL::Utils::Exception qw(throw warning deprecate);
use Bio::EnsEMBL::Utils::Argument  qw(rearrange);
use Bio::EnsEMBL::Utils::Scalar qw(assert_ref);
use Bio::EnsEMBL::Slice;
use Bio::EnsEMBL::Variation::Utils::Constants qw(%VARIATION_CLASSES);
use Bio::EnsEMBL::Variation::Utils::VariationEffect qw(MAX_DISTANCE_FROM_TRANSCRIPT);
use Bio::EnsEMBL::Variation::StructuralVariationOverlap;
use Bio::EnsEMBL::Variation::TranscriptStructuralVariation;
use Bio::EnsEMBL::Variation::IntergenicStructuralVariation;

our @ISA = ('Bio::EnsEMBL::Variation::BaseVariationFeature');

=head2 new

  Arg [-dbID] :
    see superclass constructor

  Arg [-ADAPTOR] :
    see superclass constructor

  Arg [-START] :
    see superclass constructor
  Arg [-END] :
    see superclass constructor

  Arg [-STRAND] :
    see superclass constructor

  Arg [-SLICE] :
    see superclass constructor
	
  Arg [-INNER_START] :
	int - the 5'-greater coordinate of the underlying structural variation
	
  Arg [-INNER_END] :
	int - the 3'-less coordinate of the underlying structural variation

	 Arg [-OUTER_START] :
	int - the 5'-less coordinate of the underlying structural variation
	
  Arg [-OUTER_END] :
	int - the 3'-greater coordinate of the underlying structural variation

  Arg [-VARIATION_NAME] :
    string - the name of the variation this feature is for (denormalisation
    from Variation object).

	Arg [-CLASS_SO_TERM] :
		string - the sequence ontology term defining the class of the structural variation.
		
	Arg [-ALLELE_STRING] :
		string - allele sequence of the structural variation.
		
  Arg [-SOURCE] :
    string - the name of the source where the variation comes from
	
  Arg [-SOURCE_VERSION]:
	string - version number of the source
	
	Arg [-IS_SOMATIC] :
	  int - flag to inform whether the structural variant is a somatic (1) or germline (0).

	Arg [-BREAKPOINT_ORDER] :
	  int - For a structural variant with multiple breakpoints, this gives the predicted order of the breakpoint event.
	
  Example    :
    $svf = Bio::EnsEMBL::Variation::StructuralVariationFeature->new
       (-start   => 100,
        -end     => 200,
        -strand  => 1,
        -slice   => $slice,
        -variation_name => 'esv25480',
		    -class_so_term => 'structural_variant',
		    -source => 'DGVa');

  Description: Constructor. Instantiates a new StructuralVariationFeature object.
  Returntype : Bio::EnsEMBL::Variation::StructuralVariationFeature
  Exceptions : none
  Caller     : general
  Status     : At Risk

=cut

sub new {
  my $caller = shift;
  my $class = ref($caller) || $caller;

  my $self = $class->SUPER::new(@_);
  
  my (
    $var_name, 
    $source, 
    $source_version, 
    $class_so_term, 
    $inner_start, 
    $inner_end,
	$outer_start,
	$outer_end, 
    $allele_string,
	$is_somatic,
	$breakpoint_order
  ) = rearrange([qw(
	VARIATION_NAME 
	SOURCE 
	SOURCE_VERSION
	CLASS_SO_TERM
	INNER_START 
	INNER_END 
	OUTER_START
	INNER_START
	ALLELE_STRING
	IS_SOMATIC
	BREAKPOINT_ORDER
  )], @_);


  $self->{'variation_name'}     = $var_name;
  $self->{'source'}             = $source;
  $self->{'source_version'}     = $source_version;
  $self->{'class_SO_term'}      = $class_so_term;
  $self->{'inner_start'}        = $inner_start;
  $self->{'inner_end'}          = $inner_end;
  $self->{'outer_start'}        = $outer_start;
  $self->{'outer_end'}          = $outer_end;
  $self->{'allele_string'}      = $allele_string;
  $self->{'is_somatic'}         = $is_somatic || 0;
  $self->{'breakpoint_order'}   = $breakpoint_order;

  return $self;
}



sub new_fast {
  my $class = shift;
  my $hashref = shift;
  return bless $hashref, $class;
}


=head2 display_id

  Arg [1]    : none
  Example    : print $svf->display_id(), "\n";
  Description: Returns the 'display' identifier for this feature. For
               StructuralVariationFeatures this is simply the name of the structural variation
               it is associated with.
  Returntype : string
  Exceptions : none
  Caller     : webcode
  Status     : At Risk

=cut

sub display_id {
  my $self = shift;
  return $self->{'variation_name'} || '';
}



=head2 variation_name

  Arg [1]    : string $newval (optional)
               The new value to set the variation_name attribute to
  Example    : $variation_name = $obj->variation_name()
  Description: Getter/Setter for the variation_name attribute.  This is the
               name of the structural variant associated with this feature.
  Returntype : string
  Exceptions : none
  Caller     : general
  Status     : Stable

=cut

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

=head2 allele_string

  Arg [1]    : string $newval (optional)
               The new value to set the allele_string attribute to
  Example    : $allele_string = $obj->allele_string()
  Description: Getter/Setter for the allele_string attribute. This is the
               genomic sequence represented by this feature.
  Returntype : string
  Exceptions : none
  Caller     : general
  Status     : Stable

=cut

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



=head2 structural_variation

  Arg [1]    : (optional) Bio::EnsEMBL::Variation::StructuralVariation or 
	             Bio::EnsEMBL::Variation::SupportingStructuralVariation $structural_variation
  Example    : $sv = $svf->structural_variation();
  Description: Getter/Setter for the structural variant associated with this feature.
               If not set, and this StructuralVariationFeature has an associated adaptor
               an attempt will be made to lazy-load the structural variation from the
               database.
  Returntype : Bio::EnsEMBL::Variation::StructuralVariation or 
	             Bio::EnsEMBL::Variation::SupportingStructuralVariation
  Exceptions : throw on incorrect argument
  Caller     : general
  Status     : Stable

=cut

sub structural_variation {
  my $self = shift;

  if(@_) {
    if(!ref($_[0]) || (!$_[0]->isa('Bio::EnsEMBL::Variation::StructuralVariation') &&
		                   !$_[0]->isa('Bio::EnsEMBL::Variation::SupportingStructuralVariation')
		)) {
      throw("Bio::EnsEMBL::Variation::StructuralVariation or Bio::EnsEMBL::Variation::SupportingStructuralVariation argument expected");
    }
    $self->{'structural_variation'} = shift;
  }
  elsif(!defined($self->{'structural_variation'}) && $self->{'adaptor'} &&
        defined($self->{'structural_variation_id'})) {
    # lazy-load from database on demand
		my $sva = $self->{'adaptor'}->db()->get_StructuralVariationAdaptor();
		$self->{'structural_variation'} = $sva->fetch_by_dbID($self->{'structural_variation_id'});
		if (!defined($self->{'structural_variation'})) {
			$sva = $self->{'adaptor'}->db()->get_SupportingStructuralVariationAdaptor();
			$self->{'structural_variation'} = $sva->fetch_by_dbID($self->{'structural_variation_id'});
		}
  }

  return $self->{'structural_variation'};
}





=head2 get_all_VariationSets

    Args        : none
    Example     : my @vs = @{$svf->get_all_VariationSets()};
    Description : returns a reference to a list of all the VariationSets this
                  StructuralVariationFeature is a member of
    ReturnType  : reference to list of Bio::EnsEMBL::Variation::VariationSets
    Exceptions  : if no adaptor is attached to this object
    Caller      : general
    Status      : At Risk
=cut

sub get_all_VariationSets {
    my $self = shift;
    
    if (!$self->adaptor()) {
      throw('An adaptor must be attached in order to get all variation sets');
    }
    my $vs_adaptor = $self->adaptor()->db()->get_VariationSetAdaptor();
    my $variation_sets = $vs_adaptor->fetch_all_by_StructuralVariation($self->structural_variation());
    
    return $variation_sets;
}


=head2 get_nearest_Gene

  Example     : $svf->get_nearest_Gene($flanking_size);
  Description : Getter a Gene which is associated to or nearest to the StructuralVariationFeature
  Returntype  : Listref of objects of Bio::EnsEMBL::Gene
  Exceptions  : None
  Caller      : general
  Status      : At Risk

=cut

sub get_nearest_Gene{

    my $self = shift;
    my $flanking_size = shift; #flanking size is optional
    $flanking_size ||= 0;
    my $sa = $self->{'adaptor'}->db()->dnadb->get_SliceAdaptor();
    my $slice = $sa->fetch_by_Feature($self,$flanking_size);
    my @genes = @{$slice->get_all_Genes};
    return \@genes if @genes; #$svf is on the gene

    if (! @genes) { #if $svf is not on the gene, increase flanking size
      warning("flanking_size $flanking_size is not big enough to overlap a gene, increase it by 1,000,000");
      $flanking_size += 1000000;
      $slice = $sa->fetch_by_Feature($self,$flanking_size);
      @genes = @{$slice->get_all_Genes};
    }
    if (@genes) {
      my %distances = ();
      foreach my $g (@genes) {
        if ($g->seq_region_start > $self->start) {
          $distances{$g->seq_region_start-$self->start}=$g;
        }
        else {
          $distances{$self->start-$g->seq_region_end}=$g;
        }
      }
      my @distances = sort {$a<=>$b} keys %distances;
      my $shortest_distance = $distances[0];
      if ($shortest_distance) {
        my $nearest_gene = $distances{$shortest_distance};
        return [$nearest_gene];
      }
    }
    else {
      throw("variation_feature with flanking_size $flanking_size is not overlap with a gene, try a bigger flanking_size");
    }
}


=head2 is_somatic

  Arg [1]    : boolean $is_somatic (optional)
               The new value to set the is_somatic flag to
  Example    : $is_somatic = $svf->is_somatic
  Description: Getter/Setter for the is_somatic flag, which identifies this structural variation feature as either somatic or germline
  Returntype : boolean
  Exceptions : none
  Caller     : general
  Status     : Stable

=cut

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


=head2 breakpoint_order

  Arg [1]    : string $bp_order (optional)
               The new value to set the breakpoint order to
  Example    : $bp_order = $svf->breakpoint_order()
  Description: Getter/Setter for the breakpoint_order attribute
  Returntype : string
  Exceptions : none
  Caller     : general
  Status     : At Risk

=cut

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

=head2 get_all_StructuralVariationOverlaps

  Description : Get all the StructuralVariationOverlaps associated with this StructuralVariation, this
                includes TranscriptStructuralVariations and regulatory feature overlap object.
  Returntype  : listref of Bio::EnsEMBL::Variation::StructuralVariationOverlap objects
  Exceptions  : none
  Status      : At Risk

=cut

sub get_all_StructuralVariationOverlaps {
  my $self = shift;
  
  my $vfos =  [
	@{ $self->get_all_TranscriptStructuralVariations },
	@{ $self->get_all_RegulatoryFeatureStructuralVariations },
	@{ $self->get_all_MotifFeatureStructuralVariations },
  ];

  if (my $iv = $self->get_IntergenicStructuralVariation) {
	push @$vfos, $iv;
  }

  return $vfos;
}

=head2 get_all_TranscriptStructuralVariations

  Arg [1]     : (optional) listref of Bio::EnsEMBL::Transcript objects
  Example     : $svf->get_all_TranscriptStructuralVariations;
  Description : Get all the TranscriptStructuralVariations associated with this
                StructuralVariationFeature. If the optional list of Transcripts
				is supplied, get only TranscriptStructuralVariations
		        associated with those Transcripts.
  Returntype  : listref of Bio::EnsEMBL::Variation::TranscriptVariation objects
  Exceptions  : Thrown on wrong argument type
  Caller      : general
  Status      : At Risk

=cut

sub get_all_TranscriptStructuralVariations {
  my ($self, $transcripts) = @_;
  
  if ($transcripts) {
	assert_ref($transcripts, 'ARRAY');
	map { assert_ref($_, 'Bio::EnsEMBL::Transcript') } @$transcripts;
  }
  
  elsif (not defined $self->{transcript_structural_variations}) {
	# this VariationFeature is not in the database so we have to build the 
	# TranscriptVariations ourselves
	
	unless ($transcripts) {
	  # if the caller didn't supply some transcripts fetch those around this VariationFeature
	  # get a slice around this transcript including the maximum distance up and down-stream
	  # that we still call consequences for
	  my $slice = $self->feature_Slice->expand(
		MAX_DISTANCE_FROM_TRANSCRIPT, 
		MAX_DISTANCE_FROM_TRANSCRIPT
	  );
	  
	  # fetch all transcripts on this slice 
	  $transcripts = $slice->get_all_Transcripts(1);
	}
	
	my @unfetched_transcripts = grep { 
	  not exists $self->{transcript_structural_variations}->{$_->stable_id} 
	} @$transcripts;
	
	for my $transcript (@unfetched_transcripts) {
	  $self->add_TranscriptStructuralVariation(
		Bio::EnsEMBL::Variation::TranscriptStructuralVariation->new(
		  -structural_variation_feature  => $self,
		  -transcript                    => $transcript,
		  -adaptor                       => undef,
		)
	  );
	}
  }
  
  if ($transcripts) {
	# just return TranscriptVariations for the requested Transcripts
	return [ map { $self->{transcript_structural_variations}->{$_->stable_id} } @$transcripts ];
  }
  else {
	# return all TranscriptVariations
	return [ values %{ $self->{transcript_structural_variations} } ];
  }
}

=head2 get_all_RegulatoryFeatureStructuralVariations

  Description : Get all the RegulatoryFeatureStructuralVariations associated with this VariationFeature.
  Returntype  : listref of Bio::EnsEMBL::Variation::StructuralVariationOverlap objects
  Exceptions  : none
  Status      : At Risk

=cut

sub get_all_RegulatoryFeatureStructuralVariations {
  my $self = shift;
  return $self->_get_all_RegulationStructuralVariations('RegulatoryFeature', @_);
}

=head2 get_all_MotifFeatureStructuralVariations

  Description : Get all the MotifFeatureStructuralVariations associated with this VariationFeature.
  Returntype  : listref of Bio::EnsEMBL::Variation::StructuralVariationOverlap objects
  Exceptions  : none
  Status      : At Risk

=cut

sub get_all_MotifFeatureStructuralVariations {
  my $self = shift;
  return $self->_get_all_RegulationStructuralVariations('MotifFeature', @_);
}

=head2 get_all_ExternalFeatureStructuralVariations

  Description : Get all the ExternalFeatureStructuralVariations associated with this VariationFeature.
  Returntype  : listref of Bio::EnsEMBL::Variation::StructuralVariationOverlap objects
  Exceptions  : none
  Status      : At Risk

=cut

sub get_all_ExternalFeatureStructuralVariations {
  my $self = shift;
  return $self->_get_all_RegulationStructuralVariations('ExternalFeature', @_);
}

sub _get_all_RegulationStructuralVariations {
  my ($self, $type) = @_;
  
  unless ($type && ($type eq 'RegulatoryFeature' || $type eq 'MotifFeature' || $type eq 'ExternalFeature')) {
	throw("Invalid Ensembl Regulation type '$type'");
  }
  
  unless ($self->{regulation_structural_variations}->{$type}) {
	my $fg_adaptor;
	
	if (my $adap = $self->adaptor) {
	  if(my $db = $adap->db) {
		$fg_adaptor = Bio::EnsEMBL::DBSQL::MergedAdaptor->new(
		  -species  => $adap->db->species, 
		  -type     => $type,
		);			
	  }
	  
	  unless ($fg_adaptor) {
		warning("Failed to get adaptor for $type");
		return [];
	  }
	}
	else {
	  warning('Cannot get variation features without attached adaptor');
	  return [];
	}
	
	my $slice = $self->feature_Slice;
	
	my $constructor = 'Bio::EnsEMBL::Variation::StructuralVariationOverlap';
	
	eval {
	  $self->{regulation_structural_variations}->{$type} = [ 
		map {  
		  $constructor->new(
			-structural_variation_feature  => $self,
			-feature                       => $_,
		  );
		} map { $_->transfer($self->slice) } @{ $fg_adaptor->fetch_all_by_Slice($slice) } 
	  ];
	};
	
	$self->{regulation_structural_variations}->{$type} ||= [];
  }
  
  return $self->{regulation_structural_variations}->{$type};
}


sub get_IntergenicStructuralVariation {
  my $self = shift;
  my $no_ref_check = shift;
  
  unless (exists $self->{intergenic_structural_variation}) {
	if (scalar(@{ $self->get_all_TranscriptStructuralVariations }) == 0) {
	  $self->{intergenic_structural_variation} = Bio::EnsEMBL::Variation::IntergenicStructuralVariation->new(
		-structural_variation_feature  => $self,
		-no_ref_check                  => $no_ref_check,
	  );
	}
	else {
	  $self->{intergenic_structural_variation} = undef;
	}
  }
  
  return $self->{intergenic_structural_variation};
}



=head2 TranscriptStructuralVariation

  Arg [1]     : Bio::EnsEMBL::Variation::TranscriptStructuralVariation
  Example     : $vf->add_TranscriptStructuralVariation($tsv);
  Description : Adds a TranscriptStructuralVariation to the structural variation
                feature object.
  Exceptions  : thrown on bad argument
  Caller      : Bio::EnsEMBL::Variation::StructuralVariationFeature,
                Bio::EnsEMBL::Varaition::Utils::VEP
  Status      : Stable

=cut

sub add_TranscriptStructuralVariation {
  my ($self, $tsv) = @_;
  assert_ref($tsv, 'Bio::EnsEMBL::Variation::TranscriptStructuralVariation');
  # we need to weaken the reference back to us to avoid a circular reference
  weaken($tsv->{base_variation_feature});
  $self->{transcript_structural_variations}->{$tsv->transcript_stable_id} = $tsv;
}


=head2 var_class

    Args         : None
    Example      : my $sv_class = $svf->var_class()
    Description  : Getter for the class of structural variation
    ReturnType   : String
    Exceptions   : none
    Caller       : General
    Status       : At Risk

=cut

sub var_class {
	my $self = shift;
    
	unless ($self->{class_display_term}) {
        my $display_term = $VARIATION_CLASSES{$self->{class_SO_term}}->{display_term};

        warn "No display term for SO term: ".$self->{class_SO_term} unless $display_term;

        $self->{class_display_term} = $display_term || $self->{class_SO_term};
    }

	return $self->{class_display_term};
}


=head2 class_SO_term

    Args         : None
    Example      : my $sv_so_term = $svf->class_SO_term()
    Description  : Getter for the class of structural variation, returning the SO term
    ReturnType   : String
    Exceptions   : none
    Caller       : General
    Status       : At Risk

=cut

sub class_SO_term {
	my $self = shift;

	return $self->{class_SO_term};
}


=head2 source

  Arg [1]    : string $source (optional)
               The new value to set the source attribute to
  Example    : $source = $svf->source()
  Description: Getter/Setter for the source attribute
  Returntype : string
  Exceptions : none
  Caller     : general
  Status     : At Risk

=cut

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

=head2 source_version

  Arg [1]    : string $source_version (optional)
               The new value to set the source_version attribute to
  Example    : $source_version = $svf->source_version()
  Description: Getter/Setter for the source_version attribute
  Returntype : string
  Exceptions : none
  Caller     : general
  Status     : At Risk

=cut

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

=head2 bound_start

		Args        : None
    Example     : my $bound_start = $svf->bound_start();
    Description : Getter/setter for the 5'-most coordinate defined for this StructuralVariationFeature (outer_start or start)
    ReturnType  : int
    Exceptions  : none
    Caller      : general
    Status      : At risk
=cut

sub bound_start{
  my $self = shift;
	return $self->{'outer_start'} if (defined($self->{'outer_start'}));
	return $self->{'start'};
}


=head2 bound_end

		Args        : None
    Example     : my $bound_end = $svf->bound_end();
    Description : Getter/setter for the 3'-most coordinate defined for this StructuralVariationFeature (outer_end or end)
    ReturnType  : int
    Exceptions  : none
    Caller      : general
    Status      : At risk
=cut

sub bound_end{
  my $self = shift;
	return $self->{'outer_end'} if (defined($self->{'outer_end'}));
	return $self->{'end'};
}


=head2 outer_start

		Arg [1]     : int $outer_start (optional)
								  The new value to set the outer_start attribute to
    Example     : my $outer_start = $svf->outer_start();
    Description : Getter/setter for the 5'-most coordinate defined for this StructuralVariationFeature
    ReturnType  : int
    Exceptions  : none
    Caller      : general
    Status      : At risk
=cut

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


=head2 outer_end

	  Arg [1]     : int $outer_end (optional)
				          The new value to set the outer_end attribute to
    Example     : my $outer_end = $svf->outer_end();
    Description : Getter/setter for the 3'-most coordinate defined for this StructuralVariationFeature
    ReturnType  : int
    Exceptions  : none
    Caller      : general
    Status      : At risk
=cut

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


=head2 inner_start

	Arg [1]       : int $inner_start (optional)
				          The new value to set the inner_start attribute to
    Example     : my $inner_start = $svf->inner_start();
    Description : Getter/setter for the 5'-less coordinate defined for this StructuralVariationFeature
    ReturnType  : int
    Exceptions  : none
    Caller      : general
    Status      : At Risk
=cut

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


=head2 inner_end

	  Arg [1]     : int $inner_end (optional)
				          The new value to set the inner_end attribute to
    Example     : my $inner_end = $svf->inner_end();
    Description : Getter/setter for the 3'-less coordinate defined for this StructuralVariationFeature
    ReturnType  : int
    Exceptions  : none
    Caller      : general
    Status      : At Risk
=cut

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


=head2 get_reference_sequence

    Args        : none
    Example     : my $seq = $svf->get_reference_sequence
    Description : returns a string containing the reference sequence for the region
				  covered by this StructuralVariationFeature
    ReturnType  : string
    Exceptions  : none
    Caller      : general
    Status      : At Risk
=cut

sub get_reference_sequence{
  my $self = shift;
  
  return $self->feature_Slice->seq();
}


sub transform {
  my $self = shift;
  
  # run the transform method from the parent class
  my $transformed = $self->SUPER::transform(@_);
  
  if(defined $transformed) {
		# fit the start and end coords to the new coords
		$transformed->_fix_bounds($self);
  }
  
  return $transformed;
}


sub transfer {
  my $self = shift;
  
  # run the transfer method from the parent class
  my $transferred = $self->SUPER::transfer(@_);
  
  if(defined $transferred) {
		# fit the start and end coords to the new coords
		$transferred->_fix_bounds($self);
  }
  
  return $transferred;
}


sub _fix_bounds {
  my $self = shift;
  my $old = shift;
  
  if(defined $old->{'outer_start'}) {
	$self->{'outer_start'} = $self->start - ($old->start - $old->{'outer_start'});
  }
  
  if(defined $old->{'outer_end'}) {
	$self->{'outer_end'} = $self->end + ($old->{'outer_end'} - $old->end);
  }
}

sub _sort_svos {
  my $self = shift;
  
  return unless defined $self->{structural_variation_overlaps};
  
  my @svos = @{$self->{structural_variation_overlaps}};
  
  # define a feature order for sorting
  my %feature_order = (
	'Bio::EnsEMBL::Gene'       => 1,
	'Bio::EnsEMBL::Transcript' => 2,
	'Bio::EnsEMBL::Exon'       => 3,
  );
  
  # sort them nicely by feature type and position
  @svos = sort {
	$feature_order{ref($a->feature)} <=> $feature_order{ref($b->feature)} ||
	$a->feature->start <=> $b->feature->start
  } @svos;
  
  $self->{structural_variation_overlaps} = \@svos;
}

1;
