# encoding: utf-8

module RuboCop
  module Cop
    module Style
      # This cop looks for uses of Perl-style global variables.
      class SpecialGlobalVars < Cop
        MSG_BOTH = 'Prefer `%s` from the English library, or `%s` over `%s`.'
        MSG_ENGLISH = 'Prefer `%s` from the English library over `%s`.'
        MSG_REGULAR = 'Prefer `%s` over `%s`.'

        PREFERRED_VARS = {
          '$:' => ['$LOAD_PATH'],
          '$"' => ['$LOADED_FEATURES'],
          '$0' => ['$PROGRAM_NAME'],
          '$!' => ['$ERROR_INFO'],
          '$@' => ['$ERROR_POSITION'],
          '$;' => ['$FIELD_SEPARATOR', '$FS'],
          '$,' => ['$OUTPUT_FIELD_SEPARATOR', '$OFS'],
          '$/' => ['$INPUT_RECORD_SEPARATOR', '$RS'],
          '$\\' => ['$OUTPUT_RECORD_SEPARATOR', '$ORS'],
          '$.' => ['$INPUT_LINE_NUMBER', '$NR'],
          '$_' => ['$LAST_READ_LINE'],
          '$>' => ['$DEFAULT_OUTPUT'],
          '$<' => ['$DEFAULT_INPUT'],
          '$$' => ['$PROCESS_ID', '$PID'],
          '$?' => ['$CHILD_STATUS'],
          '$~' => ['$LAST_MATCH_INFO'],
          '$=' => ['$IGNORECASE'],
          '$*' => ['$ARGV', 'ARGV'],
          '$&' => ['$MATCH'],
          '$`' => ['$PREMATCH'],
          '$\'' => ['$POSTMATCH'],
          '$+' => ['$LAST_PAREN_MATCH']
        }.symbolize_keys

        # Anything *not* in this set is provided by the English library.
        NON_ENGLISH_VARS = Set.new([
          '$LOAD_PATH',
          '$LOADED_FEATURES',
          '$PROGRAM_NAME',
          'ARGV'
        ])

        def on_gvar(node)
          global_var, = *node

          add_offense(node, :expression) if PREFERRED_VARS[global_var]
        end

        def message(node)
          global_var, = *node

          regular, english = PREFERRED_VARS[global_var].partition do |var|
            NON_ENGLISH_VARS.include? var
          end

          # For now, we assume that lists are 2 items or less. Easy grammar!
          regular_msg = regular.join('` or `')
          english_msg = english.join('` or `')

          if regular.length > 0 && english.length > 0
            format(MSG_BOTH, english_msg, regular_msg, global_var)
          elsif regular.length > 0
            format(MSG_REGULAR, regular_msg, global_var)
          elsif english.length > 0
            format(MSG_ENGLISH, english_msg, global_var)
          else
            fail 'Bug in SpecialGlobalVars - global var w/o preferred vars!'
          end
        end

        def autocorrect(node)
          @corrections << lambda do |corrector|
            global_var, = *node

            corrector.replace(node.loc.expression,
                              PREFERRED_VARS[global_var].first)
          end
        end
      end
    end
  end
end
