from optparse import make_option
import os

from rbtools.api.errors import APIError
from rbtools.commands import Command
from rbtools.utils.filesystem import make_tempfile


class Patch(Command):
    """Applies a specific patch from a RB server.

    The patch file indicated by the request id is downloaded from the
    server and then applied locally."""
    name = "patch"
    author = "John Sintal"
    option_list = [
        make_option("--diff-revision",
                    dest="diff_revision",
                    default=None,
                    help="revision id of diff to be used as patch"),
        make_option("--px",
                    dest="px",
                    default=None,
                    help="numerical pX argument for patch"),
        make_option("--server",
                    dest="server",
                    metavar="SERVER",
                    help="specify a different Review Board server to use"),
        make_option("-d", "--debug",
                    action="store_true",
                    dest="debug",
                    help="display debug output"),
    ]

    def __init__(self):
        super(Patch, self).__init__()
        self.option_defaults = {
            'server': self.config.get('REVIEWBOARD_URL', None),
            'username': self.config.get('USERNAME', None),
            'password': self.config.get('PASSWORD', None),
            'debug': self.config.get('DEBUG', False),
        }

    def get_patch(self, request_id, patch_id=None):
        """Given a review request ID request_id and a diff/patch ID
        patch_id, return the patch as a string, the used patch ID,
        and its basedir.

        If patch ID is not specified, then this will look at the most
        recent patch."""
        try:
            diffs = self.root_resource \
                .get_review_requests() \
                .get_item(request_id) \
                .get_diffs()
        except APIError, e:
            die("Error getting diffs: %s" % (e))

        # Use the latest patch if a patch ID was not given.
        if not patch_id:
            patch_id = diffs.total_results

        try:
            diff = diffs.get_item(patch_id).get_patch().diff
            base_dir = diffs.get_item(patch_id).basedir
        except APIError:
            die('The specified diff revision does not exist.')

        return diff, patch_id, base_dir

    def apply_patch(self, request_id, patch_id, patch_file, base_dir):
        """Apply patch patch_file and display results to user."""
        print "Patch is being applied to request %s with patch revision" \
              " %s." % (request_id, patch_id)
        self.tool.apply_patch(patch_file, self.repository_info.base_path,
                              base_dir, self.options.px)

    def main(self, request_id, *args):
        """Run the command."""
        self.repository_info, self.tool = self.initialize_scm_tool()
        server_url = self.get_server_url(self.repository_info, self.tool)
        self.root_resource = self.get_root(server_url)

        # Get the patch, the used patch ID and base dir for the diff
        patch, patch_id, base_dir = self.get_patch(request_id,
                                                   self.options.
                                                   diff_revision)

        tmp_patch_file = make_tempfile(patch)

        self.apply_patch(request_id, patch_id, tmp_patch_file, base_dir)

        os.remove(tmp_patch_file)

@@ -1482,24 +1482,29 @@
 est_id, 
-patch_id
+diff_revision
 =None):%0A
@@ -1544,56 +1544,36 @@
  ID 
-request_id and a diff/patch ID%0A        patch_id,
+and a diff revision,%0A       
  ret
@@ -1580,21 +1580,20 @@
 urn the 
-patch
+diff
  as a st
@@ -1607,24 +1607,29 @@
 he used 
-patch ID
+diff revision
 ,%0A      
@@ -1659,24 +1659,31 @@
      If 
-patch ID
+a diff revision
  is not 
@@ -1707,16 +1707,24 @@
 his will
+%0A       
  look at
@@ -1736,30 +1736,30 @@
 most
-%0A        recent patch.
+ recent diff.%0A        
 %22%22%22%0A
@@ -2029,27 +2029,31 @@
 est 
-patch
+diff
  if a 
-patch ID
+diff revision
  was
@@ -2076,45 +2076,237 @@
     
-if not patch_id:%0A            patch_id
+# Since diff revisions start a 1, increment by one, and%0A        # never skip a number, the latest diff revisions number%0A        # should be equal to the number of diffs.%0A        if diff_revision is None:%0A            diff_revision
  = d
@@ -2372,25 +2372,59 @@
 et_item(
-patch_id)
+diff_revision)%0A            diff_body = diff
 .get_pat
@@ -2464,28 +2464,8 @@
 diff
-s.get_item(patch_id)
 .bas
@@ -2577,26 +2577,36 @@
 urn diff
-, patch_id
+_body, diff_revision
 , base_d
@@ -2651,28 +2651,37 @@
 id, 
-patch_id, patch_file
+diff_revision, diff_file_path
 , ba
@@ -2793,18 +2793,20 @@
 applied 
-to
+from
  request
@@ -2814,21 +2814,20 @@
 %25s with 
-patch
+diff
  revisio
@@ -2867,24 +2867,29 @@
 est_id, 
-patch_id
+diff_revision
 )%0A      
@@ -2912,26 +2912,30 @@
 y_patch(
-patch_file
+diff_file_path
 , self.r
@@ -3368,23 +3368,32 @@
     
-patch, patch_id
+diff_body, diff_revision
 , ba
@@ -3416,27 +3416,16 @@
 t_patch(
-request_id,
 %0A       
@@ -3433,112 +3433,45 @@
     
-                                       self.options.%0A                                                   
+request_id,%0A            self.options.
 diff
@@ -3521,21 +3521,25 @@
 empfile(
-patch
+diff_body
 )%0A%0A     
@@ -3574,16 +3574,21 @@
 id, 
-patch_id
+diff_revision
 , tm

Refactor patch command variables