module MetricFu
  class Loader
    # TODO: This class mostly serves to clean up the base MetricFu module,
    #   but needs further work

    attr_reader :loaded_files
    def initialize(lib_root)
      @lib_root = lib_root
      @loaded_files = []
    end

    def lib_require(base='',&block)
      paths = []
      base_path = File.join(@lib_root, base)
      Array((yield paths, base_path)).each do |path|
        file = File.join(base_path, *Array(path))
        require file
        if @loaded_files.include?(file)
          puts "!!!\tAlready loaded #{file}" if !!(ENV['MF_DEBUG'] =~ /true/i)
        else
          @loaded_files << file
        end
      end
    end

    # TODO: Reduce duplication of directory logic
    # Adds methods x_dir and _x_require for the directory x,
    #   relative to the metric_fu lib, to the given klass
    # @param klass [Class] the klass to add methods for the specified directories
    # @yieldreturn [Array<String>] Takes a list of directories and adds readers for each
    # @example For the directory 'metrics', which is relative to the metric_fu lib directory,
    #   creates on the klass two methods:
    #     ::metrics_dir which returns the full path
    #     ::metrics_require which takes a block of files to require relative to the metrics_dir
    def create_dirs(klass)
      class << klass
        Array(yield).each do |dir|
          define_method("#{dir}_dir") do
            File.join(lib_dir,dir)
          end
          module_eval(%Q(def #{dir}_require(&block); lib_require('#{dir}', &block); end))
        end
      end
    end

    # Adds method x_dir relative to the metric_fu artifact directory to the given klass
    #   And strips any leading non-alphanumerical character from the directory name
    # @param klass [Class] the klass to add methods for the specified artifact sub-directories
    # @yieldreturn [Array<String>] Takes a list of directories and adds readers for each
    # @example For the artifact sub-directory '_scratch', creates on the klass one method:
    #     ::scratch_dir (which notably has the leading underscore removed)
    def create_artifact_subdirs(klass)
      class << klass
        Array(yield).each do |dir|
          define_method("#{dir.gsub(/[^A-Za-z0-9]/,'')}_dir") do
            File.join(artifact_dir,dir)
          end
        end
      end
    end

    # Load specified task task only once
    #   if and only if rake is required and the task is not yet defined
    #   to prevent the task from being loaded multiple times
    # @param tasks_relative_path [String] 'metric_fu.rake' by default
    # @param options [Hash] optional task_name to check if loaded
    # @option options [String] :task_name The task_name to load, if not yet loaded
    def load_tasks(tasks_relative_path, options={task_name: ''})
      if defined?(Rake::Task) and not Rake::Task.task_defined?(options[:task_name])
        load File.join(@lib_root, 'tasks', *Array(tasks_relative_path))
      end
    end

    def setup
      MetricFu.lib_require { 'configuration' }
      MetricFu.lib_require { 'metric' }
      # TODO: consolidate these setup files
      MetricFu.lib_require { 'initial_requires' }
      # Load a few things to make our lives easier elsewhere.
      MetricFu.lib_require { 'load_files' }
      load_user_configuration
    end

    def load_user_configuration
      file = File.join(MetricFu.run_dir, '.metrics')
      load file if File.exist?(file)
    end

  end
end