#######################################################################
#                  Licensing Information                              #
#                                                                     #
#  The following code is a derivative work of the code from the       #
#  Mercurial project, which is licensed GPLv2. This code therefore    #
#  is also licensed under the terms of the GNU Public License,        #
#  verison 2.                                                         #
#                                                                     #
#  For information on the license of this code when distributed       #
#  with and used in conjunction with the other modules in the         #
#  Amp project, please see the root-level LICENSE file.               #
#                                                                     #
#  © Michael J. Edgar and Ari Brown, 2009-2010                        #
#                                                                     #
#######################################################################

command :import do |c|
  c.workflow :hg
  c.desc "Import an ordered set of patches"
  c.help <<-EOS
amp import [options]+ [FILE]+
  
  Import a list of patches and commit them individually.

  If there are outstanding changes in the working directory, import
  will abort unless given the -f flag.

  You can import a patch straight from a mail message. Even patches
  as attachments work (body part must be type text/plain or
  text/x-patch to be used). From and Subject headers of email
  message are used as default committer and commit message. All
  text/plain body parts before first diff are added to commit
  message.

  If the imported patch was generated by amp export, user and description
  from patch override values from message headers and body. Values
  given on command line with -m and -u override these.

  If --exact is specified, import will set the working directory
  to the parent of each patch before applying it, and will abort
  if the resulting changeset has a different ID than the one
  recorded in the patch. This may happen due to character set
  problems or other deficiencies in the text patch format.

  With --similarity, amp will attempt to discover renames and copies
  in the patch in the same way as 'addremove'.

  To read a patch from standard input, use patch name "-".
  
  Where options are:
EOS
  
  c.on_run do |opts, args|
    require 'date'
    require 'open-uri'
    
    repo = opts[:repository]
    patches = args
    
    opts[:date] &&= DateTime.parse(opts[:date])
    opts[:similarity] = Float(opts[:similarity] || 0)
    
    if opts[:similarity] < 0 || opts[:similarity] > 100
      raise abort('similarity must be between 0 and 100')
    end
    
    if opts[:exact] || !opts[:force]
      raise abort("Outstanding changes or uncommitted merges exist") if repo.changed?
    end
    
    d     = opts[:base]
    strip = opts[:strip]
    repo.lock_working_and_store do
      patches.each do |patch|
        patch_file = File.join d, patch
        
        if patch_file == '-'
          Amp::UI.status 'applying patch from STDIN'
          patch_file = $stdin
        else
          Amp::UI.status "applying #{patch_file}"
          patch_file = Kernel.open patch_file # uses open-uri's version of #open
        end
        
        data = Amp::Patch.extract patch_file
        # python uses an array for this:
        #  tmpname, message, user, date, branch, nodeid, p1, p2 = *data
        # WRONG BITCH! We're using a hash
        raise abort('no patch found') if data[:tmp_name].nil?
        
        begin
          message = if (msg = c.log_message opts[:message], opts[:log_file])
                      msg
                    elsif !data[:message].empty?
                      data[:message].strip
                    end # defaults to nil
          Amp::UI.debug "message: #{message}"
          
          wp = repo.parents
          if opts[:exact]
            raise abort('not a mercurial patch') unless data[:node_id] && data[:p1]
            p1 = repo.lookup data[:p1]
            p2 = repo.lookup data[:p2] || Amp::Mercurial::RevlogSupport::Node::NULL_ID.hexlify
            
            repo.update(p1, false, true, nil).success? if p1 != wp.first.node
            repo.dirstate.parents = [p1, p2]
          elsif p2
            begin
              p1 = repo.lookup p1
              p2 = repo.lookup p2
              repo.dirstate.parents = [p1, p2] if p1 == wp[0].node
            rescue Amp::RepoError
              # Do nothing...
            end
          end
          
          if opts[:exact] || opts[:"import-branch"]
            repo.dirstate.branch = data[:branch] || 'default'
          end
          
          files = {}
          begin
            fuzz = Amp::Patch.patch data[:tmp_name], :strip => data[:strip],
                                                     :cwd   => repo.root   ,
                                                     :file  => files
          ensure
            files = Amp::Patch.update_dir repo, files, opts.only(:similarity)
          end
          
          unless opts[:"no-commit"]
            n = repo.commit files, :message => message                   ,
                                   :user    => opts[:user] || data[:user],
                                   :date    => opts[:date] || data[:date]
            if opts[:exact]
              if n.hexlify != data[:node_id]
                repo.rollback!
                raise abort('patch is damaged or loses information')
              end
            end
            
            # Force a dirstate write so that the next transaction
            # backups an up-do-date file.
            repo.dirstate.write
          end
        ensure
          File.safe_unlink data[:tmp_name]
        end
      end
    end
  end  # end on_run
end