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

"""Des fonctions pour gérer les chemins.
"""

__all__ = ('in_PATH', 'matches_name',
           'KNOWN_BINARY_EXTS', 'fileext_is_binary',
           'dirname', 'basename', 'splitall',
           'abspath', 'abspath2', 'relpath', 'ppath',
           'mkdirp', 'mkdirof',
           )

import os, re
from os import path
from fnmatch import fnmatch

def in_PATH(cmd):
    """Retourner True si la commande cmd est dans le PATH et est exécutable.
    """
    PATH = os.environ.get('PATH', None)
    if not PATH: return False

    for p in PATH.split(os.pathsep):
        pf = path.join(p, cmd)
        if os.access(pf, os.X_OK):
            return True
    else:
        return False

def matches_name(pattern, name, basedir=''):
    """Retourner True si name correspond au pattern pattern

    name étant exprimé relativement au répertoire basedir, si pattern se
    termine par '/', alors la correspondance ne se fait que si name est
    effectivement un répertoire sur le système de fichier. Sinon, la
    correspondance se fait sur le nom de fichier, sans tenir compte du système
    de fichier.
    """
    if pattern[-1:] == '/':
        pf = path.join(basedir, name)
        pattern = pattern[:-1]
        return path.isdir(pf) and fnmatch(name, pattern)
    else:
        return fnmatch(name, pattern)

KNOWN_BINARY_EXTS = (
    '.avi', '.bin', '.bmp', '.bpt', '.bro', '.bz2', '.class', '.com',
    '.dat', '.dll', '.doc', '.eps', '.ex_', '.exe', '.fm', '.gif', '.gz',
    '.hqx', '.icns', '.ico', '.ins', '.jar', '.jpeg', '.jpg', '.lib',
    '.mcp', '.mov', '.mp3', '.mpg', '.objectplant', '.ofp', '.opn',
    '.ovl', '.pdf', '.png', '.ppt', '.pqg', '.prj', '.ps', '.pyc', '.pyd',
    '.pyo', '.rpm', '.sit', '.sl', '.snd', '.so', '.so.3', '.srpm',
    '.strings', '.swf', '.tar', '.tgz', '.tbz2', '.tif', '.tiff',
    '.wordbreak', '.xbm', '.xls', '.zip',
    )
def fileext_is_binary(file):
    """Retourner True si l'extension du fichier est celle d'un fichier binaire
    """
    _, f = path.split(file)
    if f == '.DS_Store': return True
    _, fe = path.splitext(f)
    return fe.lower() in KNOWN_BINARY_EXTS

def dirname(file):
    """Retourner le répertoire qui contient file.

    dirname('/') --> '/'
    """
    return path.split(file)[0]

def basename(file, strip_ext=None):
    """Retourner le nom de base de file, en enlevant éventuellement l'extension strip_ext
    """
    bn = path.split(file)[1]
    if strip_ext is not None:
        n, e = path.splitext(bn)
        if e == strip_ext: bn = n
    return bn

RE_ROOT = re.compile(r'/*$')
def splitall(file):
    """Retourner tous les composants de file (répertoires et fichier)

    Par exemple:
        splitall("/a/b") --> ["a", "b"]
    Cas particuliers:
        splitall("") --> []
        splitall("/") --> []
    """
    dirs = []
    while RE_ROOT.match(file) is None:
        dir, name = path.split(file)
        dirs.insert(0, name)
        file = dir
    return dirs

def _join(dir, name):
    return path.normpath(path.join(dir, name))

def abspath(file, basedir=None, cwd=None):
    """Retourner le chemin absolu de file.

    file est exprimé par rapport au répertoire basedir, qui est exprimé par
    rapport à cwd s'il est relatif.
    """
    if path.isabs(file): return file
    if basedir is not None:
        file = _join(basedir, file)
        if path.isabs(file): return file
    if cwd is None: cwd = os.getcwd()
    return _join(cwd, file)

def abspath2(file, basedir=None, cwd=None):
    """Retourner le chemin absolu de file.

    Si file a un chemin ou s'il existe dans cwd, il est exprimé par rapport à cwd.
    Si file est sans chemin et qu'il n'existe pas dans cwd, il est exprimé par
    rapport à basedir.
    """
    if path.isabs(file): return file
    if cwd is None: cwd = os.getcwd()
    cwdfile = _join(cwd, file)
    if path.exists(cwdfile): return cwdfile
    if basedir is not None:
        dir, _ = path.split(file)
        if dir == "": file = _join(basedir, file)
        if path.isabs(file): return file
    return _join(cwd, file)

def relpath(file, basedir=None, cwd=None):
    """Retourner le chemin relatif pour accéder à file depuis cwd.
    file est exprimé par rapport au répertoire basedir.
    """
    if cwd is None: cwd = os.getcwd()
    cwd = path.abspath(cwd)
    cwdslash = cwd + '/'
    if basedir is None: basedir = cwd
    if not path.isabs(file): file = path.abspath(path.join(basedir, file))
    file = path.normpath(file)

    if file.startswith(cwdslash):
        file = file[len(cwdslash):]
    elif file == cwd:
        file = '.'
    else:
        prefix = path.commonprefix([file, cwd])
        if not prefix.endswith('/'):
            pos = prefix.rfind('/')
            if pos != - 1: prefix = prefix[:pos + 1]
        file = file[len(prefix):]
        cwd = cwd[len(prefix):]
        levels = len(splitall(cwd))
        relpath = ''
        for _ in range(levels):
            relpath = path.join('..', relpath)
        file = path.join(relpath, file)
    return file

def ppath(file, basedir=None, cwd=None, homedir=None):
    """Retourner si possible le chemin relatif pour accéder à file depuis cwd,
    homedir, ou le chemin absolu, suivant les cas exposés ci-dessous. file est
    exprimé par rapport au répertoire basedir.

    Le chemin retourné par cette fonction est destiné à l'affichage.

    - Si file est situé dans l'arborescence à partir de cwd, alors retourner un
      chemin du genre relpath/to/file.
    - Si file est situé dans le répertoire $HOME, alors retourner un chemine du
      genre ~/path/to/file
    - Sinon, retourner le chemin absolu /path/to/file
    """
    if cwd is None: cwd = os.getcwd()
    cwd = path.abspath(cwd)
    if basedir is None: basedir = cwd
    if not path.isabs(file): file = path.abspath(path.join(basedir, file))
    file = path.normpath(file)
    if homedir is None: homedir = path.expanduser('~')
    homedir = path.abspath(homedir)
    # depuis cwd
    ppath = relpath(file, cwd=cwd)
    if not ppath.startswith('../'): return ppath
    # depuis ~
    ppath = relpath(file, cwd=homedir)
    if ppath == '.': return '~'
    elif not ppath.startswith('../'): return '~/' + ppath
    # sinon chemin absolu
    return file

def mkdirp(dir):
    """Créer si nécessaire le répertoire

    @return: True si le répertoire a été créé, False sinon
    """
    if not path.isdir(dir):
        os.makedirs(dir)
        return True
    return False

def mkdirof(file):
    """Créer si nécessaire le répertoire du fichier file

    @return: True si le répertoire a été créé, False sinon
    """
    dir, _ = path.split(file)
    return mkdirp(dir)
