require 'rbconfig'
require 'pathname'
require 'guard/ui'

module Guard

  # The notifier class handles cross-platform system notifications that supports:
  # - Growl on Mac OS X
  # - Libnotify on Linux
  # - Notifu on Windows
  #
  module Notifier

    # Application name as shown in the specific notification settings
    APPLICATION_NAME = "Guard"

    # Turn notifications of.
    #
    def self.turn_off
      ENV["GUARD_NOTIFY"] = 'false'
    end

    # Turn notifications on. This tries to load the platform
    # specific notification library.
    #
    # @return [Boolean] whether the notification could be enabled.
    #
    def self.turn_on
      ENV["GUARD_NOTIFY"] = 'true'
      case RbConfig::CONFIG['target_os']
      when /darwin/i
        require_growl
      when /linux/i
        require_libnotify
      when /mswin|mingw/i
        require_rbnotifu
      end
    end

    # Show a message with the system notification.
    #
    # @see .image_path
    #
    # @param [String] the message to show
    # @param [Hash] options the notification options
    # @option options [Symbol, String] image the image symbol or path to an image
    # @option options [String] title the notification title
    #
    def self.notify(message, options = {})
      if enabled?
        image = options.delete(:image) || :success
        title = options.delete(:title) || "Guard"

        case RbConfig::CONFIG['target_os']
        when /darwin/i
          notify_mac(title, message, image, options)
        when /linux/i
          notify_linux(title, message, image, options)
        when /mswin|mingw/i
          notify_windows(title, message, image, options)
        end
      end
    end

    # Test if the notifications are enabled and available.
    #
    # @return [Boolean] whether the notifications are available
    #
    def self.enabled?
      ENV["GUARD_NOTIFY"] == 'true'
    end

  private

    # Send a message to Growl either with the growl gem or the growl_notify gem.
    #
    # @param [String] title the notification title
    # @param [String] message the message to show
    # @param [Symbol, String] the image to user
    # @param [Hash] options the growl options
    #
    def self.notify_mac(title, message, image, options)
      require_growl # need for guard-rspec formatter that is called out of guard scope

      default_options = { :title => title, :icon => image_path(image), :name => APPLICATION_NAME }
      default_options.merge!(options)

      if defined?(GrowlNotify)
        default_options[:description] = message
        default_options[:application_name] = APPLICATION_NAME
        default_options.delete(:name)

        GrowlNotify.send_notification(default_options) if enabled?
      else
        Growl.notify message, default_options.merge(options) if enabled?
      end
    end

    # Send a message to libnotify.
    #
    # @param [String] title the notification title
    # @param [String] message the message to show
    # @param [Symbol, String] the image to user
    # @param [Hash] options the libnotify options
    #
    def self.notify_linux(title, message, image, options)
      require_libnotify # need for guard-rspec formatter that is called out of guard scope
      default_options = { :body => message, :summary => title, :icon_path => image_path(image), :transient => true }
      Libnotify.show default_options.merge(options) if enabled?
    end

    # Send a message to notifu.
    #
    # @param [String] title the notification title
    # @param [String] message the message to show
    # @param [Symbol, String] the image to user
    # @param [Hash] options the notifu options
    #
    def self.notify_windows(title, message, image, options)
      require_rbnotifu # need for guard-rspec formatter that is called out of guard scope
      default_options = { :message => message, :title => title, :type => image_level(image), :time => 3 }
      Notifu.show default_options.merge(options) if enabled?
    end

    # Get the image path for an image symbol.
    #
    # Known symbols are:
    # - failed
    # - pending
    # - success
    #
    # @param [Symbol] image the image name
    # @return [String] the image path
    #
    def self.image_path(image)
      images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
      case image
      when :failed
        images_path.join("failed.png").to_s
      when :pending
        images_path.join("pending.png").to_s
      when :success
        images_path.join("success.png").to_s
      else
        # path given
        image
      end
    end

    # The notification level type for the given image.
    #
    # @param [Symbol] image the image
    # @return [Symbol] the level
    #
    def self.image_level(image)
      case image
      when :failed
        :error
      when :pending
        :warn
      when :success
        :info
      else
        :info
      end
    end

    # Try to safely load growl and turns notifications
    # off on load failure.
    #
    def self.require_growl
      begin
        require 'growl_notify'

        if GrowlNotify.application_name != APPLICATION_NAME
          GrowlNotify.config do |c|
            c.notifications = c.default_notifications = [ APPLICATION_NAME ]
            c.application_name = c.notifications.first
          end
        end
      rescue LoadError
        require 'growl'
      end
    rescue LoadError
      turn_off
      UI.info "Please install growl_notify or growl gem for Mac OS X notification support and add it to your Gemfile"
    end

    # Try to safely load libnotify and turns notifications
    # off on load failure.
    #
    def self.require_libnotify
      require 'libnotify'
    rescue LoadError
      turn_off
      UI.info "Please install libnotify gem for Linux notification support and add it to your Gemfile"
    end

    # Try to safely load rb-notifu and turns notifications
    # off on load failure.
    #
    def self.require_rbnotifu
      require 'rb-notifu'
    rescue LoadError
      turn_off
      UI.info "Please install rb-notifu gem for Windows notification support and add it to your Gemfile"
    end

  end
end
