require 'yaml'

class Subversion

  def initialize(opts={})
    @config = opts
  end
  
  def config
    @config
  end
  
  # Subversion understands 'HEAD' to refer to the latest revision in the
  # repository.
  def head
    "HEAD"
  end

  def repository
    config[:repository]
  end

  # Returns the command that will check out the given revision to the
  # given destination.
  def checkout(revision, destination)
    scm :checkout, config[:arguments], verbose, authentication, "-r#{revision}", repository, destination
  end

  # Returns the command that will do an "svn update" to the given
  # revision, for the working copy at the given destination.
  def sync(revision, destination)
    scm :update, config[:arguments], verbose, authentication, "-r#{revision}", destination
  end

  # Returns the command that will do an "svn export" of the given revision
  # to the given destination.
  def export(revision, destination)
    scm :export, config[:arguments], verbose, authentication, "-r#{revision}", repository, destination
  end

  # Returns the command that will do an "svn diff" for the two revisions.
  def diff(from, to=nil)
    scm :diff, repository, authentication, "-r#{from}:#{to || head}"
  end

  # Returns an "svn log" command for the two revisions.
  def log(from, to=nil)
    scm :log, repository, authentication, "-r#{from}:#{to || head}"
  end

  # Attempts to translate the given revision identifier to a "real"
  # revision. If the identifier is an integer, it will simply be returned.
  # Otherwise, this will yield a string of the commands it needs to be
  # executed (svn info), and will extract the revision from the response.
  def query_revision(revision)
    return revision if revision =~ /^\d+$/
    command = scm(:info, repository, authentication, "-r#{revision}")
    command_output = yield(command)
    result = {}
    command_output.each_line do |line|
      key, value = line.split(/:/, 2)
      result[key] = value if key && value
    end
    rev = result['Last Changed Rev'] || result['Revision']
    raise "tried to run `#{command}' and got unexpected result #{command_output}" unless rev
    rev
  end

  # Increments the given revision number and returns it.
  def next_revision(revision)
    revision.to_i + 1
  end


  private

    # If a username is configured for the SCM, return the command-line
    # switches for that. Note that we don't need to return the password
    # switch, since Capistrano will check for that prompt in the output
    # and will respond appropriately.
    def authentication
      username = config[:svn_username]
      return "" unless username
      result = "--username #{config[:svn_username]} "
      result << "--password #{config[:svn_password]} "
      result
    end

    # If verbose output is requested, return nil, otherwise return the
    # command-line switch for "quiet" ("-q").
    # FIXME: This is currently flipped logically since it doesn't check
    # for any verbosity configuration flag
    def verbose
      "-q"
    end
    
    def svn
      res = "svn"
      res = "sudo -u #{config[:user]} #{res}" if config[:user]
      res
    end        
    
    def scm(*args)
      [svn, *args].compact.join(" ")
    end

    def svn_password_prompt
      config[:svn_password]
    end
end
