/*
 * 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.SystemTray;

import javax.swing.SwingUtilities;

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

import com.mucommander.commons.runtime.OsFamily;
import com.mucommander.ui.main.WindowManager;

/**
 * AbstractNotifier is a generic representation of a system notifier. It also provides factory methods to
 * retrieve the current platform's notifier instance, if there is one.
 * <p>
 * A notifier serves the purpose of displaying notifications to the screen, to inform the user of an event when
 * the application is not visible (in the background).
 * </p>
 * <p>
 * The notifier instance returnd by {@link #getNotifier()} is platform-dependent. At this time, two notifier
 * implementations are available:
 * <ul>
 *  <li>{@link GrowlNotifier}: for macOS that is installed with Growl
 *  <li>{@link SystemTrayNotifier}: otherwise, using the java.awt.SystemTray API
 * </ul>
 * </p>
 *
 * @author Maxence Bernard
 */
public abstract class AbstractNotifier {
	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractNotifier.class);

    /** AbstractNotifier instance, null if none is available on the current platform */
    private static AbstractNotifier notifier;

    static {
        // Finds and creates a suitable AbstractNotifier instance for the platform, if there is one
        if (OsFamily.MAC_OS_X.isCurrent() && GrowlNotifier.isGrowlRunning()) {
            notifier = new GrowlNotifier();
        } else if (SystemTray.isSupported())
            notifier = new SystemTrayNotifier();
    }

    /**
     * Returns <code>true<code> if an AbstractNotifier instance is available. In other words, if <code>true</code> is
     * returned, {@link #getNotifier()} will return a non-null value.
     *
     * @return true if an AbstractNotifier instance is available
     */
    public static boolean isAvailable() {
        return notifier!=null;
    }

    /**
     * Returns an AbstractNotifier instance that can be used on the current platform, <code>null</code> if none
     * is available.
     * Note that the returned <code>AbstractNotifier</code> must be enabled before it can be used, which is not
     * guaranteed to succeed.
     *
     * @return an AbstractNotifier instance that can be used on the current platform, null if none is available
     */
    public static AbstractNotifier getNotifier() {
        return notifier;
    }


    /**
     * Displays a notification with the specified type, title and description and returns <code>true</code> if the
     * notification could be displayed. The notification will not be displayed if the current muCommander window
     * (or one of its child windows) is presently in the foreground, so that the user doesn't get notified for things
     * that he/she can already see on the screen.
     *
     * <p>
     * The notification will not be displayed if:
     * <ul>
     *  <li>muCommander is in the foreground
     *  <li>this notifier is not enabled
     *  <li>the notification could not be delivered because of an error
     * </ul>
     * </p>
     *
     * <p>
     * Note that this method is executed in a separate thread after all pending Swing events have been processed,
     * to ensure in the event of a window being made inactive that the notification will not be triggered. This method
     * immediately return s(i.e. does not wait for pending events) and thus is not be able to return if the notification
     * was displayed or not, unlike {@link #displayNotification(NotificationType, String, String)}.
     * </p>
     *
     * @param notificationType one of the available notification types, see {@link NotificationType} for possible values
     * @param title the title of the notification to display
     * @param description the description of the notification to display
     */
    public void displayBackgroundNotification(final NotificationType notificationType, final String title, final String description) {
        SwingUtilities.invokeLater(() -> {
            if(WindowManager.getCurrentMainFrame().isAncestorOfActiveWindow()) {
                LOGGER.debug("Ignoring notification, application is in foreground");
                return;
            }

            if(!displayNotification(notificationType, title, description))
                LOGGER.debug("Notification failed to be displayed");
        });
    }


    //////////////////////
    // Abstract methods //
    //////////////////////

    /**
     * Enables/disables this notifier and returns <code>true</code> if the operation succeeded. A typical case
     * for returning false, is when the underlying notification system (e.g. Growl under Mac OS X) could not be reached.
     * 
     * @param enabled true to enable this notifier, false to disable it
     * @return true if the operation succeeded
     */
    public abstract boolean setEnabled(boolean enabled);

    /**
     * Returns <code>true</code> if this notifier is enabled and ready to display notifications.
     *
     * @return true if this notifier is enabled and ready to display notifications
     */
    public abstract boolean isEnabled();

    /**
     * Displays a notification with the specified type, title and description and returns <code>true</code> if the
     * notification could be displayed. Unlike {@link #displayBackgroundNotification(NotificationType, String, String)}, the
     * notification will be attempted for display even if muCommander is currently in the foreground.
     *
     * <p>
     * Returns <code>true</code> if the notification could be displayed, <code>false</code> if:
     * <ul>
     *  <li>this notifier is not enabled
     *  <li>the notification could not be delivered because of an error
     * </ul>
     * </p>
     *
     * @param notificationType one of the available notification types, see {@link NotificationType} for possible values
     * @param title the title of the notification to display
     * @param description the description of the notification to display
     * @return true if the notification was properly sent, false otherwise
     */
    public abstract boolean displayNotification(NotificationType notificationType, String title, String description);

    /**
     * Returns a pretty name for the underlying notification system that can be displayed to the end user.
     *
     * @return a pretty name for the underlying notification system
     */
    public abstract String getPrettyName();
}
