require 'repertoire/exceptions/repository_exists'
require 'repertoire/exceptions/checkout_failed'
require 'repertoire/exceptions/update_failed'
require 'repertoire/repository'
require 'repertoire/media'

require 'fileutils'

module Repertoire
  #
  # Checkout the repository at the specified _options_. If a _block_
  # is given, it will be passed a newly created Repository object
  # for the checked out repository.
  #
  # _options_ must contain the following key:
  # <tt>:uri</tt>:: The URI of the repository to checkout.
  #
  # _options_ may also contain the additional keys:
  # <tt>:path</tt>:: Path to checkout the repository to.
  # <tt>:media</tt>:: The media type of the repository. Defaults to the
  #                   value of Media.get_for_uri.
  # <tt>:into</tt>:: Checkout the repository into the given directory.
  #                  Cannot be used with <tt>:path</tt>.
  #
  def Repertoire.checkout(options={},&block)
    unless options[:uri]
      raise(ArgumentError,"the :uri option must be specified",caller)
    end

    uri = options[:uri].to_s
    path = options[:path]
    into = options[:into]
    media = options[:media]

    unless path
      if into
        into = File.expand_path(into)

        unless File.directory?(into)
          FileUtils.mkdir_p(into)
        end

        path = File.join(into,Repository.name(uri))
      else
        path = Repository.name(uri)
      end
    end

    path = File.expand_path(path)
    if File.exists?(path)
      raise(RepositoryExists,"the repository #{path.dump} already exists",caller)
    end

    if media
      handler = Media.get(media)
    else
      handler = Media.guess_from_uri(uri)
    end

    begin
      handler.checkout(uri,path)
    rescue CommandFailed
      raise(CheckoutFailed,"failed to checkout the repository located at #{uri.dump}",caller)
    end

    return Repository.new(path,handler,&block)
  end

  #
  # Update the repository with the specified _options_. If a _block_
  # is given, it will be passed a newly created Repository object
  # for the updated repository.
  #
  # _options_ must contain the following keys:
  # <tt>:path</tt>:: The path of the repository to update.
  #
  # _options_ may also contain the additional keys:
  # <tt>:uri</tt>:: The URI to update against.
  # <tt>:media</tt>:: The type of the repository. Defaults to
  #                   Media.guess_from_uri if <tt>:uri</tt> is given,
  #                   otherwise Media.guess_from_path.
  #
  def Repertoire.update(options={},&block)
    unless options[:path]
      raise(ArgumentError,"the :path option must be specified",caller)
    end

    path = File.expand_path(options[:path])
    uri = options[:uri]
    media = options[:media]

    if media
      handler = Media.get(media)
    elsif uri
      handler = Media.guess_from_uri(uri)
    else
      handler = Media.guess_from_path(path)
    end

    begin
      handler.update(path,uri)
    rescue CommandFailed
      raise(UpdateFailed,"failed to update the repository at #{path.dump}",caller)
    end

    return Repository.new(path,handler,&block)
  end

  #
  # Delete the repository at the specified _path_. If a _block_ is
  # given, it will be passed the _path_ before it is deleted.
  #
  def Repertoire.delete(path,&block)
    path = File.expand_path(path)

    block.call(path) if block
    FileUtils.rm_r(path,:force => true, :secure => true)
    return nil
  end
end