import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT

import buffer, default, dirutil, lex, regex, util, window
from point import Point

from method import Method, Argument

class SvnException(Exception):
    pass

statuses = {
    ' ': 'Unmodified',
    'A': 'Added',
    'C': 'Conflicted',
    'D': 'Deleted',
    'I': 'Ignored',
    'M': 'Modified',
    'R': 'Replaced',
    'X': 'External',
    '?': 'Unknown',
    '!': 'Missing',
    '~': 'Obstructed',
}

def get_status(path, base=None):
    if base is None: base = os.getcwd() + os.path.sep
    if path.startswith(base): path = path[len(base):]

    cmd = "svn status -v %r" % path
    status, data = commands.getstatusoutput(cmd)
    status = status >> 8

    if status != 0:
        raise SvnException("Problems with 'svn status': %d" % status)

    c = data[0]
    status = statuses.get(c, 'Error (%s)' % c)
    fields = data[6:].split()

    try:
        rrev, lrev, lauthor, filename = fields
    except:
        raise Exception, '%r %r' % (fields, data[6:])

    return {
        'svn-filename': filename,
        'svn-status':   status,
        'svn-lrev':     lrev,
        'svn-rrev':     rrev,
        'svn-author':   lauthor,
    }

class SvnCommit(Method):
    '''diff the current file with the version in SVN'''
    args = [Argument("msg", type=type(""), prompt="Commit Message: ")]
    regex = re.compile(r'^Committed revision ([0-9]+)\.$')
    def _execute(self, w, **vargs):
        if not hasattr(w.buffer, 'path'):
            w.set_error("Buffer has no corresponding file")
            return

        cwd = os.getcwd() + os.path.sep
        path = w.buffer.path
        if path.startswith(cwd):
            path = path[len(cwd):]

        cmd = "svn ci -m %r %r" % (vargs['msg'], path)
        (status, data) = commands.getstatusoutput(cmd)
        status = status >> 8
        lines = data.split('\n')

        if status == 0:
            try:
                for line in lines:
                    m = self.regex.match(line)
                    if m:
                        rev = m.group(1)
                        w.buffer.metadata['svn-lrev'] = rev
                        w.buffer.metadata['svn-rrev'] = rev
                        w.set_error("Committed [%s]" % rev)
                        return
            except:
                pass
        w.set_error("Problems with SVN commit: %d" % status)
        w.application.data_buffer("*Commit*", repr(lines), switch_to=True)
    
class SvnStatus(Method):
    column = {
        ' ': 'Unmodified',
        'A': 'Added',
        'C': 'Conflicted',
        'D': 'Deleted',
        'I': 'Ignored',
        'M': 'Modified',
        'R': 'Replaced',
        'X': 'External',
        '?': 'Unknown',
        '!': 'Missing',
        '~': 'Obstructed',
    }
    def _execute(self, w, **vargs):
        if not hasattr(w.buffer, 'path'):
            w.set_error("Buffer has no corresponding file")
            return

        cwd = os.getcwd() + os.path.sep
        path = w.buffer.path
        if path.startswith(cwd):
            path = path[len(cwd):]
        cmd = "svn status -v %r" % path
        (status, data) = commands.getstatusoutput(cmd)
        status = status >> 8

        if status != 0:
            w.set_error("Problems with 'svn status': %d" % status)
            return

        c = data[0]
        status = self.column.get(c, 'Error (%s)' % c)
        fields = data[6:].split()
        if len(fields) == 4:
            rrev, lrev, lauthor, filename = fields
            w.buffer.metadata['svn-filename'] = filename
            w.buffer.metadata['svn-status']   = status
            w.buffer.metadata['svn-lrev']     = lrev
            w.buffer.metadata['svn-rrev']     = rrev
            w.buffer.metadata['svn-author']   = lauthor
            w.buffer.metadata['vc-info']      = '[svn:%s/%s]' % (lrev, rrev)
            w.set_error('%s  %s  %s/%s  [%s]' % (filename, status, rrev, lrev, lauthor))
        else:
            w.buffer.metadata['svn-filename'] = path
            w.buffer.metadata['svn-status']   = status
            w.buffer.metadata['vc-info']      = '[svn:%s]' % status.lower()
            w.set_error('%s  %s' % (path, status))

class SvnLog(Method):
    '''display the SVN log for the current file'''
    sep_re = re.compile('^-+$')
    log_re = re.compile('^(.+?) \| (.+?) \| (.{25}) .+? \| (.+)$')
    def _build_entry(self, log_line, mesg_lines):
        log_data = '[c:d:*]%s  [g:d:*]%s  [b:d:*]%s  [c:d:*]%s' % log_line
        mesg_data = '\n'.join(mesg_lines).strip()
        if mesg_data:
            mesg_data += '\n'
        return '[b:d:*]' + log_data + '\n' + mesg_data
    def _execute(self, w, **vargs):
        cmd = "svn log %r" % w.buffer.path
        (status, data) = commands.getstatusoutput(cmd)

        entries = []
        log_line, mesg_lines = None, []
        for line in data.split('\n'):
            if self.sep_re.match(line):
                if log_line is not None:
                    entries.append(self._build_entry(log_line, mesg_lines))
                    log_line   = None
            else:
                m = self.log_re.match(line)
                if m:
                    log_line   = m.groups()
                    mesg_lines = []
                else:
                    assert log_line is not None, '%r %r' % (entries, line)
                    mesg_lines.append(line)
        data2 = ''.join(entries)

        if status == 0 and data:
            w.application.color_data_buffer("*Log*", data2, switch_to=True)
            w.set_error("%s: logfile" % self.name)
        elif status == 0:
            w.set_error("%s: There was no data" % self.name)
        else:
            w.set_error("%s: There was an error (%s)" % (self.name, status))
        

class SvnDiff(Method):
    '''diff the current file with the version in SVN'''
    def _execute(self, w, **vargs):
        if not hasattr(w.buffer, 'path'):
            w.set_error("Buffer has no corresponding file")
            return

        cmd = "svn diff %r" % w.buffer.path
        (status, data) = commands.getstatusoutput(cmd)

        if status == 0:
            if data:
                w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff')
                w.set_error("Differences were found")
            else:
                w.set_error("No difference found")
        else:
            w.set_error("There was an error (%s)" % (status))
class SvnDiff2(Method):
    '''diff the current file with the version in SVN'''
    rev_regex = re.compile('^[0-9]+$')
    args = [Argument("revision", type=type(""), prompt="Old Revision: ")]
    def _execute(self, w, **vargs):
        if not hasattr(w.buffer, 'path'):
            w.set_error("Buffer has no corresponding file")
            return

        rev = vargs['revision']
        if not self.rev_regex.match(rev):
            w.set_error("Could not parse revision: %r" % rev)
            return

        cwd = os.getcwd() + os.path.sep
        path = w.buffer.path
        if path.startswith(cwd):
            path = path[len(cwd):]

        cmd = "svn diff -r %s %r" % (rev, path)
        (status, data) = commands.getstatusoutput(cmd)
        status = status >> 8

        if data:
            w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff')
            w.set_error("Differences were found")
        else:
            w.set_error("No difference found")
class SvnDiff3(Method):
    '''diff the current file with the version in SVN'''
    rev_regex = re.compile('^[0-9]+$')
    args = [Argument("revision1", type=type(""), prompt="Old Revision: "),
            Argument("revision2", type=type(""), prompt="New Revision: ")]
    def _execute(self, w, **vargs):
        if not hasattr(w.buffer, 'path'):
            w.set_error("Buffer has no corresponding file")
            return

        rev1 = vargs['revision1']
        if not self.rev_regex.match(rev1):
            w.set_error("Could not parse revision1: %r" % rev)
            return

        rev2 = vargs['revision2']
        if not self.rev_regex.match(rev2):
            w.set_error("Could not parse revision2: %r" % rev)
            return

        cwd = os.getcwd() + os.path.sep
        path = w.buffer.path
        if path.startswith(cwd):
            path = path[len(cwd):]

        cmd = "svn diff -r %s:%s %r" % (rev1, rev2, path)
        (status, data) = commands.getstatusoutput(cmd)
        status = status >> 8

        if data:
            w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff')
            w.set_error("Differences were found")
        else:
            w.set_error("No difference found")

class SvnBlame(Method):
    '''show blame output for the current version in SVN'''
    line_re = re.compile('^ *(\d+) *([a-zA-Z0-9_]+) *([-0-9]+) *([:0-9]+) *(-\d{4}) *\(([^\)]+)\) (.*)\n$')
    def _execute(self, w, **vargs):
        if not hasattr(w.buffer, 'path'):
            w.set_error("Buffer has no corresponding file")
            return

        cmd = ("/usr/bin/svn", 'blame', '-v', w.buffer.path)
        pipe = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)

        linetokens = []
        for line in pipe.stdout:
            m = self.line_re.match(line)
            if not m:
                raise Exception, line
            (rev, user, date, t, tz, vdate, content) = m.groups()
            linetokens.append([rev, user, date, content, []])
        status = pipe.wait() >> 8

        lines = [x[3] for x in linetokens]
        if w.mode.grammar:
            lexer     = lex.Lexer(w.mode, w.mode.grammar)
            lextokens = [[] for l in lines]
            for t in lexer.lex(lines):
                linetokens[t.y][4].append(t)

        lines = []
        for linetoken in linetokens:
            (rev, user, date, content, lextokens) = linetoken
            prefix = '[b:d:*]%-4s [c:d:*]%-10s [b:d:*]%10s[d:d:*]' % (rev, user, date)
            if lextokens:
                suffixes = []
                for lt in lextokens:
                    s = lt.string.replace('\\', '\\\\')
                    s = s.replace('[', '\\[').replace(']', '\\]')
                    suffixes.append('[%s:%s:*]%s' % (lt.color[0], lt.color[1], s))
                suffix = ''.join(suffixes)
            else:
                suffix = content + '\n'
            lines.append('%s %s' % (prefix, suffix))
        data = ''.join(lines)

        if status == 0:
            w.application.color_data_buffer("*Blame*", data, switch_to=True)
        else:
            w.set_error("There was an error (%s)" % (status))