#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS

function display_help() {
    uecho "$scriptname: renommer un fichier ou les fichiers d'un répertoire

USAGE
    $scriptname -f FILE [cmds...]
    $scriptname FILE [cmds...]

Une ou plusieurs commandes peuvent être spécifiées, séparées //
La commande par défaut est 'fixcase'

OPTIONS
    -N, --detect-always
        Pour la commande conv, ne pas optimiser le calcul de l'encoding. Cette
        option n'est valide que si src_enc n'est pas spécifié. On assume que
        tous les fichiers n'ont pas le même encoding: l'encoding src_enc est
        donc recalculé à chaque fois.
    -r, --reverse
        Pour la commande conv, inverser src_enc et dest_enc, qui doivent être
        tous les deux spécifiés.
    -f, --file FILE
        Spécifier le fichier ou le répertoire concerné par le renommage. Les
        aliases -d et --dir sont aussi reconnus.
        Si cette option n'est pas spécifiée, le premier argument est considéré
        comme le nom du fichier ou du répertoire à renommer.
        Si un répertoire est spécifié, le traitement est appliqué à tous les
        fichiers et répertoires de façon récursive, sans limite de profondeur.
    --show-cmd
        Afficher la commande qui serait exécutée

COMMANDES
    C, conv dest_enc [src_enc]
        Convertir le nom du fichier dans un autre encoding.
        dest_enc est l'encoding destination. Il doit être spécifié.
        src_enc est l'encoding source. S'il n'est pas spécifié ou vaut 'detect',
            il est autodétecté.
    U, utf8 [src_enc]
        Equivalent à conv utf8 src_enc
    L, latin1 [src_enc]
        Equivalent à conv latin1 src_enc
    lc, latin1compat
        Transformer certains caratères UTF-8 en équivalents qui existent en Latin1
    na, noaccents
        Transformer les caractères accentués en caractères non accentués
    l, lowercase
        Transfomer le nom en minuscule
    u, uppercase
        Transformer le nom en majuscule
    f, fixcase
        Transformer le nom en minuscule s'il est entièrement en majuscule"
}

function _lowercase() {
    evalx cat // strlower
}
function _uppercase() {
    evalx cat // strupper
}
function _fixcase() {
    local actual upper
    setx actual=cat
    setx upper=strupper "$actual"
    if [ "$actual" == "$upper" ]; then
        strlower "$actual"
    else
        echo "$actual"
    fi
}

function detect_enc() {
    local filename enc
    if [ -f "$1" ]; then
        setx filename=basename -- "$1"
    elif [ -d "$1" ]; then
        local -a files
        setx -a files=evalp find "$1" // head -n1
        if [ ${#files[*]} -gt 0 ]; then
            setx filename=basename -- "${files[0]}"
        fi
    fi
    setx enc="$scriptdir/lib/pywrapper" uencdetect.py "$filename"
    echo "$enc"
    [ "$enc" != Unknown ]
}

function before_parse_cmd() {
    case "$1" in
    conv|c)
        if [ -n "$OPTIMIZE_CONV" ]; then
            local to="$2"
            [ -n "$to" ] && setx CONV_TO=__norm_encoding "$to"

            local from="${3:-detect}"
            if [ "$from" == "detect" ]; then
                setx from=detect_enc "$FILE"
                if [ "$from" == "Unknown" ]; then
                    echo "$FILE: Impossible de détecter l'encoding"
                    return 1
                fi
            fi
            [ -n "$from" ] && setx CONV_FROM=__norm_encoding "$from"
            OPTIMIZE_CONV=
        fi
        ;;
    esac
}

function iconv_detect() {
    local to="$1"; shift
    local from
    if setx from=detect_enc "$FILE"; then
        if [ -n "$REVERSE_CONV" ]; then
            local tmp="$to"
            to="$from"
            from="$tmp"
        fi
        iconv -f "$from" -t "$to" "$@"
        return 0
    else
        cat
        return 1
    fi
}

function parse_cmd() {
    local cmd="$1"; shift
    local -a args
    case "$cmd" in
    utf8|U) parse_cmd conv utf8 "$@";;
    latin1|L) parse_cmd conv latin1 "$@";;
    conv|c)
        local to="$CONV_TO" from="$CONV_FROM"
        [ -n "$to" ] || to="$1"; shift
        [ -f "$from" ] || from="${1:-detect}"; shift

        if [ -z "$to" ]; then
            echo "conv: vous devez spécifier l'encoding destination"
            return 1
        fi
        setx to=__norm_encoding "$to"
        if [ "$from" == "detect" ]; then
            qvals iconv_detect "$to" "$@"
        else
            setx from=__norm_encoding "$from"
            if [ "$from" == "$to" ]; then
                echo "Une conversion de $from à $to n'a pas de sens. Veuillez re-essayer avec -N si nécessaire."
                return 1
            fi
            if [ -n "$REVERSE_CONV" ]; then
                local tmp="$to"
                to="$from"
                from="$tmp"
            fi
            qvals iconv -f "$from" -t "$to" "$@"
        fi
        ;;
    latin1compat|lc) echo _latin1compat;;
    noaccents|na|fixchars|fc) echo _noaccents;;
    lowercase|lower|l) echo _lowercase;;
    uppercase|upper|u) echo _uppercase;;
    fixcase|fix|f) echo _fixcase;;
    *)
        echo "$cmd: commande invalide"
        return 1
        ;;
    esac
    return 0
}

function fnconv() {
    local dir oldname newname
    setx dir=dirname -- "$FILE"
    setx oldname=basename -- "$FILE"
    setx newname=eval "$CMD" <<<"$oldname"

    if [ -d "$FILE" ]; then
        if [ "$newname" != "$oldname" ]; then
            etitle "$(ppath "$FILE") --> $newname"
            mv "$FILE" "$dir/$newname" || die
            FILE="$dir/$newname"
        else
            etitle "$(ppath "$FILE")"
        fi
        local -a sfiles
        array_lsall sfiles "$FILE"
        for FILE in "${sfiles[@]}"; do
            fnconv
        done
        eend
    else
        if [ "$newname" != "$oldname" ]; then
            estep "$oldname --> $newname"
            mv "$FILE" "$dir/$newname" || die
            FILE="$dir/$newname"
        fi
    fi
}

CONV_FROM=
CONV_TO=
OPTIMIZE_CONV=1
REVERSE_CONV=
FILE=
show_cmd=
parse_opts "${PRETTYOPTS[@]}" \
    --help '$exit_with display_help' \
    -N,--detect-always OPTIMIZE_CONV= \
    -r,--reverse REVERSE_CONV=1 \
    -f:,--file:,-d:,--dir: FILE= \
    --show-cmd show_cmd=1 \
    @ args -- "$@" && set -- "${args[@]}" || die "$args"

if [ -z "$FILE" ]; then
    FILE="${1:--}"
    shift
fi
[ "$FILE" == - ] && FILE=/dev/stdin
if [ "$FILE" != /dev/stdin ]; then
    [ -e "$FILE" ] || die "$FILE: fichier introuvable"
fi

[ $# -gt 0 ] || set -- fixcase # commande par défaut

CMD= # la commande finale à lancer pour convertir le nom du fichier
while [ $# -gt 0 ]; do
    args=()
    while [ $# -gt 0 ]; do
        arg="$1"; shift
        if [ "$arg" == // ]; then
            break
        elif [ "${arg%//}" != "$arg" ]; then
            local tmp="${arg%//}"
            if [ -z "${tmp//\\/}" ]; then
                arg="${arg#\\}"
            fi
        fi
        args=("${args[@]}" "$arg")
    done

    if [ ${#args[*]} -gt 0 ]; then
        before_parse_cmd "${args[@]}"
        setx cmd=parse_cmd "${args[@]}" || die "$cmd"
        CMD="${CMD:+$CMD | }$cmd"
    fi
done

if [ -n "$show_cmd" ]; then
    echo "$CMD"
elif [ "$FILE" == /dev/stdin ]; then
    eval "$CMD"
elif [ -f "$FILE" -o -d "$FILE" ]; then
    fnconv
else
    die "$FILE: fichier invalide"
fi
