require 'pp'
require 'yaml'

require 'roodi/core/checking_visitor'
require 'roodi/core/parser'
require 'roodi/core/sexp'

module Roodi
  module Core
    class Runner
      attr_writer :config
      attr_reader :files_checked

      def initialize(*checks)
        @config = default_config
        @checks = checks unless checks.empty?
      end

      def default_config
        project_config ? project_config : roodi_gem_config
      end

      def roodi_gem_config
        File.join(File.dirname(__FILE__), "..", "..", "..", "roodi.yml")
      end

      def project_config
        File.exists?("roodi.yml") ? "roodi.yml" : nil
      end

      def start(paths)
        puts "\nRunning Roodi checks"

        paths = ['.'] if paths == []
        all_files = collect_files(paths)
        @files_checked = all_files.count
        all_files.each do |path|
          check_file(path)
        end

        output_result(errors, @files_checked)
      end

      def output_result(errors, files_checked)
        errors.each {|error| puts "\e[31m#{error}\e[0m"}

        puts "\nChecked #{files_checked} files"
        result = "Found #{errors.size} errors."
        if errors.empty?
          puts "\e[32m#{result}\e[0m"
        else
          raise "\e[31m#{result}\e[0m"
        end
      end

      def collect_files(paths)
        files = []
        paths.each do |path|
          if File.file?(path)
            files << path
          elsif File.directory?(path)
            files += Dir.glob(File.join(path, '**/*.{rb}'))
          else
            files += Dir.glob(path)
          end
        end
        files
      end

      def check(filename, content)
        @checks ||= load_checks
        @checker ||= CheckingVisitor.new(@checks)
        @checks.each {|check| check.start_file(filename)}
        node = parse(filename, content)
        node.accept(@checker) if node
        @checks.each {|check| check.end_file(filename)}
      end

      def check_content(content, filename = "dummy-file.rb")
        check(filename, content)
      end

      def check_file(filename)
        return unless File.exists?(filename)
        check(filename, File.read(filename))
      end

      def print(filename, content)
        node = parse(filename, content)
        pp node
      end

      def print_content(content)
        print("dummy-file.rb", content)
      end

      def print_file(filename)
        print(filename, File.read(filename))
      end

      def errors
        @checks ||= []
        all_errors = @checks.collect {|check| check.errors}
        all_errors.flatten
        all_errors.flatten + parsing_errors
      end

      private

      def parse(filename, content)
        begin
          Parser.new.parse(content, filename)
        rescue Exception
          parsing_errors << "#{filename} looks like it's not a valid Ruby file."
          nil
        end
      end

      def parsing_errors
        @parsing_errors ||= []
      end

      def load_checks
        check_objects = []
        checks = load_config(@config)
        checks.each do |check_class_name, options|
          check_class = Roodi::Checks.const_get(check_class_name)
          check_objects << check_class.make(options || {})
        end
        check_objects
      end

      def load_config(config_file)
        YAML.load_file config_file
      end
    end
  end
end
