require 'ore/exceptions/invalid_metadata'
require 'ore/versions/version'
require 'ore/dependency'

module Ore
  #
  # A mixin for {Project} which provides methods for normalizing and
  # setting project attributes.
  #
  module Settings
    protected

    #
    # Sets the version of the project.
    #
    # @param [Hash<Integer>, String] version
    #   The version from the metadata file.
    #
    # @raise [InvalidVersion]
    #   The version must either be a `String` or a `Hash`.
    #
    def set_version!(version)
      case version
      when Hash
        major = version['major']
        minor = version['minor']
        patch = version['patch']
        build = version['build']

        @version = Versions::Version.new(major,minor,patch,build)
      when String
        @version = Versions::Version.parse(version.to_s)
      else
        raise(InvalidMetadata,"version must be a Hash or a String")
      end
    end

    #
    # Sets the license(s) of the project.
    #
    # @param [Array, String] license
    #   The license(s) of the project.
    #
    def set_license!(license)
      case license
      when Array
        @licenses += license
      else
        @licenses << license
      end
    end

    #
    # Sets the authors of the project.
    #
    # @param [Array<String>, String] authors
    #   The authors listed in the metadata file.
    #
    def set_authors!(authors)
      case authors
      when Array
        @authors += authors
      else
        @authors << authors
      end
    end

    #
    # Sets the email contacts of the project.
    #
    # @param [Array<String>, String] emails
    #   The email addresses listed in the metadata file.
    #
    # @since 0.1.3
    #
    def set_emails!(emails)
      case emails
      when Array
        @emails += emails
      else
        @emails << emails
      end
    end

    #
    # Sets the release date of the project.
    #
    # @param [String] date
    #   The release date from the metadata file.
    #
    def set_date!(date)
      @date = Date.parse(date)
    end

    #
    # Sets the require-paths of the project.
    #
    # @param [Array<String>, String] paths
    #   The require-paths or the glob-pattern listed in the metadata file.
    #
    def set_require_paths!(paths)
      each_path(paths) { |path| add_require_path(path) }
    end

    #
    # Sets the executables of the project.
    #
    # @param [Array<String>, String]
    #   The executable names or the glob-pattern listed in the metadata
    #   file.
    #   
    def set_executables!(paths)
      each_path(paths) { |path| add_executable(path) }
    end

    #
    # Sets the default executable of the project.
    #
    # @param [String] name
    #   The default executable name listed in the metadata file.
    #
    def set_default_executable!(name)
      if @executables.include?(name)
        @default_executable = name
      else
        warn "#{name} is not in the executables list"
      end
    end

    #
    # Sets the extra documentation files of the project.
    #
    # @param [Array<String>, String] paths
    #   The file paths or the glob-pattern listed in the metadata file.
    #
    def set_extra_doc_files!(paths)
      each_path(paths) { |path| add_extra_doc_file(path) }
    end

    #
    # Sets the files of the project.
    #
    # @param [Array<String>, String] paths
    #   The files or the glob-pattern listed in the metadata file.
    #
    def set_files!(paths)
      each_path(paths) { |path| add_file(path) }
    end

    #
    # Sets the test-files of the project.
    #
    # @param [Array<String>, String] paths
    #   The test-files of the glob-pattern listed in the metadata file.
    #
    def set_test_files!(paths)
      each_path(paths) { |path| add_test_file(path) }
    end

    #
    # Sets the external requirements of the project.
    #
    # @param [Array, String] requirements
    #   The external requirements.
    #
    # @since 0.2.0
    #
    def set_requirements!(requirements)
      case requirements
      when Array
        @requirements += requirements
      else
        @requirements << requirements
      end
    end

    #
    # Sets the dependencies of the project.
    #
    # @param [Hash{String => String}] dependencies
    #   The dependency names and versions listed in the metadata file.
    #
    # @raise [InvalidMetadata]
    #   The dependencies must be a `Hash`.
    #
    def set_dependencies!(dependencies)
      unless dependencies.kind_of?(Hash)
        raise(InvalidMetadata,"dependencies must be a Hash")
      end

      dependencies.each do |name,versions|
        @dependencies << Dependency.parse_versions(name,versions)
      end
    end

    #
    # Sets the runtime-dependencies of the project.
    #
    # @param [Hash{String => String}] dependencies
    #   The runtime-dependency names and versions listed in the metadata
    #   file.
    #
    # @raise [InvalidMetadata]
    #   The runtime-dependencies must be a `Hash`.
    #
    def set_runtime_dependencies!(dependencies)
      unless dependencies.kind_of?(Hash)
        raise(InvalidMetadata,"runtime_dependencies must be a Hash")
      end

      dependencies.each do |name,versions|
        @runtime_dependencies << Dependency.parse_versions(name,versions)
      end
    end

    #
    # Sets the development-dependencies of the project.
    #
    # @param [Hash{String => String}] dependencies
    #   The development-dependency names and versions listed in the
    #   metadata file.
    #
    # @raise [InvalidMetadata]
    #   The development-dependencies must be a `Hash`.
    #
    def set_development_dependencies!(dependencies)
      unless dependencies.kind_of?(Hash)
        raise(InvalidMetadata,"development_dependencies must be a Hash")
      end

      dependencies.each do |name,versions|
        @development_dependencies << Dependency.parse_versions(name,versions)
      end
    end
  end
end