=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

=head1 NAME

Bio::EnsEMBL::StableIdEvent- object representing a stable ID mapping event

=head1 SYNOPSIS

  my $old_id = Bio::EnsEMBL::ArchiveStableId->new(
    -stable_id => 'ENSG001',
    -version   => 1,
    -type      => 'Gene',
  );

  my $new_id = Bio::EnsEMBL::ArchiveStableId->new(
    -stable_id => 'ENSG001',
    -version   => 2,
    -type      => 'Gene',
  );

  my $event = Bio::EnsEMBL::StableIdEvent->new(
    -old_id => $old_id,
    -new_id => $new_id,
    -score  => 0.997
  );

  # directly access attributes in old and new ArchiveStableId
  my $old_stable_id = $event->get_attribute( 'old', 'stable_id' );

=head1 DESCRIPTION

This object represents a stable ID mapping event. Such an event links two
ArchiveStableIds with a mapping score.

=head1 METHODS

  new
  old_ArchiveStableId
  new_ArchiveStableId
  score
  get_attribute
  ident_string

=head1 RELATED MODULES

  Bio::EnsEMBL::ArchiveStableId
  Bio::EnsEMBL::DBSQL::ArchiveStableIdAdaptor
  Bio::EnsEMBL::StableIdHistoryTree

=cut

package Bio::EnsEMBL::StableIdEvent;

use strict;
use warnings;
no warnings 'uninitialized';

use Bio::EnsEMBL::Utils::Exception qw(throw warning);
use Bio::EnsEMBL::Utils::Argument qw(rearrange);


=head2 new

  Arg[1]      : Bio::EnsEMBL::ArchiveStableId $old_id
                The old ArchiveStableId in the mapping event
  Arg[2]      : Bio::EnsEMBL::ArchiveStableId $new_id
                The new ArchiveStableId in the mapping event
  Arg[3]      : (optional) float $score - score of this mapping event
  Example     : my $event = Bio::EnsEMBL::StableIdEvent->new(
                  $arch_id1, $arch_id2, 0.977);
  Description : object constructor
  Return type : Bio::EnsEMBL::StableIdEvent
  Exceptions  : thrown on wrong argument types
  Caller      : Bio::EnsEMBL::DBSQL::ArchiveStableIdAdaptor::fetch_history_tree_by_stable_id, general
  Status      : At Risk
              : under development

=cut

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

  my ($old_id, $new_id, $score) = rearrange([qw(OLD_ID NEW_ID SCORE)], @_);

  throw("Need old or new Bio::EnsEMBL::ArchiveStableId to create StableIdEvent")
    unless ($old_id || $new_id);

  my $self = {};
  bless $self, $class;

  # initialise object
  $self->old_ArchiveStableId($old_id);
  $self->new_ArchiveStableId($new_id);
  $self->score($score);
  
  return $self;
}


=head2 old_ArchiveStableId

  Arg[1]      : (optional) Bio::EnsEMBL::ArchiveStableId $archive_id, or undef
                The old ArchiveStableId to set for this mapping event
  Example     : # getter
                my $archive_id = $event->old_ArchiveStableId;
                
                # setter
                $event->old_ArchiveStableId($archive_id);
  Description : Getter/setter for old ArchiveStableId in this mapping event.
  Return type : Bio::EnsEMBL::ArchiveStableId
  Exceptions  : thrown on wrong argument type
  Caller      : general
  Status      : At Risk
              : under development

=cut

sub old_ArchiveStableId {
  my $self = shift;
  
  # setter
  if (@_) {
    my $archive_id = shift;

    # if argument is defined, check type. undef is also legal as an argument.
    if (defined($archive_id)) {
      throw("Need a Bio::EnsEMBL::ArchiveStableId.") unless
        (ref($archive_id) && $archive_id->isa('Bio::EnsEMBL::ArchiveStableId'));
    }

    $self->{'old_id'} = $archive_id;
  }

  # getter
  return $self->{'old_id'};
}


=head2 new_ArchiveStableId

  Arg[1]      : (optional) Bio::EnsEMBL::ArchiveStableId $archive_id, or undef
                The new ArchiveStableId to set for this mapping event
  Example     : # getter
                my $archive_id = $event->new_ArchiveStableId;
                
                # setter
                $event->new_ArchiveStableId($archive_id);
  Description : Getter/setter for new ArchiveStableId in this mapping event.
  Return type : Bio::EnsEMBL::ArchiveStableId
  Exceptions  : thrown on wrong argument type
  Caller      : general
  Status      : At Risk
              : under development

=cut

sub new_ArchiveStableId {
  my $self = shift;
  
  # setter
  if (@_) {
    my $archive_id = shift;

    # if argument is defined, check type. undef is also legal as an argument.
    if (defined($archive_id)) {
      throw("Need a Bio::EnsEMBL::ArchiveStableId.") unless
        (ref($archive_id) && $archive_id->isa('Bio::EnsEMBL::ArchiveStableId'));
    }

    $self->{'new_id'} = $archive_id;
  }

  # getter
  return $self->{'new_id'};
}


=head2 score

  Arg[1]      : (optional) float $score - the score to set
  Example     : my $score = $event->score;
  Description : Getter/setter for mapping event score.
  Return type : float or undef
  Exceptions  : none
  Caller      : general
  Status      : At Risk
              : under development

=cut

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


=head2 get_attribute

  Arg[1]      : String $type - determines whether to get attribute from 'old'
                or 'new' ArchiveStableId
  Arg[2]      : String $attr - ArchiveStableId attribute to fetch
  Example     : my $old_stable_id = $event->get_attribute('old', 'stable_id');
  Description : Accessor to attributes of the ArchiveStableIds attached to this
                event. Convenience method that does the check for undef old
                and/or new ArchiveStableId for you.
  Return type : same as respective method in Bio::EnsEMBL::ArchiveStableId, or
                undef
  Exceptions  : thrown on wrong arguments
  Caller      : general
  Status      : At Risk
              : under development

=cut

sub get_attribute {
  my ($self, $type, $attr) = @_;

  throw("First argument passed to this function has to be 'old' or 'new'.")
    unless ($type eq 'old' or $type eq 'new');

  my %allowed_attribs = map { $_ => 1 }
    qw(stable_id version db_name release assembly);

  throw("Attribute $attr not allowed.") unless $allowed_attribs{$attr};

  my $call = $type.'_ArchiveStableId';

  if (my $id = $self->$call) {
    return $id->$attr;
  } else {
    return undef;
  }
}


=head2 ident_string

  Example     : print $event->ident_string, "\n";
  Description : Returns a string that can be used to identify your StableIdEvent.
                Useful in debug warnings.
  Return type : String
  Exceptions  : none
  Caller      : general
  Status      : At Risk
              : under development

=cut

sub ident_string {
  my $self = shift;

  my $old_id = $self->old_ArchiveStableId;
  my $new_id = $self->new_ArchiveStableId;

  my $str;

  if ($old_id) {
    $str = $old_id->stable_id.'.'.$old_id->version.' ('.
      $old_id->release.')';
  } else {
    $str = 'null';
  }

  $str .= ' -> ';

  if ($new_id) {
    $str .= $new_id->stable_id.'.'.$new_id->version.' ('.
      $new_id->release.')';
  } else {
    $str .= 'null';
  }

  $str .= ' ['.$self->score.']';
  
  return $str;
}


1;

