/*
 *This library is free software; you can redistribute it and/or
 *modify it under the terms of the GNU Lesser General Public
 *License as published by the Free Software Foundation; either
 *version 2.1 of the License, or (at your option) any later version.
 *
 *This library is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *Lesser General Public License for more details.
 *
 *You should have received a copy of the GNU Lesser General Public
 *License along with this library; if not, write to the Free Software
 *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* 
 * This software was written by Phillip Lord (p.lord@hgmp.mrc.ac.uk)
 * whilst at the University of Manchester as a Pfizer post-doctoral 
 * Research Fellow. 
 *
 * The initial code base is copyright by Pfizer, or the University
 * of Manchester. Modifications to the initial code base are copyright
 * of their respective authors, or their employers as appropriate. 
 * Authorship of the modifications may be determined from the ChangeLog
 * placed at the end of this file
 */

package uk.ac.man.bioinf.analysis.consensus; // Package name inserted by JPack
import uk.ac.man.bioinf.analysis.consensus.ConsensusCalculator;
import uk.ac.man.bioinf.sequence.Element;
import uk.ac.man.bioinf.sequence.alignment.AbstractGappedSequence;
import uk.ac.man.bioinf.sequence.alignment.EmptyGappedSequence;
import uk.ac.man.bioinf.sequence.alignment.GappedSequence;
import uk.ac.man.bioinf.sequence.alignment.event.AlignmentEvent;
import uk.ac.man.bioinf.sequence.alignment.event.AlignmentListener;
import uk.ac.man.bioinf.sequence.event.SequenceEvent;
import uk.ac.man.bioinf.sequence.event.SequenceEventType;
import uk.ac.man.bioinf.sequence.event.SequenceListener;
import uk.ac.man.bioinf.sequence.event.SequenceVetoException;
import uk.ac.man.bioinf.sequence.event.VetoableSequenceEvent;
import uk.ac.man.bioinf.sequence.event.VetoableSequenceListener;
import uk.ac.man.bioinf.sequence.identifier.Identifier;


/**
 * DefaultConsensusSequence.java
 *
 *
 * Created: Thu Jun 15 17:00:04 2000
 *
 * @author Phillip Lord
 * @version $Id: DefaultConsensusSequence.java,v 1.8 2001/04/11 17:04:41 lord Exp $
 */

public class DefaultConsensusSequence extends AbstractGappedSequence 
  implements ConsensusSequence, AlignmentListener, SequenceListener
{
  private GappedSequence currentConsensus = EmptyGappedSequence.getInstance();
  private ConsensusCalculator calculator;
  private VetoableSequenceListener seqListener;
  
  public DefaultConsensusSequence( ConsensusCalculator calculator )
  {
    super( calculator.getSequenceAlignment().getSequenceType() );
    // this constructor is actually fairly tricky as you have to get
    // things in the right order or it all falls down. 
    this.calculator = calculator;
    installAlignmentListeners( calculator );
    installVetoListeners();
    validate();
  }

  protected void installVetoListeners()
  {
    addVetoableSequenceListener
      ( seqListener =  new VetoableSequenceListener(){
	  public void changeOccurred( SequenceEvent event )
	  {
	  }
	  
	  public void vetoableChangeOccurred( VetoableSequenceEvent event ) throws SequenceVetoException
	  {
	    throw new SequenceVetoException( "Consensus sequences can't be changed except by themselves", event );
	  }
	});
  }

  protected void installAlignmentListeners( ConsensusCalculator cons )
  {
    cons.getSequenceAlignment().addSequenceListener ( this );
    cons.getSequenceAlignment().addAlignmentListener( this );
  }
  
  protected void uninstallAlignmentListners( ConsensusCalculator cons )
  {
    cons.getSequenceAlignment().removeSequenceListener ( this );
    cons.getSequenceAlignment().removeAlignmentListener( this );
  }
  
  protected void validate()
  {
    calcConsensus();
    fireChangeEvent();
  }
  
  protected void fireChangeEvent()
  {
    fireSequenceEvent( new SequenceEvent( this, 1, getGappedLength(), SequenceEventType.UNSPECIFIED ) );
  }
  
  protected void calcConsensus()
  {
    currentConsensus.removeVetoableSequenceListener( seqListener );
    currentConsensus = calculator.getCurrentConsensus();
    currentConsensus.addVetoableSequenceListener( seqListener );
  }
  
  // listener methods
  public void changeOccurred( SequenceEvent event )
  {
    validate();
  }
  
  public void changeOccurred( AlignmentEvent event )
  {
    validate();
  }
  
  // delegation methods
  public Element getElementAt( int index )
  {
    return currentConsensus.getElementAt( index );
  }
  
  
  public Element getGappedElementAt( int index )
  {
    return currentConsensus.getGappedElementAt( index );
  }
  
  public int getGappedLength()
  {
    return currentConsensus.getGappedLength();
  }
  
  public int getGappedPositionOf( int index )
  {
    return currentConsensus.getGappedPositionOf( index );
  }
  
  public Element[] getGappedSequenceAsElements()
  {
    return currentConsensus.getGappedSequenceAsElements();
  }
  
  public GappedSequence getGappedSubSequence( int start, int length )
  {
    return currentConsensus.getGappedSubSequence( start, length );
  }
  
  public int getUngappedPositionOf( int index )
  {
    return currentConsensus.getUngappedPositionOf( index );
  }
  
  public int getLength()
  {
    return currentConsensus.getLength();
  }
  
  public Element[] getSequenceAsElements()
  {
    return currentConsensus.getSequenceAsElements();
  }
  
  public Identifier getIdentifier()
  {
    return currentConsensus.getIdentifier();
  }
  
  public void setConsensusCalculator( ConsensusCalculator calculator )
  {
    this.calculator = calculator;
    validate();
  }
  
  public ConsensusCalculator getConsensusCalculator()
  {
    return calculator;
  }
  
  // gap element methods which should all fire veto's
  public void deleteGapAtQuietly( int index )
  {
  }
  
  public void insertGapAtQuietly( int index )
  {
  }

  public void destroy()
  {
    uninstallAlignmentListners( calculator );
    // we are no longer listening to the alignment so therefore we
    // want to null the calculator to allow GC, and set the current
    // consensus to empty to prevent using an out of date sequence. 
    calculator = null;
    currentConsensus = EmptyGappedSequence.getInstance();
  }
} // DefaultConsensusSequence



/*
 * ChangeLog
 * $Log: DefaultConsensusSequence.java,v $
 * Revision 1.8  2001/04/11 17:04:41  lord
 * Added License agreements to all code
 *
 * Revision 1.7  2000/12/05 15:54:37  lord
 * Import rationalisation
 *
 * Revision 1.6  2000/12/05 15:09:06  lord
 * Removed debug statement
 *
 * Revision 1.5  2000/11/08 18:26:33  lord
 * Major rewiring. Split quite a few methods to allow subclass
 * over-riding. ConsensusCalculator now has state so changes made to
 * reflect this.
 *
 * Revision 1.4  2000/09/11 14:00:13  lord
 * SequenceIdentifier renamed to Identifier
 *
 * Revision 1.3  2000/08/01 16:06:21  lord
 * Instance now tied to a specific SequenceAlignment
 *
 * Revision 1.2  2000/07/18 10:35:10  lord
 * Removed biointerface, and replaced it with identifiable
 *
 * Revision 1.1  2000/06/27 15:53:56  lord
 * Initial checkin
 *
 */
