# -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
import ulib.base.i_need_py23

__all__ = ('is_svndir', 'SVNDir')

import re
from os import path

from ulib.base.uio import _s
from ulib.base.base import updated, isstr
from ulib.base.paths import relpath
from ulib.base.procs import spawn, spawn_capture, spawn_redirect
from ulib.base.tmpfiles import mktemp
from ulib.base.lines import Lines
from base import *

def svncmd(*args, **kw):
    cmd = ['svn']
    if isquiet(kw):
        if args[0:1] and args[0].startswith('-'):
            cmd.append('-q')
            cmd.extends(args)
        else:
            cmd.extend(args[0:1])
            cmd.append('-q')
            cmd.extend(args[1:])
    else:
        cmd.extend(args)
    return tuple(cmd)

def is_svndir(p):
    return path.isdir(path.join(p, '.svn'))

class SVNDir(VcsDir):
    def __init__(self, p, rootdir=None):
        VcsDir.__init__(self, p, rootdir)

    __str__ = VcsDir.get_path
    __repr__ = VcsDir.get_path

    def get_vcs_name(self):
        return "svn"

    def _is_vcsdir(self, p):
        return is_svndir(p)

    def _new_vcsdir(self, p, rootdir=False):
        vcsdir = SVNDir(p)
        if rootdir: vcsdir.rootdir = vcsdir
        return vcsdir

    def add(self, *files, **kw):
        basedir = self.get_path()
        cmd = list(svncmd('add', **kw))
        for file in files:
            cmd.append(relpath(file, basedir))
        return spawn(*cmd, **kw)

    def remove(self, *files, **kw):
        basedir = self.get_path()
        folded = self.autofold(**kw)
        try:
            cmd = list(svncmd('delete', **kw))
            for file in files:
                cmd.append(relpath(file, basedir))
            return spawn(*cmd, **kw)
        finally:
            if folded: self.autounfold(**kw)

    def copy(self, fromf, tof, **kw):
        basedir = self.get_path()
        fromf = relpath(fromf, basedir)
        tof = relpath(tof, basedir)
        return spawn(*svncmd('copy', '--parents', fromf, tof, **kw), **kw)

    def move(self, fromf, tof, **kw):
        basedir = self.get_path()
        folded = self.autofold(**kw)
        try:
            fromf = relpath(fromf, basedir)
            tof = relpath(tof, basedir)
            return spawn(*svncmd('move', '--parents', fromf, tof, **kw), **kw)
        finally:
            if folded: self.autounfold(**kw)

    def mkdir(self, dir, **kw):
        basedir = self.get_path()
        dir = relpath(dir, basedir)
        return spawn(*svncmd('mkdir', '--parents', dir, **kw), **kw)

    def commit(self, message=None, *files, **kw):
        basedir = self.get_path()
        folded = self.autofold(**kw)
        try:
            if message is None: cmd = svncmd('ci', **kw)
            else: cmd = svncmd('ci', '-m', message, **kw)
            cmd = list(cmd)

            if not files: files = ['.']
            for file in files:
                cmd.append(relpath(file, basedir))
            return spawn(*cmd, **kw)
        finally:
            if folded: self.autounfold(**kw)

    def status(self, **kw):
        folded = self.autofold(**kw)
        try:
            return spawn(*svncmd('status', **kw), **kw)
        finally:
            if folded: self.autounfold(**kw)

    def update(self, ignore_externals=False, **kw):
        folded = self.autofold(**kw)
        try:
            args = ['update']
            if ignore_externals:
                args.append('--ignore-externals')
            return spawn(*svncmd(*args, **kw), **kw)
        finally:
            if folded: self.autounfold(**kw)

    def _get_info_lines(self):
        self.set_LANG('C')
        try:
            _, stdout, _ = spawn_capture(stderr=False, *svncmd('info', relpath('.', self.get_path())))
            lines = Lines(stdout)
        finally:
            self.restore_LANG()
        return lines

    reposurl = None

    def get_reposurl(self):
        if self.reposurl is None:
            lines = self._get_info_lines()
            lines = lines.grep('^Repository Root: ')
            if not lines: raise ValueError("Got no content from 'svn info'")
            line = lines[0][len('Repository Root: '):]
            self.reposurl = line
        return self.reposurl

    url = None

    def get_url(self):
        if self.url is None:
            lines = self._get_info_lines()
            lines = lines.grep('^URL: ')
            if not lines: raise ValueError("Got no content from 'svn info'")
            line = lines[0][len('URL: '):]
            self.url = line
        return self.url

    RE_TRUNK = re.compile(r'(.*)/(trunk)(?:/(.*))?$')
    RE_BRANCHES = re.compile(r'(.*)/(branches)(?:/(.*))?$')
    RE_TAGS = re.compile(r'(.*)/(tags)(?:/(.*))?$')

    def __split_url(self, url):
        mo = None
        for re_url in (self.RE_TRUNK, self.RE_BRANCHES, self.RE_TAGS):
            mo = re_url.match(url)
            if mo is not None: break
        else:
            return url, None, None
        return mo.group(1), mo.group(2), mo.group(3)


    def __build_url(self, url, part):
        prefix, oldpart, suffix = self.__split_url(url)
        if oldpart is None: return path.join(url, part)
        else:
            url = path.join(prefix, part)
            if suffix is not None: url = path.join(url, suffix)
            return url

    def get_trunk_url(self, url=None):
        if url is None: url = self.get_url()
        return self.__build_url(url, 'trunk')

    def get_branches_url(self, url=None):
        if url is None: url = self.get_url()
        return self.__build_url(url, 'branches')

    def get_tags_url(self, url=None):
        if url is None: url = self.get_url()
        return self.__build_url(url, 'tags')

    def tag(self, name, message=None, **kw):
        if self._tag_rootdir and not self.is_rootdir():
            return self.get_rootdir().tag(name, message, **kw)
        trunk_url = self.get_trunk_url()
        tags_url = self.get_tags_url()
        tagged_url = path.join(tags_url, name)
        cmd = svncmd('copy', '--parents', trunk_url, tagged_url, **kw)
        if message is not None: cmd += ('-m', message)
        return spawn(*cmd, **kw)

    def __interesting_status(self, line):
        line = line.strip()
        if not line: return False
        for start in ('M', 'X', 'Performing status on external item at'):
            if line[:len(start)] == start:
                return False
        return True

    def diff_to(self, outfile=None, compare_with=None, **kw):
        folded = self.autofold(**kw)
        outf = outfile; close_outf = False
        try:
            if outf is None:
                outf, outfile = mktemp()
                close_outf = True
            elif isstr(outf):
                outf = open(outf, 'wb')
                close_outf = True

            # affichés les fichiers "particuliers" qui ne sont pas modifiés
            # (par exemple, les fichiers à ajouter, etc.)
            self.set_LANG('C')
            try:
                _, stdout, _ = spawn_capture(stderr=False, *svncmd('status'))
                lines = Lines(stdout)
            finally:
                self.restore_LANG()

            lines.filter(self.__interesting_status)
            if lines:
                outf.write(_s(u"Fichiers non modifiés dans un état spécial:\n"))
                lines.writelines(outf)
                outf.write("===================================================================\n")

            # Ajouter les différences sur les fichiers modifiés
            cmd = svncmd('diff', '-x', '-u')
            if compare_with is not None: cmd += ('-r', _s(compare_with))
            status, _, _ = spawn_redirect(*cmd, **updated(kw, stdout=outf))

            if folded: self.autounfold(**kw)
            return status, outfile
        finally:
            if close_outf: outf.close()
