/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2003-2006 RSSOwl Development Team                                  **
 **   http://www.rssowl.org/                                                 **
 **                                                                          **
 **   All rights reserved                                                    **
 **                                                                          **
 **   This program and the accompanying materials are made available under   **
 **   the terms of the Eclipse Public License 1.0 which accompanies this     **
 **   distribution, and is available at:                                     **
 **   http://www.rssowl.org/legal/epl-v10.html                               **
 **                                                                          **
 **   A copy is found in the file epl-v10.html and important notices to the  **
 **   license from the team is found in the textfile LICENSE.txt distributed **
 **   in this package.                                                       **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Contributors:                                                          **
 **     RSSOwl - initial API and implementation (bpasero@rssowl.org)         **
 **                                                                          **
 **  **********************************************************************  */

package net.sourceforge.rssowl.controller.tray;

import net.sourceforge.rssowl.controller.DisposeListenerImpl;
import net.sourceforge.rssowl.controller.GUI;
import net.sourceforge.rssowl.controller.MenuManager;
import net.sourceforge.rssowl.util.GlobalSettings;
import net.sourceforge.rssowl.util.shop.PaintShop;
import net.sourceforge.rssowl.util.shop.WidgetShop;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tray;
import org.eclipse.swt.widgets.TrayItem;

/**
 * RSSOwl is supporting a simple system tray menu that allows to re-open and to
 * exit RSSOwl. Supported on Windows and Linux.
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class SystemTray {

  /** Remember the status of teasing in the tray */
  public static boolean trayIsTeasing = false;

  /** Minimum time between two Tray Alerts in Miliseconds */
  private static final int MIN_POPUP_GAP = 10000;

  /** Delay the restoring of the application window */
  private static final int RESTORE_DELAY = 100;

  private Display display;
  private MenuItem exitItem;
  private MenuItem markAllReadItem;
  private MenuItem openItem;
  private long popupTimeLock;
  private MenuItem reloadAllItem;
  private SystemTrayAlert rssOwlTrayPopup;
  private Listener shellIconifyListener;
  private Tray systemTray;
  boolean isMinimizedToTray;
  GUI rssOwlGui;
  Shell shell;
  TrayItem systemTrayItem;
  Menu systemTrayItemMenu;

  /**
   * Instantiate a new SystemTray
   * 
   * @param display The display
   * @param shell The shell
   * @param rssOwlGui The MainController
   */
  public SystemTray(Display display, Shell shell, GUI rssOwlGui) {
    this.rssOwlGui = rssOwlGui;
    this.display = display;
    this.shell = shell;
    isMinimizedToTray = false;
    initComponents();

    /** Create the popup if required */
    if (GlobalSettings.showTrayPopup)
      SystemTrayAlert.getInstance(display, this);
  }

  /** Disable systray icon */
  public void disable() {
    if (WidgetShop.isset(shell))
      shell.removeListener(SWT.Iconify, shellIconifyListener);
  }

  /**
   * Get wether RSSOwl is minimized to system tray
   * 
   * @return boolean TRUE if minimized to tray
   */
  public boolean isMinimizedToTray() {
    return isMinimizedToTray;
  }

  /**
   * Set the Popup Time Lock to 0 in order to have it reset.
   */
  public void resetPopupTimeLock() {
    popupTimeLock = 0;
  }

  /** Hide tray item and show RSSOwl */
  public void restoreWindow() {

    /** Close the Tray-Popup if showing */
    if (rssOwlTrayPopup != null && !rssOwlTrayPopup.isPopupClosed())
      rssOwlTrayPopup.hide();

    /** setVisible() only supported on win32 */
    if (GlobalSettings.isWindows())
      systemTrayItem.setVisible(false);

    /** Restore the Shell */
    if (WidgetShop.isset(shell)) {
      shell.setVisible(true);
      shell.setActive();
      shell.setMinimized(false);
    }

    /** Reset flag */
    isMinimizedToTray = false;
  }

  /**
   * Set the trayitem state to either teasing or not.
   * 
   * @param teasing Set to TRUE to indicate unread news are available
   */
  public void setTrayItemState(boolean teasing) {

    /** Show popup if unread news are available */
    if (teasing) {

      /**
       * If the Tray was not teasing before, show the Tray Popup immediately.<br>
       * In case the Tray was already teasing, make sure to let some seconds
       * pass before showing the next Popup.
       */
      long curTimeMillis = System.currentTimeMillis();
      if (trayIsTeasing != teasing || curTimeMillis - popupTimeLock > MIN_POPUP_GAP) {
        showTrayPopup();
        popupTimeLock = curTimeMillis;
      }
    }

    /** Status did not change, so return */
    if (trayIsTeasing == teasing && systemTrayItem.getImage() != null)
      return;

    /** Update flag */
    trayIsTeasing = teasing;

    /** Dispose old image */
    if (systemTrayItem.getImage() != null)
      systemTrayItem.getImage().dispose();

    /** Set new image */
    systemTrayItem.setImage(PaintShop.loadImage((teasing == true) ? "/img/trayowl_tease.gif" : "/img/trayowl.gif"));

    /** Set Tooltip */
    systemTrayItem.setToolTipText((teasing == true) ? "RSSOwl - " + GUI.i18n.getTranslation("TOOLTIP_UNREAD_AVAILABLE") : "RSSOwl");
  }

  /**
   * @see net.sourceforge.rssowl.util.i18n.ITranslatable#updateI18N()
   */
  public void updateI18N() {

    /** Set labels */
    if (openItem != null)
      openItem.setText(GUI.i18n.getTranslation("SYSTRAY_SHOW"));

    if (reloadAllItem != null)
      reloadAllItem.setText(GUI.i18n.getTranslation("BUTTON_RELOAD_ALL"));

    if (markAllReadItem != null)
      markAllReadItem.setText(GUI.i18n.getTranslation("BUTTON_MARK_ALL_READ"));

    if (exitItem != null)
      exitItem.setText(GUI.i18n.getTranslation("MENU_EXIT"));

    /** Update Tooltip */
    systemTrayItem.setToolTipText((trayIsTeasing == true) ? "RSSOwl - " + GUI.i18n.getTranslation("TOOLTIP_UNREAD_AVAILABLE") : "RSSOwl");

    /** Init Mnemonics */
    if (WidgetShop.isset(systemTrayItemMenu))
      MenuManager.initMnemonics(systemTrayItemMenu);
  }

  /** Init the System Tray Menu */
  private void initComponents() {

    /** System Tray */
    systemTray = display.getSystemTray();

    /** System Tray Item for RSSOwl */
    systemTrayItem = new TrayItem(systemTray, SWT.NONE);
    setTrayItemState(trayIsTeasing);

    /** setVisible() only supported on win32 */
    if (GlobalSettings.isWindows())
      systemTrayItem.setVisible(false);

    /** Show the tray item menu on SWT.MenuDetect */
    systemTrayItem.addListener(SWT.MenuDetect, new Listener() {
      public void handleEvent(Event event) {
        if (WidgetShop.isset(systemTrayItemMenu))
          systemTrayItemMenu.setVisible(true);
      }
    });

    /** Restore Window upon default selection */
    systemTrayItem.addSelectionListener(new SelectionAdapter() {
      public void widgetDefaultSelected(SelectionEvent e) {

        /** Restore Window if minimized to tray */
        if (isMinimizedToTray)

          /**
           * Workaround for Bug 106299 (Double-Click in system tray forwarded to
           * adjacent icon). Delay the restoring of the application window.
           */
          GUI.display.timerExec(RESTORE_DELAY, new Runnable() {
            public void run() {
              restoreWindow();
            }
          });

        /** Minimize Window if visible and OS is Linux */
        else if (GlobalSettings.isLinux())
          minimizeWindow();
      }
    });

    /** System Tray Item Menu */
    systemTrayItemMenu = new Menu(shell, SWT.POP_UP);

    /** MenuItem: Reload All */
    reloadAllItem = new MenuItem(systemTrayItemMenu, SWT.NONE);
    if (!GlobalSettings.isMac()) {
      reloadAllItem.setImage(PaintShop.loadImage("/img/icons/reload_all.gif"));
      reloadAllItem.addDisposeListener(DisposeListenerImpl.getInstance());
    }
    reloadAllItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        rssOwlGui.getEventManager().actionReloadAllCategories();
      }
    });

    /** Separator */
    new MenuItem(systemTrayItemMenu, SWT.SEPARATOR);

    /** MenuItem: Mark All Read */
    markAllReadItem = new MenuItem(systemTrayItemMenu, SWT.NONE);
    if (!GlobalSettings.isMac()) {
      markAllReadItem.setImage(PaintShop.loadImage("/img/icons/mark_read.gif"));
      markAllReadItem.addDisposeListener(DisposeListenerImpl.getInstance());
    }
    markAllReadItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        rssOwlGui.getEventManager().actionMarkAllCategoriesRead();
      }
    });

    /** Separator */
    new MenuItem(systemTrayItemMenu, SWT.SEPARATOR);

    /** MenuItem: Show RSSOwl */
    openItem = new MenuItem(systemTrayItemMenu, SWT.NONE);
    if (!GlobalSettings.isMac()) {
      openItem.setImage(PaintShop.loadImage("/img/icons/maximize.gif"));
      openItem.addDisposeListener(DisposeListenerImpl.getInstance());
    }
    openItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        restoreWindow();
      }
    });

    /** Set the open item as default item */
    systemTrayItemMenu.setDefaultItem(openItem);

    /** Separator */
    new MenuItem(systemTrayItemMenu, SWT.SEPARATOR);

    /** MenuItem: Exit RSSOwl */
    exitItem = new MenuItem(systemTrayItemMenu, SWT.NONE);
    exitItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {

        /**
         * On Linux, the Tray is even showing when the Application is visible.
         * In that case, save the Shell Bounds to Settings.
         */
        if (GlobalSettings.isLinux() && !isMinimizedToTray) {
          GlobalSettings.shellBounds = shell.getBounds();
          GlobalSettings.isShellMaximized = shell.getMaximized();
        }

        /** Shutdown RSSOwl, do not save bounds */
        GUI.isClosing = true;
        shell.dispose();
      }
    });

    /** Set the labels */
    updateI18N();

    /** Listen for the iconify event */
    shellIconifyListener = new Listener() {
      public void handleEvent(Event event) {
        minimizeWindow();
      }
    };
    shell.addListener(SWT.Iconify, shellIconifyListener);
  }

  /**
   * Show Tray Popup if:
   * <p>- GlobalSettings allow it (GlobalSettings.showTrayPopup = TRUE)
   * <p>- Unread news are available (teasing = TRUE)
   * <p>- RSSOwl is minimized to the tray and not visible (isMinimizedToTray)
   * <p>- No tray popup was made before (rssOwlTrayPopup = NULL), or
   * <p>- The previous tray popup was already closed
   */
  private void showTrayPopup() {
    if (GlobalSettings.showTrayPopup && isMinimizedToTray && (rssOwlTrayPopup == null || rssOwlTrayPopup.isPopupClosed())) {
      rssOwlTrayPopup = SystemTrayAlert.getInstance(display, this);
      rssOwlTrayPopup.show();
    }
  }

  /**
   * Minimize RSSOwl to the system tray
   */
  void minimizeWindow() {

    /** setVisible() only supported on win32 */
    if (GlobalSettings.isWindows())
      systemTrayItem.setVisible(true);

    shell.setVisible(false);
    isMinimizedToTray = true;
  }
}