/*
 * 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.util.Hashtable;
import java.util.Map;

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

import com.mucommander.commons.runtime.OsFamily;
import com.mucommander.os.notifier.AbstractNotifier;
import com.mucommander.os.notifier.NotificationType;
import com.mucommander.text.Translator;
import com.mucommander.ui.macos.AppleScript;

/**
 * GrowlNotifier implements a notifier that uses the Growl notification system.
 *
 * <p>Growl is a third party notification system for Mac OS X, which allows Growl-enabled applications to display small,
 * unintrusive popup notifications to inform the user of noteworthy events. Growl can be found at:
 * <a href="http://growl.info">http://growl.info</a>.
 *
 * <p>This class communicates with Growl using {@link AppleScript}. More information about the AppleScript syntax can
 * be found <a href="http://growl.info/documentation/applescript-support.php"/>here</a>.
 * The Growl Java library part of the Growl SDK was previously used but it relied on the Cocoa-Java library which has
 * been deprecated by Apple since then.</p>.
 *
 * @author Maxence Bernard
 */
public class GrowlNotifier extends AbstractNotifier {
	private static final Logger LOGGER = LoggerFactory.getLogger(GrowlNotifier.class);
	
    /** Is this notifier enabled ? */
    private static boolean isEnabled;

    /** Has muCommander been registered ? */
    private static boolean isRegistered;

    /** Dictionary keys for the different notification types */
    private final static Map<NotificationType, String> NOTIFICATION_KEYS;

    /** Name of the application to be registered with Growl, as spelled in the .app */
    private final static String APP_NAME = "muCommander";

    /** This AppleScript returns "true" if Growl is currently running, "false" if it isn't */
    private final static String IS_GROWL_RUNNING_APPLESCRIPT =
            "tell application \"System Events\"\n" +
                "\tset isRunning to (count of (every process whose name is \"GrowlHelperApp\")) > 0\n" +
            "end tell";

    static {
        NOTIFICATION_KEYS = new Hashtable<NotificationType, String>();
        NOTIFICATION_KEYS.put(NotificationType.JOB_COMPLETED, "progress_dialog.job_finished");
        NOTIFICATION_KEYS.put(NotificationType.JOB_ERROR, "progress_dialog.job_error");
    }


    public GrowlNotifier() {
    }


    /**
     * Puts the given AppleScript bit inside a <code>tell application / end tell</code> block, executes the script
     * and returns <code>true</code> if it was successfully executed.
     *
     * @param appleScript the AppleScript bit to execute
     * @return true if the script was successfully executed
     */
    private static boolean tellGrowl(String appleScript) {
        return AppleScript.execute(
                "tell application \"GrowlHelperApp\"\n" +
                    "\t"+appleScript+"\n" +
                "end tell",
            null);
    }


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

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


    @Override
    public boolean setEnabled(boolean enabled) {
        if(enabled) {
            // No need to bother if the OS is not macOS
            if(!OsFamily.MAC_OS.isCurrent())
                return false;

            // Nothing else to do if the application has already been registered
            if(isRegistered)
                return (isEnabled = true);

            // Test if Growl is currently running and abort if it is not
            if(!isGrowlRunning()) {
            	LOGGER.debug("Growl is not running, aborting");

                return false;
            }

            // Register the application (muCommander) with Growl

            // The list of notification types muCommander uses
            String notificationTypes =
                "{"+
                    "\""+Translator.get(NOTIFICATION_KEYS.get(NotificationType.JOB_COMPLETED))+"\","+
                    "\""+Translator.get(NOTIFICATION_KEYS.get(NotificationType.JOB_ERROR))+"\""+
                "}";

            // Register muCommander with Growl, declare the notifications types and enable all of them by default
            isRegistered = tellGrowl(
                "register as application \""+APP_NAME+"\""+
                " all notifications "+notificationTypes+
                " default notifications "+notificationTypes+
                " icon of application \""+APP_NAME+"\"");

            LOGGER.info(isRegistered?
                "Successfully registered "+APP_NAME+" with Growl":
                "Error while registering "+APP_NAME+" with Growl");

            return isEnabled = isRegistered;
        }
        else {
            return (isEnabled = false);
        }
    }

    public static boolean isGrowlRunning() {
        StringBuilder outputBuffer = new StringBuilder();
        return AppleScript.execute(IS_GROWL_RUNNING_APPLESCRIPT, outputBuffer) && outputBuffer.toString().trim().equals("true");
    }

    @Override
    public boolean isEnabled() {
        return 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;
        }

        boolean success = tellGrowl(
            "notify with"+
            " name \""+Translator.get(NOTIFICATION_KEYS.get(notificationType))+"\""+
            " title \""+title+"\""+
            " description \""+description+"\""+
            " application name \""+APP_NAME+"\"");

        LOGGER.debug(success?
            "Notification sent successfully":
            "Error while sending notification");

        return success;
    }

// The following commented methods are implemented using the Growl Java library that comes with the Growl SDK. This
// library relies on the Cocoa-Java library which has been deprecated by Apple, which is why we're not using it anymore.
// The code has been kept for the record, in case the Growl Java library is ever used again.    

//    public boolean setEnabled(boolean enabled) {
//        if(enabled) {
//            // No need to bother if the OS is not Mac OS X
//            if(PlatformManager.getOsFamily()!=PlatformManager.MAC_OS_X)
//                return false;
//
//            // If Growl notifier has already been initialized
//            if(growl!=null) {
//                return (isEnabled = true);
//            }
//
//            try {
//                // Register the application (muCommander) and its icon. Growl doesn't seem to be able to retrieve the
//                // application's icon by itself, so we have to use some Cocoa magic to get it and feed it to Growl.
//                growl = new Growl("muCommander", com.apple.cocoa.application.NSApplication.sharedApplication().applicationIconImage().TIFFRepresentation());
//
//                String notificationTypes[] = new String[]{
//                        Translator.get(NOTIFICATION_KEYS[NOTIFICATION_TYPE_JOB_COMPLETED]),
//                        Translator.get(NOTIFICATION_KEYS[NOTIFICATION_TYPE_JOB_ERROR])
//                };
//
//                // Declare a list of available notification types
//                growl.setAllowedNotifications(notificationTypes);
//                // Declare a list of notification types enabled by default
//                growl.setDefaultNotifications(notificationTypes);
//
//                // Commit everything
//                growl.register();
//
//                AppLogger.fine("Application registered OK");
//
//                return (isEnabled = true);
//            }
//            catch(Exception e) {
//                AppLogger.fine("Exception thrown while initializing Growl support (Growl not running?)", e);
//            }
//            catch(Error e) {
//                AppLogger.fine("Error while initializing Growl support (cocoa-java not available?)", e);
//            }
//
//            growl = null;
//            return (isEnabled = false);
//        }
//        else {
//            return (isEnabled = false);
//        }
//    }
//
//    public boolean isEnabled() {
//        return growl!=null && isEnabled;
//    }
//
//    public boolean displayNotification(int notificationType, String title, String description) {
//        AppLogger.finer("notificationType="+notificationType+" title="+title+" description="+description);
//
//        if(!isEnabled()) {
//            AppLogger.fine("Ignoring notification, this notifier is not enabled");
//
//            return false;
//        }
//
//        try {
//            growl.notifyGrowlOf(Translator.get(NOTIFICATION_KEYS[notificationType]), title, description);
//            AppLogger.finer("Notification sent OK");
//
//            return true;
//        }
//        catch(Exception e) {
//            AppLogger.fine("Exception thrown while sending notification", e);
//
//            return false;
//        }
//    }

}
