require 'repertoire/media'
require 'repertoire/repository'

require 'uri'

module Repertoire
  class Repository

    # Path to the directory
    attr_reader :path

    # Media type of the directory
    attr_reader :media

    #
    # Creates a new Repository object with the specified _path_ and _media_
    # type. If a _block_ is given, it will be passed the newly created
    # Repository object.
    #
    #   Repository.new('path/to/repo',Media::SVN)
    #
    #   Repository.new('path/to/svn_repo',Media::SVN) do |repo|
    #     puts repo['**/']
    #   end
    #
    def initialize(path,media=nil,&block)
      @path = File.expand_path(path)

      begin
        @media = (media || Media.guess_from_path(@path))
      rescue Media::UnknownMedia
        @media = nil
      end

      block.call(self) if block
    end

    #
    # Returns the +basename+ of the specified _uri_, unless the +basename+ is
    # +'trunk'+ then the +basename+ of the parent directory within the _uri_
    # is returned.
    #
    #   Repertoire.name('http://www.today.com/is/now')
    #   # => "now"
    #
    #   Repertoire.name('svn://svn.repo.com/var/svn/awesome/trunk')
    #   # => "awesome"
    #
    def Repository.name(uri)
      uri = URI(uri.to_s)

      if uri.path.empty?
        path = File::SEPARATOR
      else
        path = File.expand_path(uri.path)
      end

      name = File.basename(path)

      if name == 'trunk'
        name = File.basename(File.dirname(path))
      elsif name =~ /\.git$/
        name.gsub!(/\.git$/,'')
      end

      if (name.empty? || name == File::SEPARATOR)
        name = uri.host
      end

      return name
    end

    #
    # Returns the name of the media used for the repository. If the media
    # could not be guessed, +nil+ will be returned.
    #
    def media_name
      return @media.name if @media
      return nil
    end

    #
    # Update the repository.
    #
    def update(uri=nil)
      if @media
        @media.update(uri)
        return true
      end

      return false
    end

    #
    # Delete the repository.
    #
    def delete
      Repertoire.delete(@path)
    end

    #
    # Similar to <tt>Dir.glob</tt>, except the media's directory is omitted
    # from the returned results. If a _block_ is given, it will be passed
    # each resulting path.
    #
    #   repo = Repository.new('path/to/my_svn')
    #   repo.glob('sub_path/.svn/*') # => []
    #   repo.glob('sub_path/**/') # => [...]
    #
    def glob(pattern,flags=0,&block)
      pattern = File.expand_path(File.join(@path,pattern))
      paths = filter(Dir.glob(pattern,flags))

      paths.each(&block) if block
      return paths
    end

    #
    # Returns +true+ if the directory contains the specified _sub_path_,
    # returns +false+ otherwise.
    #
    def has_path?(sub_path)
      !(glob(sub_path).empty?)
    end

    #
    # Returns an +Array+ of the files matching the _sub_path_ within the
    # directory.
    #
    def files(sub_path='*')
      glob(sub_path).select { |path| File.file?(path) }
    end

    #
    # Returns +true+ if there is a file with the _sub_path_ within the
    # directory, returns +false+ otherwise.
    #
    def has_file?(sub_path)
      !(files(sub_path).empty?)
    end

    #
    # Returns an +Array+ of the directories matching the _sub_path_ within
    # the directory.
    #
    def directories(sub_path='*')
      glob(sub_path).select { |path| File.directory?(path) }
    end

    #
    # Returns +true+ if there is a directory with the _sub_path_ within the
    # directory, returns +false+ otherwise.
    #
    def has_directory?(sub_path)
      !(directories(sub_path).empty?)
    end

    alias [] glob

    #
    # Returns the path of the directory in +String+ form.
    #
    def to_s
      @path.to_s
    end

    protected

    #
    # Filter the specified _paths_, removing any path that contains
    # the media's directory.
    #
    def filter(paths)
      return paths unless @media

      return paths.reject do |path|
        path.split(File::SEPARATOR).include?(@media.directory)
      end
    end

  end
end