/* GraphMenu.java
 *
 * created: Tue Sep 18 2001
 *
 * This file is part of Artemis
 *
 * Copyright (C) 2001  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/GraphMenu.java,v 1.5 2002/11/04 17:43:10 kmr Exp $
 */

package diana.components;

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

import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.io.File;
import java.io.FileReader;
import java.util.Vector;

/**
 *  This menu controls one particular BasePlotGroup.
 *
 *  @author Kim Rutherford <kmr@sanger.ac.uk>
 *  @version $Id: GraphMenu.java,v 1.5 2002/11/04 17:43:10 kmr Exp $
 **/

public class GraphMenu extends Menu {
  /**
   *  Create a new GraphMenu object and all it's menu items.
   *  @param frame The Frame that owns this Menu.
   *  @param entry_group The EntryGroup containing the sequence to plot.
   *  @param base_plot_group The BasePlotGroup that this menu controls.
   *  @param view_menu This ViewMenu is updated when a usage plot is added.
   *  @param add_menu This AddMenu is updated when a usage plot is added.
   *  @param menu_name The name of the new menu.
   **/
  public GraphMenu (final Frame frame,
                    final EntryGroup entry_group,
                    final BasePlotGroup base_plot_group,
                    final FeatureDisplay feature_display,
                    final String menu_name) {
    super (menu_name);
    this.frame = frame;
    this.entry_group = entry_group;
    this.base_plot_group = base_plot_group;
    this.view_menu = view_menu;
    this.add_menu = add_menu;
    this.feature_display = feature_display;

    final BaseAlgorithm [] orig_algorithms =
      base_plot_group.getPlotAlgorithms ();

    final MenuItem hide_all_graphs_item = new MenuItem ("Hide All Graphs");
    hide_all_graphs_item.addActionListener (new ActionListener () {
      public void actionPerformed (ActionEvent event) {
        final BaseAlgorithm [] current_algorithms =
          base_plot_group.getPlotAlgorithms ();

        for (int i = 0 ; i < current_algorithms.length ; ++i) {
          final BaseAlgorithm this_algorithm = current_algorithms[i];

          base_plot_group.setVisibleByAlgorithm (this_algorithm, false);

          final CheckboxMenuItem this_menu_item =
            (CheckboxMenuItem) algorithm_menu_items.elementAt (i);

          this_menu_item.setState (false);
        }
        if (getParent () != null) {
          frame.validate ();
        }
      }
    });
    add (hide_all_graphs_item);

    addSeparator ();

    if (Options.isStandAlone ()) {
      final MenuItem usage_plot_item = new MenuItem ("Add Usage Plots ...");
      usage_plot_item.addActionListener (new ActionListener () {
        public void actionPerformed (ActionEvent event) {
          addUsagePlot ();
        }
      });
      add (usage_plot_item);

      final MenuItem user_plot_item = new MenuItem ("Add User Plot ...");
      user_plot_item.addActionListener (new ActionListener () {
        public void actionPerformed (ActionEvent event) {
          addUserPlot ();
        }
      });

      add (user_plot_item);

      addSeparator ();
    }

    for (int i = 0 ; i < orig_algorithms.length ; ++i) {
      final BaseAlgorithm this_algorithm = orig_algorithms[i];

      addAlgorithm (this_algorithm, false);
    }

    if (Options.getOptions ().getProperty ("codon_usage_file") != null) {
      final String codon_usage_file_name =
        Options.getOptions ().getProperty ("codon_usage_file");

      try {
        addUsagePlot (codon_usage_file_name, true, false);
        addUsagePlot (codon_usage_file_name, false, false);

        if (getParent () != null) {
          frame.validate ();
        }
      } catch (IOException e) {
        new MessageDialog (frame, "error while reading usage data: " + e);
      }
    }
  }

  /**
   *  Create a new GraphMenu object and all it's menu items.
   *  @param frame The Frame that owns this Menu.
   *  @param entry_group The EntryGroup containing the sequence to plot.
   *  @param base_plot_group The BasePlotGroup that this menu controls.
   *  @param view_menu This ViewMenu is updated when a usage plot is added.
   *  @param add_menu This AddMenu is updated when a usage plot is added.
   *  @param menu_name The name of the new menu.
   **/
  public GraphMenu (final Frame frame,
                    final EntryGroup entry_group,
                    final BasePlotGroup base_plot_group,
                    final FeatureDisplay feature_display) {
    this (frame, entry_group, base_plot_group, feature_display, "Graph");
  }

  /**
   *  Add a menu item for the given algorithm to the Display menu.
   *  @param is_visible The CheckboxMenuItem starts in the true state if and
   *    only if this is true.
   *  @return The new CheckboxMenuItem
   **/
  public CheckboxMenuItem addAlgorithm (final BaseAlgorithm algorithm,
                                        final boolean is_visible) {

    final CheckboxMenuItem new_item =
      new CheckboxMenuItem (algorithm.getAlgorithmName ());

    new_item.setState (is_visible);

    new_item.addItemListener (new ItemListener () {
      public void itemStateChanged(ItemEvent event) {
        base_plot_group.setVisibleByAlgorithm (algorithm,
                                               new_item.getState ());
      }
    });

    add (new_item);

    algorithm_menu_items.addElement (new_item);

    return new_item;
  }

  /**
   *  Ask the user for a file name, then read the codon usage data from that
   *  file, then make and add forward and a reverse BasePlot component using
   *  the data.
   **/
  public void addUsagePlot () {
    final FileDialog dialog =
      new FileDialog (Utilities.getComponentFrame (base_plot_group),
                      "Select a codon usage data file name ...",
                      FileDialog.LOAD);

    dialog.setVisible (true);

    if (dialog.getFile () != null && dialog.getFile ().length () != 0) {
      final String codon_usage_file_name =
        dialog.getDirectory () + java.io.File.separator + dialog.getFile ();

      try {
        final BasePlot new_forward_plot =
          addUsagePlot (codon_usage_file_name, true, true);
        final BasePlot new_reverse_plot =
          addUsagePlot (codon_usage_file_name, false, true);

        final Algorithm forward_algorithm = new_forward_plot.getAlgorithm ();
        final Algorithm reverse_algorithm = new_reverse_plot.getAlgorithm ();

        base_plot_group.setVisibleByAlgorithm (forward_algorithm, true);
        base_plot_group.setVisibleByAlgorithm (reverse_algorithm, true);
      } catch (IOException e) {
        new MessageDialog (Utilities.getComponentFrame (base_plot_group),
                           "error while reading usage data: " + e);
      }
    }
  }

  /**
   *  Read the codon usage data from the given file, then make and add a
   *  BasePlot component using the data.
   *  @param use_forward_strand The plot will be a forward plot if and only if
   *    this is true.
   *  @param is_visible The plot will start off visible if and only if this is
   *    true.
   *  @return The BasePlot that was added.
   **/
  private BasePlot addUsagePlot (final String codon_usage_file_name,
                                 final boolean use_forward_strand,
                                 final boolean is_visible)
      throws IOException {
    final CodonUsageAlgorithm codon_usage_algorithm;

    if (use_forward_strand) {
      final Strand forward_strand =
        entry_group.getBases ().getForwardStrand ();

      final CodonUsageWeight usage_weights =
        new CodonUsageWeight (codon_usage_file_name, forward_strand);

      codon_usage_algorithm =
        new CodonUsageAlgorithm (forward_strand, usage_weights);
    } else {
      final Strand backward_strand =
        entry_group.getBases ().getReverseStrand ();

      final CodonUsageWeight usage_weights =
        new CodonUsageWeight (codon_usage_file_name, backward_strand);

      codon_usage_algorithm =
        new CodonUsageAlgorithm (backward_strand, usage_weights);
    }

    addAlgorithm (codon_usage_algorithm, is_visible);

    final BasePlot new_plot =
      base_plot_group.addAlgorithm (codon_usage_algorithm);

    base_plot_group.setVisibleByAlgorithm (codon_usage_algorithm, is_visible);

    // XXX hack to force the BasePlot to initialise
    final DisplayAdjustmentEvent event =
      new DisplayAdjustmentEvent (this,
                                  feature_display.getFirstVisibleForwardBase (),
                                  feature_display.getLastVisibleForwardBase (),
                                  feature_display.getMaxVisibleBases (),
                                  feature_display.getScaleValue (),
                                  feature_display.getScaleFactor (),
                                  feature_display.isRevCompDisplay (),
                                  DisplayAdjustmentEvent.ALL_CHANGE_ADJUST_EVENT);

    base_plot_group.displayAdjustmentValueChanged (event);

    return new_plot;
  }

  /**
   *  Add a UserDataAlgorithm to the display.
   **/
  private void addUserPlot () {
    final FileDialog dialog =
      new FileDialog (Utilities.getComponentFrame (base_plot_group),
                      "Select a data file name ...", FileDialog.LOAD);

    dialog.setVisible (true);

    if (dialog.getFile () != null && dialog.getFile ().length () != 0) {
      final String data_file_name = dialog.getDirectory () + dialog.getFile ();

      final uk.ac.sanger.pathogens.Document document =
        new uk.ac.sanger.pathogens.FileDocument (new File (data_file_name));

      final Strand forward_strand =
        getEntryGroup ().getBases ().getForwardStrand ();

      try {
        final UserDataAlgorithm new_algorithm =
          new UserDataAlgorithm (forward_strand, document);

        final BasePlot new_base_plot =
          base_plot_group.addAlgorithm (new_algorithm);

        base_plot_group.setVisibleByAlgorithm (new_algorithm, true);

        addAlgorithm (new_algorithm, true);

        // XXX hack to force the BasePlot to initialise
        final DisplayAdjustmentEvent event =
          new DisplayAdjustmentEvent (this,
                                      feature_display.getFirstVisibleForwardBase (),
                                      feature_display.getLastVisibleForwardBase (),
                                      feature_display.getMaxVisibleBases (),
                                      feature_display.getScaleValue (),
                                      feature_display.getScaleFactor (),
                                      feature_display.isRevCompDisplay (),
                                      DisplayAdjustmentEvent.ALL_CHANGE_ADJUST_EVENT);

        base_plot_group.displayAdjustmentValueChanged (event);

        if (getParent () != null) {
          frame.validate ();
        }
      } catch (IOException e) {
        new MessageDialog (Utilities.getComponentFrame (base_plot_group),
                           "error while reading user data: " + e);
      }
    }
  }

  /**
   *  Return the Frame that was passed to the constructor.
   **/
  public Frame getParentFrame () {
    return frame;
  }

  /**
   *  Return the EntryGroup that was passed to the constructor.
   **/
  private EntryGroup getEntryGroup () {
    return entry_group;
  }

  /**
   *  The Frame reference that was passed to the constructor.
   **/
  private Frame frame;

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

  /**
   *  The EntryGroup object that was passed to the constructor.
   **/
  private EntryGroup entry_group = null;

  /**
   *  This ViewMenu is updated when a usage plot is added.
   **/
  private ViewMenu view_menu;

  /**
   *  This AddMenu is updated when a usage plot is added.
   **/
  private AddMenu add_menu;

  /**
   *  The FeatureDisplay that was passed to the constructor.
   **/
  private FeatureDisplay feature_display;

  /**
   *  This list of the CheckboxMenuItems for the graphs is stored so that we
   *  can turn them all off with "Hide All Graphs".
   **/
  private Vector algorithm_menu_items = new Vector ();
}
