require "English"
require "tempfile"
require "svn/error"
require "svn/util"
require "svn/core"
require "svn/repos"
require "svn/ext/ra"

module Svn
  module Ra
    Util.set_constants(Ext::Ra, self)
    Util.set_methods(Ext::Ra, self)

    @@ra_pool = Svn::Core::Pool.new
    Ra.initialize(@@ra_pool)

    class << self
      def modules
        print_modules("")
      end
    end
    
    Session = SWIG::TYPE_p_svn_ra_session_t

    class Session
      class << self
        def open(url, config={}, callbacks=nil)
          Ra.open2(url, callbacks, config)
        end
      end

      def latest_revnum
        Ra.get_latest_revnum(self)
      end

      def dated_revision(time)
        Ra.get_dated_revision(self, time.to_apr_time)
      end

      def set_prop(name, value, rev=nil)
        Ra.change_rev_prop(self, rev || latest_revnum, name, value)
      end

      def proplist(rev=nil)
        Ra.rev_proplist(self, rev || latest_revnum)
      end

      def prop(name, rev=nil)
        Ra.rev_prop(self, rev || latest_revnum, name)
      end

      def commit_editor(log_msg, lock_tokens={}, keep_lock=false)
        callback = Proc.new do |new_revision, date, author|
          date = Time.from_svn_format(date) if date
          yield(new_revision, date, author)
        end
        editor, editor_baton = Ra.get_commit_editor(self, log_msg, callback,
                                                    lock_tokens, keep_lock)
        editor.baton = editor_baton
        [editor, editor_baton]
      end

      def commit_editor2(log_msg, lock_tokens={}, keep_lock=false, &callback)
        editor, editor_baton = Ra.get_commit_editor2(self, log_msg, callback,
                                                     lock_tokens, keep_lock)
        editor.baton = editor_baton
        editor
      end

      def file(path, rev=nil)
        output = StringIO.new
        rev ||= latest_revnum
        fetched_rev, props = Ra.get_file(self, path, rev, output)
        output.rewind
        props_filter(props)
        [output.read, props]
      end

      def dir(path, rev=nil, fields=nil)
        rev ||= latest_revnum
        fields ||= Core::DIRENT_ALL
        entries, fetched_rev, props = Ra.get_dir2(self, path, rev, fields)
        props_filter(props)
        [entries, props]
      end

      def update(revision_to_update_to, update_target,
                 editor, editor_baton, recurse=true, &block)
        editor.baton = editor_baton
        update2(revision_to_update_to, update_target,
                editor, recurse, &block)
      end

      def update2(revision_to_update_to, update_target, editor, recurse=true)
        reporter, reporter_baton = Ra.do_update(self, revision_to_update_to,
                                                update_target, recurse,
                                                editor)
        reporter.baton = reporter_baton
        if block_given?
          yield(reporter)
          reporter.finish_report
          nil
        else
          reporter
        end
      end

      def switch(revision_to_switch_to, switch_target, switch_url,
                 editor, editor_baton, recurse=true, &block)
        editor.baton = editor_baton
        switch2(revision_to_switch_to, switch_target, switch_url,
                editor, recurse, &block)
      end

      def switch2(revision_to_switch_to, switch_target, switch_url,
                  editor, recurse=true)
        reporter, reporter_baton = Ra.do_switch(self, revision_to_switch_to,
                                                switch_target, recurse,
                                                switch_url, editor)
        reporter.baton = reporter_baton
        if block_given?
          yield(reporter)
          reporter.finish_report
          nil
        else
          reporter
        end
      end

      def status(revision, status_target, editor, editor_baton,
                 recurse=true, &block)
        editor.baton = editor_baton
        status2(revision, status_target, editor, recurse, &block)
      end

      def status2(revision, status_target, editor, recurse=true)
        reporter, reporter_baton = Ra.do_status(self, status_target,
                                                revision, recurse, editor)
        reporter.baton = reporter_baton
        if block_given?
          yield(reporter)
          reporter.finish_report
          nil
        else
          reporter
        end
      end

      def diff(rev, target, versus_url, editor,
               recurse=true, ignore_ancestry=true, text_deltas=true)
        args = [self, rev, target, recurse, ignore_ancestry,
                text_deltas, versus_url, editor]
        reporter, baton = Ra.do_diff2(*args)
        reporter.baton = baton
        reporter
      end

      def log(paths, start_rev, end_rev, limit,
              discover_changed_paths=true,
              strict_node_history=false)
        paths = [paths] unless paths.is_a?(Array)
        receiver = Proc.new do |changed_paths, revision, author, date, message|
          date = Time.parse_svn_format(date) if date
          yield(changed_paths, revision, author, date, message)
        end
        Ra.get_log(self, paths, start_rev, end_rev, limit,
                   discover_changed_paths, strict_node_history,
                   receiver)
      end

      def check_path(path, rev=nil)
        Ra.check_path(self, path, rev || latest_revnum)
      end

      def stat(path, rev=nil)
        Ra.stat(self, path, rev || latest_revnum)
      end

      def uuid
        Ra.uuid(self)
      end

      def repos_root
        Ra.get_repos_root(self)
      end

      def locations(path, location_revisions, peg_revision=nil)
        peg_revision ||= latest_revnum
        Ra.get_locations(self, path, peg_revision, location_revisions)
      end

      def file_revs(path, start_rev, end_rev=nil)
        end_rev ||= latest_revnum
        revs = []
        handler = Proc.new do |path, rev, rev_props, prop_diffs|
          revs << [path, rev, rev_props, prop_diffs]
          yield(path, rev, rev_props, prop_diffs) if block_given?
        end
        Ra.get_file_revs(self, path, start_rev, end_rev, handler)
        revs
      end

      def lock(path_revs, comment=nil, steal_lock=false)
        lock_func = Proc.new do |path, do_lock, lock, ra_err|
          yield(path, do_lock, lock, ra_err)
        end
        Ra.lock(self, path_revs, comment, steal_lock, lock_func)
      end

      def unlock(path_tokens, break_lock=false, &lock_func)
        Ra.unlock(self, path_tokens, break_lock, lock_func)
      end

      def get_lock(path)
        Ra.get_lock(self, path)
      end

      def get_locks(path)
        Ra.get_locks(self, path)
      end

      def replay(rev, low_water_mark, editor, send_deltas=true)
        Ra.replay(self, rev, low_water_mark, send_deltas, editor)
      end

      def reparent(url)
        Ra.reparent(self, url)
      end

      private
      def props_filter(props)
        date_str = props[Svn::Core::PROP_ENTRY_COMMITTED_DATE]
        if date_str
          date = Time.parse_svn_format(date_str)
          props[Svn::Core::PROP_ENTRY_COMMITTED_DATE] = date
        end
        props
      end
    end

    class Reporter2
      attr_accessor :baton

      def set_path(path, revision, start_empty=true, lock_token=nil)
        Ra.reporter2_invoke_set_path(self, @baton, path, revision,
                                     start_empty, lock_token)
      end
      
      def delete_path(path)
        Ra.reporter2_invoke_set_path(self, @baton, path)
      end
      
      def link_path(path, url, revision, start_empty=true, lock_token=nil)
        Ra.reporter2_invoke_link_path(self, @baton, path, url,
                                      revision, start_empty, lock_token)
      end

      def finish_report
        Ra.reporter2_invoke_finish_report(self, @baton)
      end

      def abort_report
        Ra.reporter2_invoke_abort_report(self, @baton)
      end
      
    end

    remove_const(:Callbacks)
    class Callbacks
      attr_accessor :auth_baton
      def initialize(auth_baton)
        @auth_baton = auth_baton
      end

      def open_tmp_file
        tmp = Tempfile.new("Svn::Ra")
        path = tmp.path
        tmp.close(true)
        path
      end

      def get_wc_prop(relpath, name)
        nil
      end

      def set_wc_prop(path, name, value)
      end

      def push_wc_prop(path, name, value)
      end

      def invalidate_wc_props(path, name)
      end

      def progress_func(progress, total)
      end
    end
  end
end
