# -*- 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 phrases et les mots.
"""

__all__ = ('enplural', 'plural',
           'splitcc', 'joincc', 'splitus', 'joinus',
           's2cc', 'cc2s',
           's2us', 'us2s',
           'cc2us', 'us2cc',
           )

import string, re

from base import isseq

RE_LAST_LETTER = re.compile(r'([a-zA-Z])[^a-zA-Z]*$')
RE_UPPERCASE = re.compile('[A-Z]')
def __caseof(s, ref):
    """Transformer s en majuscules ou en minuscules suivant la casse de la
    dernière lettre de la chaine ref. si ref ne contient aucune lettre,
    transformer s en minuscules
    """
    mo = RE_LAST_LETTER.search(ref)
    if mo is None: return s.lower()
    if RE_UPPERCASE.match(mo.group(1)) is not None:
        # majuscule
        return s.upper()
    else:
        return s.lower()

RE_CHILD = re.compile(r'(?i)child$')
RE_Y = re.compile(r'(?i)y$')
RE_X = re.compile(r'(?i)x$')
RE_S = re.compile(r'(?i)s$')
def enplural(word):
    """Retourner la forme plurielle du mot anglais word

    cas particuliers:
    child --> children
    *y --> *ies
    *x --> *xes
    *s --> *ses

    sinon, on se contente de rajouter un s

    XXX la règle est un peu plus compliquée que cela. l'implémenter
    correctement. http://en.wikipedia.org/wiki/English_plural
    """
    if RE_CHILD.match(word): return word + __caseof('ren', word)
    elif RE_Y.search(word): return word[:-1] + __caseof('ies', word)
    elif RE_X.search(word): return word + __caseof('es', word)
    elif RE_S.search(word): return word + __caseof('es', word)
    else: return word + __caseof('s', word)

RE_PLURAL = re.compile(r'%(?:<([^#<>]*))?(?:>([^#<>]*))?#')

def plural(text, count, format=True):
    """Dans text, remplacer des marqueurs de pluralité par la chaine appropriée
    en fonction de la valeur de count.

    Un marqueur de pluralité est de la forme %[<sing][>plur]#
    Par défaut, sing == '' et plur == 's'

    Si count > 1, les marqueurs sont remplacés par plur, sinon ils sont
    remplacés par sing.

    Si format==True et que text contienne une occurence de %i, le formater avec
    text % count.
    """
    pos = 0
    while True:
        mo = RE_PLURAL.search(text, pos)
        if mo is None: break
        before = text[:mo.start(0)]
        after = text[mo.end(0):]
        if count > 1:
            plural = mo.group(2)
            if plural is None: plural = "s"
        else:
            plural = mo.group(1)
            if plural is None: plural = ""
        pos = len(before) + len(plural)
        text = before + plural + after
    if format and text.find("%i") != -1:
        text = text % count
    return text

def __split_maybe(s, sep=None):
    if not isseq(s): s = s.split(sep)
    return s

ALL_UPPERCASE_PATTERN = re.compile(r'[A-Z0-9]+$')
UNDERSCORE_PATTERN = re.compile(r'_+')
UPPERCASE_PATTERN = re.compile(r'([A-Z]+)?([^A-Z]*)')

def splitcc(src, plural=False):
    """Spliter des mots écrits en CamelCase. La casse des mots n'est pas
    modifiée.

    Si plural==True, utiliser enplural pour mettre au pluriel le dernier mot.

    e.g.
    splitcc('camelCase') --> ['camel', 'Case']
    splitcc('URClass') --> ['UR', 'Class']
    """
    src = src.strip()
    if not src: return []

    parts = UPPERCASE_PATTERN.findall(src)
    if parts[ - 1] == ('', ''): parts = parts[: - 1]

    dests = []
    for prefix, suffix in parts:
        if prefix == "":
            dests.append(suffix)
        elif len(prefix) == 1:
            dests.append(prefix + suffix)
        else:
            # len(prefix) > 1
            dests.append(prefix[: - 1])
            dests.append(prefix[ - 1] + suffix)

    if dests and plural: dests[-1] = enplural(dests[-1])
    return dests

def joincc(src, firstcap=False, plural=False):
    """Joindre des mots en camelCase. Si les mots sont tout en majuscules, il ne
    sont pas modifiés. Sinon, il sont transformés en minuscule, et la première
    lettre est capitalisée au besoin.

    Si plural==True, utiliser enplural pour mettre au pluriel le dernier mot.

    e.g.
    joincc('hello world') --> 'helloWorld'
    joincc(['hello', 'world']) --> 'helloWorld'
    joincc('Hello world') --> 'helloWorld'
    joincc('Hello World') --> 'helloWorld'
    joincc('HELLO WORLD') --> 'HELLOWORLD'
    joincc('Hello world', true) --> 'HelloWorld'
    joincc('Hello World', true) --> 'HelloWorld'
    joincc('HELLO WORLD', true) --> 'HELLOWORLD'
    """
    dests = []
    first = True
    for s in __split_maybe(src):
        if ALL_UPPERCASE_PATTERN.match(s) is None:
            if first and not firstcap:
                s = s.lower()
            else:
                s = s.capitalize()
        dests.append(s)
        first = False

    if dests and plural: dests[-1] = enplural(dests[-1])
    return ''.join(dests)

def splitus(src, plural=False):
    """Splitter des mots écrits séparés par des '_'. La casse des mots n'est pas
    modifiée.

    Si plural==True, utiliser enplural pour mettre au pluriel le dernier mot.

    e.g.
    splitus('under_score') --> ['under', 'score']
    splitus('UR_CLASS') --> ['UR', 'CLASS']
    """
    src = src.strip()
    if not src: return []

    dests = UNDERSCORE_PATTERN.split(src)
    if dests and plural: dests[-1] = enplural(dests[-1])
    return dests

def joinus(src, plural=False):
    """Joindre des mots en les séparant par des '_'. Si les mots sont tout en
    majuscules, il ne sont pas modifiés. Sinon, il sont transformés en
    minuscule avant d'être séparés par '_'.

    Si plural==True, utiliser enplural pour mettre au pluriel le dernier mot.

    e.g.
    joinus('hello world') --> 'hello_world'
    joinus(['hello', 'world']) --> 'hello_world'
    joinus('Hello world') --> 'hello_world'
    joinus('Hello World') --> 'hello_world'
    joinus('HELLO WORLD') --> 'HELLO_WORLD'
    """
    dests = []
    for s in __split_maybe(src):
        if ALL_UPPERCASE_PATTERN.match(s) is None:
            s = s.lower()
        dests.append(s)

    if dests and plural: dests[-1] = enplural(dests[-1])
    return '_'.join(dests)


def s2cc(s, firstcap=False, sep=None, plural=False):
    """Transformer une suite de mots séparés par sep en une suite de mots en camelCase.
    e.g. s2cc('hello world') --> 'helloWorld'
    """
    dests = __split_maybe(s, sep)
    if dests and plural: dests[-1] = enplural(dests[-1])
    return joincc(dests, firstcap)

def cc2s(cc, sep=None, plural=False):
    """Transformer une suite de mots en camelCase en une suite de mots séparés par sep.
    e.g. cc2s('helloWorld') --> 'hello world'
    """
    return (sep or ' ').join(map(string.lower, splitcc(cc, plural)))

def s2us(s, sep=None, plural=False):
    """Transformer une suite de mots séparés par sep en une suite de mots séparés par '_'.
    e.g. s2cc('hello world') --> 'hello_world'
    """
    dests = __split_maybe(s, sep)
    if dests and plural: dests[-1] = enplural(dests[-1])
    return joinus(dests)

def us2s(us, sep=None, plural=False):
    """Transformer une suite de mots séparés par '_' en une suite de mots séparés par sep.
    e.g. cc2s('hello_world') --> 'hello world'
    """
    return (sep or ' ').join(map(string.lower, splitus(us, plural)))

def cc2us(cc, plural=False):
    """Transformer une suite de mots en camelCase en une suite de mots séparés par '_'.
    e.g. cc2s('helloWorld') --> 'hello_world'
    """
    return joinus(splitcc(cc, plural))

def us2cc(us, firstcap=False, plural=False):
    """Transformer une suite de mots séparés par '_' en une suite de mots en camelCase.
    e.g. cc2s('hello_world') --> 'helloWorld'
    """
    return joincc(splitus(us, plural), firstcap)
