require 'thor'

require 'guard'
require 'guard/version'
require 'guard/dsl_describer'
require 'guard/guardfile'

module Guard

  # Facade for the Guard command line interface managed by [Thor](https://github.com/wycats/thor).
  # This is the main interface to Guard that is called by the Guard binary `bin/guard`.
  # Do not put any logic in here, create a class and delegate instead.
  #
  class CLI < Thor

    default_task :start

    desc 'start', 'Starts Guard'

    method_option :clear,
                  type:    :boolean,
                  default: false,
                  aliases: '-c',
                  banner:  'Auto clear shell before each action'

    method_option :notify,
                  type:    :boolean,
                  default: true,
                  aliases: '-n',
                  banner:  'Notifications feature'

    method_option :debug,
                  type:    :boolean,
                  default: false,
                  aliases: '-d',
                  banner:  'Show debug information'

    method_option :group,
                  type:    :array,
                  default: [],
                  aliases: '-g',
                  banner:  'Run only the passed groups'

    method_option :plugin,
                  type:    :array,
                  default: [],
                  aliases: '-P',
                  banner:  'Run only the passed plugins'

    method_option :watchdir,
                  type:    :array,
                  aliases: '-w',
                  banner:  'Specify the directories to watch'

    method_option :guardfile,
                  type:    :string,
                  aliases: '-G',
                  banner:  'Specify a Guardfile'

    method_option :no_interactions,
                  type: :boolean,
                  default: false,
                  aliases: '-i',
                  banner: 'Turn off completely any Guard terminal interactions'

    method_option :no_bundler_warning,
                  type: :boolean,
                  default: false,
                  aliases: '-B',
                  banner: 'Turn off warning when Bundler is not present'

    method_option :show_deprecations,
                  type: :boolean,
                  default: false,
                  banner: 'Turn on deprecation warnings'

    # Listen options
    method_option :latency,
                  type:    :numeric,
                  aliases: '-l',
                  banner:  'Overwrite Listen\'s default latency'

    method_option :force_polling,
                  type:    :boolean,
                  default: false,
                  aliases: '-p',
                  banner:  'Force usage of the Listen polling listener'

    # Start Guard by initializing the defined Guard plugins and watch the file system.
    # This is the default task, so calling `guard` is the same as calling `guard start`.
    #
    # @see Guard.start
    #
    def start
      _verify_bundler_presence unless options[:no_bundler_warning]
      ::Guard.start(options)

      return if ENV['GUARD_ENV'] == 'test'

      while ::Guard.running do
        sleep 0.5
      end
    end

    desc 'list', 'Lists Guard plugins that can be used with init'

    # List the Guard plugins that are available for use in your system and marks
    # those that are currently used in your `Guardfile`.
    #
    # @see Guard::DslDescriber.list
    #
    def list
      ::Guard::DslDescriber.new(options).list
    end

    desc 'notifiers', 'Lists notifiers and its options'

    # List the Notifiers for use in your system.
    #
    # @see Guard::DslDescriber.notifiers
    #
    def notifiers
      ::Guard::DslDescriber.new(options).notifiers
    end

    desc 'version', 'Show the Guard version'
    map %w(-v --version) => :version

    # Shows the current version of Guard.
    #
    # @see Guard::VERSION
    #
    def version
      puts "Guard version #{ ::Guard::VERSION }"
    end

    desc 'init [GUARDS]', 'Generates a Guardfile at the current directory (if it is not already there) and adds all installed Guard plugins or the given GUARDS into it'

    method_option :bare,
                  type: :boolean,
                  default: false,
                  aliases: '-b',
                  banner: 'Generate a bare Guardfile without adding any installed plugin into it'

    # Initializes the templates of all installed Guard plugins and adds them
    # to the `Guardfile` when no Guard name is passed. When passing
    # Guard plugin names it does the same but only for those Guard plugins.
    #
    # @see Guard::Guardfile.initialize_template
    # @see Guard::Guardfile.initialize_all_templates
    #
    # @param [Array<String>] plugin_names the name of the Guard plugins to initialize
    #
    def init(*plugin_names)
      _verify_bundler_presence

      ::Guard::Guardfile.create_guardfile(abort_on_existence: options[:bare])

      return if options[:bare]

      if plugin_names.empty?
        ::Guard::Guardfile.initialize_all_templates
      else
        plugin_names.each do |plugin_name|
          ::Guard::Guardfile.initialize_template(plugin_name)
        end
      end
    end

    desc 'show', 'Show all defined Guard plugins and their options'
    map %w(-T) => :show

    # Shows all Guard plugins and their options that are defined in
    # the `Guardfile`
    #
    # @see Guard::DslDescriber.show
    #
    def show
      ::Guard::DslDescriber.new(options).show
    end

    private

    # Verifies if Guard is run with `bundle exec` and
    # shows a hint to do so if not.
    #
    def _verify_bundler_presence
      if File.exists?('Gemfile') && !ENV['BUNDLE_GEMFILE']
        ::Guard::UI.info <<EOF

Guard here! It looks like your project has a Gemfile, yet you are running
`guard` outside of Bundler. If this is your intent, feel free to ignore this
message. Otherwise, consider using `bundle exec guard` to ensure your
dependencies are loaded correctly.
(You can run `guard` with --no-bundler-warning to get rid of this message.)
EOF
      end
    end

  end
end
