/* EntryGroupPanel.java
 *
 * created: Wed Jun 21 2000
 *
 * This file is part of Artemis
 *
 * Copyright (C) 2000,2001,2002  Genome Research Limited
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /nfs/pathsoft/repository/powmap/diana/components/EntryGroupPanel.java,v 1.16 2002/09/19 10:04:00 kmr Exp $
 */

package diana.components;

import diana.*;
import diana.sequence.*;

import java.awt.*;
import java.awt.event.*;

/**
 *  A Panel that can show an EntryGroup (in some way).
 *
 *  @author Kim Rutherford <kmr@sanger.ac.uk>
 *  @version $Id: EntryGroupPanel.java,v 1.16 2002/09/19 10:04:00 kmr Exp $
 **/

abstract public class EntryGroupPanel extends CanvasPanel {
  /**
   *  Create a new EntryGroupPanel for the given EntryGroup.
   *  @param entry_group The EntryGroup that this component will display.
   *  @param selection The Selection object for this component.  Selected
   *    objects will be highlighted.
   *  @param base_plot_group The BasePlotGroup associated with this Menu -
   *    needed to call getCodonUsageAlgorithm()
   **/
  public EntryGroupPanel (final EntryGroup entry_group,
                          final Selection selection,
                          final GotoEventSource goto_event_source,
                          final BasePlotGroup base_plot_group) {
    this.entry_group       = entry_group;
    this.selection         = selection;
    this.goto_event_source = goto_event_source;
    this.base_plot_group   = base_plot_group;

    getCanvas ().addKeyListener (new KeyAdapter () {
      public void keyPressed (final KeyEvent event) {
        handleKeyPress (event);
      }
    });
  }

  /**
   *  Return an object that implements the GotoEventSource interface and which
   *  is relative to the sequence that this DisplayComponent is displaying.
   **/
  public GotoEventSource getGotoEventSource () {
    return goto_event_source;
  }

  /**
   *  Walk up through parents of this Component and return the Frame at the
   *  top.
   **/
  public Frame getParentFrame () {
    Component parent = this.getParent ();

    while (parent != null && !(parent instanceof Frame)) {
      parent = parent.getParent();
    }

    return (Frame) parent;
  }

  /**
   *  Return the EntryGroup object that this FeatureDisplay is displaying.
   **/
  public EntryGroup getEntryGroup () {
    return entry_group;
  }

  /**
   *  Returns the Selection that was passed to the constructor.
   **/
  public Selection getSelection  () {
    return selection;
  }

  /**
   *  Returns the BasePlotGroup that was passed to the constructor.
   **/
  public BasePlotGroup getBasePlotGroup  () {
    return base_plot_group;
  }

  /**
   *  This method sends an GotoEvent to all the GotoEvent listeners that will
   *  make the start of the first selected Feature or FeatureSegment visible.
   **/
  protected void makeSelectionVisible () {
    final Marker first_base = getSelection ().getStartBaseOfSelection ();

    final GotoEvent new_event = new GotoEvent (this, first_base);

    getGotoEventSource ().sendGotoEvent (new_event);
  }

  /**
   *  Return true if and only if the given MouseEvent (a mouse press) should
   *  pop up a PopupMenu.
   **/
  protected boolean isMenuTrigger (final MouseEvent event) {
    if (event.isPopupTrigger () ||
        (event.getModifiers () & InputEvent.BUTTON3_MASK) != 0) {
      return true;
    } else {
      return false;
    }
  }

  /**
   *  Handle key press events.
   **/
  private void handleKeyPress (final KeyEvent event) {
    if (!event.isShiftDown () && event.getModifiers () != 0) {
      // this is done so that menu shortcuts don't cause each action to be
      // performed twice
      return;
    }

    final FeatureVector selected_features = getSelection ().getAllFeatures ();

    if (selected_features.size () == 0) {
      final MarkerRange marker_range = getSelection ().getMarkerRange ();

      if (marker_range != null) {
        switch (event.getKeyCode ()) {
        case AddMenu.CREATE_FROM_BASE_RANGE_KEY:
          AddMenu.createFeatureFromBaseRange (getParentFrame (),
                                              getSelection (),
                                              entry_group,
                                              getGotoEventSource ());
          break;
        }
      }

    } else {
      final Feature first_selected_feature = selected_features.elementAt (0);

      final int feature_index =
        getEntryGroup ().indexOf (first_selected_feature);

      final int event_key_code = event.getKeyCode ();

      switch (event_key_code) {
      case KeyEvent.VK_UP:
      case KeyEvent.VK_DOWN:
      case KeyEvent.VK_LEFT:
      case KeyEvent.VK_RIGHT:
        {
          if ((event_key_code == KeyEvent.VK_LEFT ||
               event_key_code == KeyEvent.VK_RIGHT) &&
              !event.isShiftDown ()) {
            break;
          }
          
          final FeatureSegmentVector selected_segments =
            getSelection ().getSelectedSegments ();

          if (event.isShiftDown ()) {
            if (selected_segments.size () > 0) {
              final FeatureSegment selected_segment =
                selected_segments.elementAt (0);

              final Feature selected_segment_feature =
                selected_segment.getFeature ();

              final FeatureSegmentVector feature_segments =
                selected_segment_feature.getSegments ();

              final int segment_index =
                feature_segments.indexOf (selected_segment);

              if (event_key_code == KeyEvent.VK_UP ||
                  event_key_code == KeyEvent.VK_LEFT &&
                  !selected_segment_feature.isForwardFeature () ||
                  event_key_code == KeyEvent.VK_RIGHT &&
                  selected_segment_feature.isForwardFeature ()) {
                final int segment_count = feature_segments.size ();
                final int new_index = segment_index + 1;
                if (segment_index < segment_count - 1) {
                  selection.set (feature_segments.elementAt (new_index));
                }
              } else {
                if (segment_index > 0) {
                  final int new_index = segment_index - 1;
                  selection.set (feature_segments.elementAt (new_index));
                }
              }
            }
          } else {
            if (event_key_code == KeyEvent.VK_DOWN) {
              if (feature_index <
                  getEntryGroup ().getAllFeaturesCount () - 1) {
                selection.set (getEntryGroup ().featureAt (feature_index + 1));
                makeSelectionVisible ();
              }
            } else {
              if (feature_index > 0) {
                selection.set (getEntryGroup ().featureAt (feature_index - 1));
                makeSelectionVisible ();
              }
            }
          }
        }
        break;
      case EditMenu.EDIT_FEATURES_KEY:
        EditMenu.editSelectedFeatures (getParentFrame (),
                                       getEntryGroup (),
                                       getSelection (),
                                       getGotoEventSource ());
        break;
      case EditMenu.UNDO_KEY:
        EditMenu.undo (getParentFrame (), getEntryGroup ());
        break;
      case EditMenu.MERGE_FEATURES_KEY:
        EditMenu.mergeFeatures (getParentFrame (), getSelection (),
                                getEntryGroup ());
        break;
      case EditMenu.DUPLICATE_KEY:
        EditMenu.duplicateFeatures (getParentFrame (), getSelection (),
                                    getEntryGroup ());
        break;
      case EditMenu.DELETE_FEATURES_KEY:
        EditMenu.deleteSelectedFeatures (getParentFrame (), getSelection (),
                                         getEntryGroup ());
        break;
      case EditMenu.TRIM_FEATURES_KEY:
        EditMenu.trimSelected (getParentFrame (), getSelection (),
                               getEntryGroup (), false, true);
        break;
      case EditMenu.TRIM_FEATURES_TO_NEXT_ANY_KEY:
        EditMenu.trimSelected (getParentFrame (), getSelection (),
                               getEntryGroup (), true, true);
        break;
      case EditMenu.EXTEND_TO_PREVIOUS_STOP_CODON_KEY:
        EditMenu.extendToORF (getParentFrame (), getSelection (),
                              getEntryGroup (), false);
        break;
      case AddMenu.CREATE_FROM_BASE_RANGE_KEY:
        AddMenu.createFeatureFromBaseRange (getParentFrame (), getSelection (),
                                            entry_group,
                                            getGotoEventSource ());
        break;
      case ViewMenu.VIEW_FEATURES_KEY:
        ViewMenu.viewSelectedFeatures (getParentFrame (), getSelection ());
        break;
      case ViewMenu.PLOT_FEATURES_KEY:
        ViewMenu.plotSelectedFeatures (getParentFrame (), getSelection ());
        break;
      case ViewMenu.OVERVIEW_KEY:
        new EntryGroupInfoDisplay (getParentFrame (), entry_group);
        break;
      case ViewMenu.FASTA_IN_BROWSER_KEY:
        viewResults ("fasta", true);
        break;
      case ViewMenu.BLASTP_IN_BROWSER_KEY:
        viewResults ("blastp", true);
        break;
      case ViewMenu.VIEW_FASTA_KEY:
        viewResults ("fasta", false);
        break;
      case ViewMenu.VIEW_BLASTP_KEY:
        viewResults ("blastp", false);
        break;
      case ViewMenu.VIEW_HTH_KEY:
        viewResults ("hth", false);
        break;
      }
    }
  }

  /**
   *  Show the output file from an external program (like fasta) for the
   *  selected Feature objects.  The name of the file to read is stored in a
   *  feature qualifier.  The qualifier used is the program name plus "_file".
   *  @param send_to_browser if true the results should be sent straight to
   *    the web browser rather than using a SearchResultViewer object.
   **/
  private void viewResults (final String program_name,
                            final boolean send_to_browser) {
    final boolean sanger_options =
      Options.getOptions ().getPropertyTruthValue ("sanger_options");

    if (sanger_options && send_to_browser) {
      ViewMenu.viewExternalResults (getParentFrame (), getSelection (),
                                    program_name, true);
    } else {
      ViewMenu.viewExternalResults (getParentFrame (), getSelection (),
                                    program_name, false);
    }
  }

  /**
   *  The shortcut for View FASTA in browser.
   **/
  final int fasta_in_browser_key = KeyEvent.VK_F;

  /**
   *  The shortcut for View FASTA.
   **/
  final int view_fasta_key = KeyEvent.VK_R;

  /**
   *  The shortcut for View BLASTP in browser.
   **/
  final int blastp_in_browser_key = KeyEvent.VK_B;

  /**
   *  The shortcut for View BLASTP.
   **/
  final int view_blastp_key = KeyEvent.VK_TAB;

  /**
   *  This is the EntryGroup that this component is displaying (as set by the
   *  constructor).
   **/
  private EntryGroup entry_group;

  /**
   *  This is a reference to the Selection object that was passed to the
   *  constructor.
   **/
  private Selection selection;

  /**
   *  This is a reference to the GotoEventSource object that was passed to the
   *  constructor.
   **/
  private GotoEventSource goto_event_source;

  /**
   *  The BasePlotGroup object that was passed to the constructor.
   **/
  private BasePlotGroup base_plot_group;
}
