/* EntryFileDialog.java
 *
 * created: Mon Dec  7 1998
 *
 * This file is part of Artemis
 *
 * Copyright (C) 1998,1999,2000  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/disk222/yeastpub/Repository/powmap/diana/components/EntryFileDialog.java,v 1.25 2000/07/24 11:08:02 kmr Exp $
 */

package diana.components;

import uk.ac.sanger.pathogens.*;
import uk.ac.sanger.pathogens.embl.Entry;
import uk.ac.sanger.pathogens.embl.DocumentEntryFactory;
import uk.ac.sanger.pathogens.embl.ReadFormatException;
import uk.ac.sanger.pathogens.embl.EntryInformation;
import uk.ac.sanger.pathogens.embl.EntryInformationException;
import uk.ac.sanger.pathogens.embl.InvalidRelationException;
import uk.ac.sanger.pathogens.embl.InvalidKeyException;
import uk.ac.sanger.pathogens.embl.InvalidQualifierException;
import uk.ac.sanger.pathogens.embl.SimpleEntryInformation;
import uk.ac.sanger.pathogens.embl.Key;
import uk.ac.sanger.pathogens.embl.Qualifier;
import uk.ac.sanger.pathogens.embl.QualifierInfo;
import uk.ac.sanger.pathogens.embl.QualifierInfoException;

import java.awt.*;
import java.io.*;

/**
 *  This class is a FileDialog that can read EMBL Entry objects.
 *
 *  @author Kim Rutherford
 *  @version $Id: EntryFileDialog.java,v 1.25 2000/07/24 11:08:02 kmr Exp $
 **/

public class EntryFileDialog extends FileDialog {
  /**
   *  Create a new EntryFileDialog component.
   *  @param owner The component where this dialog was created.
   **/
  public EntryFileDialog (final Frame owner) {
    super (owner, "Select an EMBL file...", FileDialog.LOAD);
    this.owner = owner;
  }

  /**
   *  Return an uk.ac.sanger.pathogens.embl.Entry object representing the file
   *  the user has selected with the dialog or null if the read failed for any
   *  reason.
   *  @param entry_information The EntryInformation to use when reading.  This
   *    supplies the list of valid keys and qualifiers.  If a key or qualifier
   *    is read that is incompatible with this EntryInformation object the
   *    EntryInformation will be changed to cope.
   *  @param listener The object to which InputStreamProgressEvents will be
   *    send while reading.
   *  @exception EntryInformationException Thrown if an Entry using the given
   *    EntryInformation object cannot contain the Key, Qualifier or
   *    Key/Qualifier combination of one of the features in the Document.
   **/
  public Entry getEntry (final EntryInformation entry_information,
                         final InputStreamProgressListener listener) {
    show ();

    if (getDirectory () == null || getFile () == null) {
      return null;
    }

    final File file = new File (getDirectory (), getFile ());

    return getEntryFromFile (owner, file, entry_information, listener);
  }

  /**
   *  This exists only to get around a bug in the 1.1/1.2 code generator,
   *  which generates unverifiable code.
   *  @param frame Used when creating MessageDialog components.
   *  @param entry_file The file to read the Entry from.
   *  @param file_document The Document to read the Entry from (should be made
   *    from entry_file).
   *  @param entry_information The EntryInformation to use when reading.  This
   *    supplies the list of valid keys and qualifiers.  If a key or qualifier
   *    is read that is incompatible with this EntryInformation object the
   *    EntryInformation will be changed to cope.
   *  @param errors One String is added to this for each error.
   **/
  private static Entry
    getEntryFromFileHelper (final Frame frame,
                            final File entry_file,
                            final FileDocument file_document,
                            final EntryInformation entry_information,
                            final StringVector errors)
      throws ReadFormatException, IOException {
    while (errors.size () < 10) {
      try {
        if (entry_file != null) {
          final Entry new_entry =
            DocumentEntryFactory.makeDocumentEntry (entry_information,
                                                    file_document);

          return new_entry;
        } else {
          new MessageDialog (frame,
                             "creating new entry: " + entry_file.getName ());

          final Entry new_entry =
            DocumentEntryFactory.makeDocumentEntry (file_document);

          return new_entry;
        }
      } catch (EntryInformationException e) {
        final String message = e.getMessage ();

        errors.add (message);

        if (errors.size () == 1) {
          final YesNoDialog yes_no_dialog =
            new YesNoDialog (frame,
                             message + " - ignore error and continue?");

          if (!yes_no_dialog.getResult ()) {
            return null;
          }
        }

        handleOpenException (entry_information, e);
      }
    }

    new MessageDialog (frame,
                       "aborted - too many errors while reading: " +
                       entry_file.getName ());

    return null;
  }

  /**
   *  Read and return an Entry from the given File.
   *  @param frame Used when creating MessageDialog components.
   *  @param entry_file The file to read the Entry from.
   *  @param entry_information The EntryInformation to use when reading.  This
   *    supplies the list of valid keys and qualifiers.  If a key or qualifier
   *    is read that is incompatible with this EntryInformation object the
   *    EntryInformation will be changed to cope.
   *  @param listener The object to which InputStreamProgressEvents will be
   *    send while reading.
   **/
  public static Entry
    getEntryFromFile (final Frame frame,
                      final File entry_file,
                      final EntryInformation entry_information,
                      final InputStreamProgressListener listener) {
    StringVector errors = new StringVector ();

    try {
      final FileDocument file_document =
        new FileProgressDocument (entry_file, listener);

      return getEntryFromFileHelper (frame, entry_file, file_document,
                                     entry_information, errors);
    } catch (ReadFormatException e) {
      final String message =
        "failed to open " + entry_file.getName () + ": " + e.getMessage () +
        " at line: " + e.getLineNumber ();
      System.out.println (message);
      new MessageDialog (frame, message);
    } catch (FileNotFoundException e) {
      final String message =
        "failed to open " + entry_file.getName () + ": file not found";
      new MessageDialog (frame, message);
    } catch (IOException e) {
      final String message =
        "failed to open " + entry_file.getName () + ": " + e.getMessage ();
      new MessageDialog (frame, message);
    } finally {
      if (errors.size () > 1) {
        final YesNoDialog dialog =
          new YesNoDialog (frame,
                           "There were warnings while reading.  " +
                           "View them now?");

        if (dialog.getResult ()) {
          final FileViewer viewer =
            new FileViewer ("Read errors from " + entry_file.getName ());

          final StringBuffer buffer = new StringBuffer ();

          for (int i = 0 ; i < errors.size () ; ++i) {
            buffer.append (errors.elementAt (i) + "\n");
          }

          viewer.clear ();
          viewer.appendString (buffer.toString ());
        }
      }
    }

    return null;
  }

  /**
   *  Fix entry_information so that the given exception won't happen again.
   **/
  public static void
    handleOpenException (final EntryInformation entry_information,
                         final EntryInformationException exception) {
    if (exception instanceof InvalidKeyException) {
      final InvalidKeyException key_exception =
        (InvalidKeyException) exception;

      entry_information.addKey (key_exception.getKey ());
    }

    if (exception instanceof InvalidRelationException) {
      final InvalidRelationException relation_exception =
        (InvalidRelationException) exception;

      if (relation_exception.getQualifier () == null) {
        final Key key_to_add = relation_exception.getKey ();

        entry_information.addKey (key_to_add);
      } else {
        final Qualifier qualifier = relation_exception.getQualifier ();

        // change the QualifierInfo object in the EntryInformation so that it
        // can handle this key/qualifier combination

        final QualifierInfo qualifier_info =
          entry_information.getQualifierInfo (qualifier.getName ());

        final QualifierInfo new_qualifier_info;

        if (qualifier_info == null) {
          new_qualifier_info =
            new QualifierInfo (qualifier.getName (),
                               QualifierInfo.UNKNOWN, null, null, false);
        } else {
          new_qualifier_info =
            new QualifierInfo (qualifier_info.getName (),
                               qualifier_info.getType (), null, null, false);
        }

        try {
          entry_information.addQualifierInfo (new_qualifier_info);
        } catch (QualifierInfoException e) {
          throw new Error ("internal error - unexpected exception: " + e);
        }
      }
    }

    if (exception instanceof InvalidQualifierException) {
      final String exception_qualifier_name =
        ((InvalidQualifierException) exception).getQualifier ().getName ();

      // add it again, but make it less restrictive
      final QualifierInfo new_info =
        new QualifierInfo (exception_qualifier_name, QualifierInfo.UNKNOWN,
                           null, null, false);

      try {
        entry_information.addQualifierInfo (new_info);
      } catch (QualifierInfoException e) {
        throw new Error ("internal error - unexpected exception: " + e);
      }
    }
  }

//    /**
//     *  Read and return the Entry with in the given Document or show a message if
//     *  there is no document with that name.
//     *  @param new_name The Document to read from.
//     *  @return The reference of the new embl.Entry or null if the read failed.
//     **/
//    public static Entry openEntry (final String new_name) {
//      try {
//        if (! new_document.readable ()) {
//          // need to create a new Document before continuing
//          new MessageDialog (this, "cannot open this file: " + new_entry_name);
//          return null;
//        }

//        final EntryInformation new_entry_information =
//          new SimpleEntryInformation (Options.getArtemisEntryInformation ());

//        boolean seen_error = false;

//        while (true) {
//          try {
//            final Entry new_entry =
//              DocumentEntryFactory.makeDocumentEntry (new_entry_information,
//                                                      new_document);
//            return new_entry;
//          } catch (EntryInformationException e) {

//            if (!seen_error) {
//              final String message =
//                "warning while reading " + new_entry_name + " - " +
//                e.getMessage ();

//              System.err.println (message);

//              new MessageDialog (this, message);

//              seen_error = true;
//            }

//            EntryFileDialog.handleOpenException (new_entry_information, e);

//            // go around the loop again
//          }
//        }


//      } catch (ReadFormatException e) {
//        final String message =
//          "failed to open " + new_entry_name + " - " +
//          e.getMessage () + " at line: " + e.getLineNumber ();
//        System.err.println (message);
//        new MessageDialog (this, message).setVisible (true);
//      } catch (IOException e) {
//        final String message =
//          "failed to open: " + e.getMessage ();
//        getStatusLabel ().setText (message);
//        new MessageDialog (this, message);
//      }

//      return null;
//    }

  /**
   *  The Frame reference that was passed to the constructor.
   **/
  private Frame owner = null;
}
