// ============================================================================
// File:               $File$
//
// Project:            
//
// Purpose:            
//
// Author:             Rammi
//
// Copyright Notice:   (c) 2005  Rammi (rammi@caff.de)
//                     This code is in the public domain.
//                     Use at own risk.
//                     No guarantees given.
//
// Latest change:      $Date$
//
// History:	       $Log$
//=============================================================================
package de.caff.util.settings.swing;

import de.caff.i18n.swing.RJMenu;
import de.caff.util.settings.AbstractBasicLocalizablePreferenceProperty;
import de.caff.util.settings.StringListPreferenceProperty;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import java.util.prefs.Preferences;

/**
 *  Hold a list of recently opened files.
 *
 *  @author <a href="mailto:rammi@caff.de">Rammi</a>
 *  @version $Revision$
 */
public class SwingRecentFilesPreferenceProperty
  extends AbstractBasicLocalizablePreferenceProperty
  implements StringListPreferenceProperty
{
  /**
   *  Interface for loaders of recent files.
   */
  public static interface RecentFileLoader
  {
    /**
     *  Load a file selected from the recent file menu.
     *  @param path file path
     */
    public void loadRecentFile(String path);
  }

  /** The preference key suffix for the number of entries. */
  private static final String PREF_KEY_SUFFIX_NR = "NR";
  /** The maximum number of allowd entries. */
  public static int MAXIMUM_STORE_SIZE = 10;

  /** The maximum number of entries stored. */
  private final int maxSize;
  /** The list of files. */
  private List files = new LinkedList();

  /**
   *  Constructor.
   *  Uses {@link #MAXIMUM_STORE_SIZE} as allowed size for list.
   *  @param basicName  basic name (prefix)
   *  @param baseTag    basic i18n tag
   */
  public SwingRecentFilesPreferenceProperty(String basicName, String baseTag)
  {
    this(basicName, baseTag, MAXIMUM_STORE_SIZE);
  }

  /**
   *  Constructor.
   *  @param basicName  basic name (prefix)
   *  @param baseTag    basic i18n tag
   *  @param maxSize    maximum number of recent files stored, must be not more than {@link #MAXIMUM_STORE_SIZE}
   */
  public SwingRecentFilesPreferenceProperty(String basicName, String baseTag, int maxSize)
  {
    super(basicName, baseTag);
    if (maxSize > MAXIMUM_STORE_SIZE) {
      throw new IllegalArgumentException("maxSize parameter is too large");
    }
    this.maxSize = maxSize;
  }

  /**
   *  Add a file path which becomes the most recent entry.
   *  @param filepath  filepath to add
   */
  public void addEntry(String filepath)
  {
    Collection oldFiles = new ArrayList(files);
    for (ListIterator lit = files.listIterator();  lit.hasNext();  ) {
      if (filepath.equals(lit.next())) {
        lit.remove();
        break;
      }
    }
    if (files.size() == maxSize) {
      files.remove(maxSize-1);
    }
    files.add(0, filepath);
    fireValueChange(getBasicName(),
                    Collections.unmodifiableCollection(oldFiles),
                    Collections.unmodifiableCollection(files));
  }

  /**
   * Read the property value from the preferences.
   *
   * @param preferences preferences from where to read the property value
   */
  public void readFrom(Preferences preferences)
  {
    final String tagPrefix = getBasicName();
    int size = preferences.getInt(tagPrefix+PREF_KEY_SUFFIX_NR, -1);
    if (size == -1) {
      // previously unset, keep swing
    }
    else {
      if (size > maxSize) {
        size = maxSize;
      }
      List oldList = new ArrayList(files);
      files = new ArrayList(size);
      for (int index = 0;  index < size;  ++index) {
        String path = preferences.get(tagPrefix+index, null);
        if (path != null) {
          files.add(path);
        }
      }
      boolean changed;
      if (oldList.size() != files.size()) {
        changed = true;
      }
      else {
        Iterator itOld = oldList.iterator();
        Iterator itNew = files.iterator();
        changed = false;
        while (itOld.hasNext()  &&  itNew.hasNext()) {
          if (!itOld.next().equals(itNew.next())) {
            changed = true;
            break;
          }
        }
        changed = changed  ||  (itOld.hasNext() ^ itNew.hasNext());
      }
      if (changed) {
        fireValueChange(getBasicName(),
                        Collections.unmodifiableCollection(oldList),
                        Collections.unmodifiableCollection(files));
      }
    }
  }

  /**
   * Store the current property value in the preferences.
   *
   * @param preferences preferences where to store the property value
   */
  public void storeTo(Preferences preferences)
  {
    final String tagPrefix = getBasicName();
    preferences.putInt(tagPrefix+PREF_KEY_SUFFIX_NR, files.size());
    int index = 0;
    for (Iterator iterator = files.iterator(); iterator.hasNext();  ++index) {
      String url = (String)iterator.next();
      preferences.put(tagPrefix+index, url);
    }
  }

  /**
   * Get the collection of strings.
   *
   * @return String list
   */
  public Collection getStringList()
  {
    return Collections.unmodifiableCollection(files);
  }

  /**
   *  Return the entry which was added most recently.
   *  @return latest entry or <code>null</code> if no entry was saved
   */
  public String getLatestEntry()
  {
    return files.isEmpty() ? null : (String)files.get(0);
  }

  /**
   *  Get a menu item representing the list of recent files.
   *  @param loader loader which loads the files if one is selected
   *  @param l locale
   *  @return menu item
   */
  public JMenuItem getMenuItem(RecentFileLoader loader, Locale l)
  {
    SpecialMenu item = new SpecialMenu(loader, getBaseTag());
    item.setLocale(l);
    item.propertyChange(new PropertyChangeEvent(this, getBasicName(), null, files));
    addValueChangeListener(item);
    return item;
  }

  /**
   *  A special menu which shows te recent files as popup.
   */
  private static class SpecialMenu
    extends RJMenu
    implements PropertyChangeListener
  {
    /**
     *  Special menu item calling the loader when activated.
     */
    private class FileMenuItem
      extends JMenuItem
      implements ActionListener
    {
      private final String filename;

      public FileMenuItem(String number, String filename)
      {
        super(number+" "+filename);
        this.filename = filename;
        addActionListener(this);
        setMnemonic(number.charAt(0));
      }

      /**
       * Invoked when an action occurs.
       */
      public void actionPerformed(ActionEvent e)
      {
        loader.loadRecentFile(filename);
      }
    }

    /** Recent file loader to be called when file is selected. */
    private final RecentFileLoader loader;

    /**
     * Create a special menu.
     * @param loader   access to recent files
     * @param baseTag  basic I18n tag
     */
    public SpecialMenu(RecentFileLoader loader, String baseTag)
    {
      super(baseTag);
      this.loader = loader;
    }

    /**
     * This method gets called when a bound property is changed.
     *
     * @param evt A PropertyChangeEvent object describing the event source
     *            and the property that has changed.
     */
    public void propertyChange(PropertyChangeEvent evt)
    {
      removeAll();
      Collection files = (Collection)evt.getNewValue();
      int nr = 1;
      for (Iterator iterator = files.iterator(); iterator.hasNext(); ++nr) {
        String file = (String)iterator.next();
        final String number = Integer.toString(nr%10);
        add(new FileMenuItem(number, file));
      }
      setEnabled(files.size() > 0);
    }

  }
}
