/*
 * This file is part of muCommander, http://www.mucommander.com
 *
 * muCommander 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 3 of the License, or
 * (at your option) any later version.
 *
 * muCommander 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, see <http://www.gnu.org/licenses/>.
 */

package com.mucommander.ui.notifier;

import java.awt.Dimension;
import java.awt.Image;
import java.awt.Menu;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.Hashtable;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mucommander.commons.runtime.JavaVersion;
import com.mucommander.ui.action.AWTActionProxy;
import com.mucommander.ui.action.ActionProperties;
import com.mucommander.ui.action.MuAction;
import com.mucommander.ui.action.impl.BringAllToFrontAction;
import com.mucommander.ui.action.impl.NewWindowAction;
import com.mucommander.ui.action.impl.QuitAction;
import com.mucommander.ui.icon.IconManager;
import com.mucommander.ui.main.WindowManager;

/**
 * SystemTrayNotifier implements a notifier that uses the System Tray to display notifications. When enabled, this
 * notifier displays an icon in the systrem tray that recalls the current {@link com.mucommander.ui.main.MainFrame}
 * when double-clicked, or shows a popup menu with additional actions ('Bring all to front', 'Quit') when right-clicked.
 *
 * <p>This notifier is available only with Java 1.6 and up.</p>
 *
 * @author Maxence Bernard
 */
public class SystemTrayNotifier extends AbstractNotifier implements ActionListener {
	private static final Logger LOGGER = LoggerFactory.getLogger(SystemTrayNotifier.class);
	
    /** TrayIcon being displayed in the system tray, null when this notifier is not enabled */
    private TrayIcon trayIcon;

    /** Is this notifier enabled ? */
    private boolean isEnabled;

    /** Name of the tray icon image */
    private final static String TRAY_ICON_NAME = "icon16_8.png";

    /** Width of the muCommander tray icon */
    private final static int TRAY_ICON_WIDTH = 16;

    /** Height of the muCommander tray icon */
    private final static int TRAY_ICON_HEIGHT = 16;

    /** System tray message types for the different notification types */
    private final static Map<NotificationType, TrayIcon.MessageType> MESSAGE_TYPES;

    static {
        MESSAGE_TYPES = new Hashtable<NotificationType, TrayIcon.MessageType>();

        MESSAGE_TYPES.put(NotificationType.JOB_COMPLETED, TrayIcon.MessageType.INFO);
        MESSAGE_TYPES.put(NotificationType.JOB_ERROR, TrayIcon.MessageType.ERROR);
    }

    SystemTrayNotifier() {
    }

    /**
     * Creates and adds a menu item that triggers the MuAction denoted by the given Class. The menu item's label
     * is set to the value returned by {@link MuAction#getLabel()}.
     */
    private void addMenuItem(Menu menu, String muActionId) {
        MenuItem menuItem = new MenuItem(ActionProperties.getActionLabel(muActionId));
        menuItem.addActionListener(new AWTActionProxy(muActionId));
        menu.add(menuItem);
    }


    /////////////////////////////////////
    // AbstractNotifier implementation //
    /////////////////////////////////////

    @Override
    public boolean setEnabled(boolean enabled) {
        if(enabled) {
            // No need to bother if the current Java runtime version is not 1.6 or up, or if SystemTray is not available
            if(JavaVersion.JAVA_1_6.isCurrentLower() || !SystemTray.isSupported())
                return false;

            // If System Tray has already been initialized
            if(trayIcon!=null) {
                return (isEnabled = true);
            }

            SystemTray systemTray = SystemTray.getSystemTray();

            Image iconImage = IconManager.getIcon(IconManager.MUCOMMANDER_ICON_SET, TRAY_ICON_NAME).getImage();
            Dimension trayIconSize = systemTray.getTrayIconSize();
            // If the sytem tray icon size is larger than the icon size, center the icon as the default is to display
            // the icon in the top left corner which is plain ugly
            if(trayIconSize.width>TRAY_ICON_WIDTH || trayIconSize.height>TRAY_ICON_HEIGHT) {
                // The buffered image uses ARGB for transparency
                BufferedImage bi = new BufferedImage(trayIconSize.width, trayIconSize.height, BufferedImage.TYPE_INT_ARGB);
                bi.getGraphics().drawImage(iconImage, (trayIconSize.width-TRAY_ICON_WIDTH)/2, (trayIconSize.height-TRAY_ICON_HEIGHT)/2, null);
                iconImage = bi;
            }

            // Create the tray icon and disable image auto-size which shouldn't be used anyway but just in case
            trayIcon = new TrayIcon(iconImage);
            trayIcon.setImageAutoSize(false);

            // Create the popup (AWT!) menu. Note there is no way with java.awt.Menu to know when the menu is selected
            // and thus it makes it hard to have contextual menu items such as the list of open windows.
            PopupMenu menu = new PopupMenu();
            addMenuItem(menu, NewWindowAction.Descriptor.ACTION_ID);
            addMenuItem(menu, BringAllToFrontAction.Descriptor.ACTION_ID);
            menu.addSeparator();
            addMenuItem(menu, QuitAction.Descriptor.ACTION_ID);

            trayIcon.setPopupMenu(menu);

            // Add the tray icon to the system tray. If an exception is caught, clean things up and leave this notifier
            // disabled.
            try {
                systemTray.add(trayIcon);
                // Tray icon was added OK, listen to action events
                trayIcon.addActionListener(this);

                return (isEnabled = true);
            }
            catch(java.awt.AWTException e) {
                trayIcon = null;

                return (isEnabled = false);
            }
        }
        else {
            if(trayIcon!=null) {
                // Remove tray icon from the system tray
                SystemTray.getSystemTray().remove(trayIcon);
                trayIcon.removeActionListener(this);

                trayIcon = null;
            }

            return (isEnabled = false);
        }
    }

    @Override
    public boolean isEnabled() {
        return trayIcon!=null && isEnabled;
    }

    @Override
    public boolean displayNotification(NotificationType notificationType, String title, String description) {
        LOGGER.debug("notificationType="+notificationType+" title="+title+" description="+description);

        if(!isEnabled()) {
            LOGGER.debug("Ignoring notification, this notifier is not enabled");

            return false;
        }

        trayIcon.displayMessage(title, description, MESSAGE_TYPES.get(notificationType));
        return true;
    }

    @Override
    public String getPrettyName() {
        return "System Tray";
    }


    ///////////////////////////////////
    // ActionListener implementation //
    ///////////////////////////////////

    public void actionPerformed(ActionEvent actionEvent) {
        LOGGER.trace("caught SystemTray ActionEvent");

        WindowManager.getCurrentMainFrame().toFront();
    }


    ////////////////////////
    // Overridden methods //
    ////////////////////////

    @Override
    protected void finalize() throws Throwable {
        // This ensures that the system tray icon is removed when the application terminates.
        // Even though this is a bit of a shot in the dark, this may fix a problem reported under Linux where the
        // tray icon stayed after the application had quit:
        /// http://www.mucommander.com/forums/viewtopic.php?t=604
        if(isEnabled())
            setEnabled(false);

        super.finalize();
    }
}
