module Reap
  require 'yaml'
  require 'rbconfig'
  require 'reap/utilities'

  # = Project
  #
  # The Project class is the main class of Reap. It provides the tools for
  # working with a project. The CLI Application class delegates to this class,
  # for instance.

  class Project
    require 'reap/metadata'
    require 'reap/settings'

    # Load up the tools
    require "reap/project/announce.rb"
    require "reap/project/check.rb"
    require "reap/project/clean.rb"
    require "reap/project/gem.rb"
    require "reap/project/html.rb"
    require "reap/project/log.rb"
    require "reap/project/make.rb"
    require "reap/project/package.rb"
    require "reap/project/publish.rb"
    require "reap/project/rdoc.rb"
    require "reap/project/release.rb"
    require "reap/project/scaffold.rb"
    require "reap/project/site.rb"
    require "reap/project/spec.rb"
    require "reap/project/stats.rb"
    require "reap/project/scm.rb"
    require "reap/project/svn.rb"
    require "reap/project/test.rb"
    require "reap/project/version.rb"

    include Utilities

    # New Project.

    def initialize(options=nil)
      @options        = options || {}
      begin
        @location       = locate
        raise LoadError, "no .reap configuration file" unless @location

        @metadata = Metadata.read(location)
        @settings = Settings.read(location, @metadata)
      rescue LoadError => e
        abort e.message.capitalize + '.'
      end
    end

    # Location of project.

    def location ; @location ; end

    # Project metadata.

    def metadata ; @metadata  ; end

    # Configuration data.

    def settings ; @settings ; end

    alias_method :configuration, :settings

    #alias_method :config, :configuration

    # Common options.

    def options  ; @options  ; end
    #alias_method :init_options, :options  # TODO: Improve me! (see stamp.rb)

    def dryrun?     ; options['dryrun']  ; end
    def trace?      ; options['trace']   ; end
    def force?      ; options['force']   ; end
    def verbose?    ; options['verbose'] ; end
    def debug?      ; options['debug']   ; end

    def dryrun=(x)  ; options['dryrun']  = x ; end
    def trace=(x)   ; options['trace']   = x ; end
    def force=(x)   ; options['force']   = x ; end
    def verbose=(x) ; options['verbose'] = x ; end
    def debug=(x)   ; options['debug']   = x ; end

    alias_method :noharm?, :dryrun?
    alias_method :noharm=, :dryrun=

    # Invoke a tool.

    def invoke(command, *args)
      #display_location
      meth = method(command)
      chdir_to_project do
        case meth.arity
        when 0
          meth.call
        else
          meth.call(*args)
        end
      end
    end

    # Display the project's root location.

    def display_location
      puts "[#{location}]"
    end

    # Change directory to project's root location,
    # and execute block if given. If a block is 
    # provided, the current directory will revert
    # back to what it was prior to this call, otherwise
    # it will remain changed.

    def chdir_to_project(&block)
      if block
        Dir.chdir(location, &block)
      else
        Dir.chdir(location)
      end
    end

    # Return a list of all ruby script in the project loadpaths.
    #def scripts
    #  @scripts ||= multiglob(*metadata.libpath.collect{ |l| File.join(l, '**', '*.rb') })
    #end

    # Query infromation about reap settings and/or the current
    # project.
    #
    # NOTE: This would dhave been naed #inspect but for the
    # built-in method.

    def introspect(options)
      args = options['arguments']
      if args
        args.each do |field|
          puts metadata.send(field)
        end
      else
        y self
      end
    end

    # Project manifest file.

    def manifest_file 
      Dir.glob('Manifest{,.txt}', File::FNM_CASEFOLD).first
    end

    private

    # Locate the project root directory. This is determined
    # by ascending up the directory tree from the current position
    # until a .reap file is found. Returns +nil+ if not found.

    def locate
      loc = nil
      dir = Dir.pwd
      while dir != '/'
        glob = File.join(dir, Settings::REAP_FILE) #Project::PROJECT_FILE)
        file = Dir.glob(glob, File::FNM_CASEFOLD).first
        if file
          loc = File.dirname(file)
          break
        else
          dir = File.dirname(dir)
        end
      end
      return loc
    end

    #

    def configure_options(options, *entries)
      options = (options || {}).rekey(&:to_s)
      entries.inject(options) do |memo, entry|
        (settings[entry] || {}).merge(memo)
      end
    end

    # Helper method for cleaning list options.
    # This will split the option on ':' or ';'
    # if it is a string, rather than an array.
    # And it will make sure there are no nil elements.

    def list_option(option)
      option = option.to_s.split(/[:;,]/) unless Array===option
      [option].compact.flatten
    end

  end

end