#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

TOOLS=(ddir dfile dcopy dmove dcmd)
if [ "$#" -eq 1 -a "$1" == --nutools-makelinks ]; then
    # créer les liens
    scriptname="$(basename "$0")"
    for tool in "${TOOLS[@]}"; do
        ln -s "$scriptname" "$tool"
    done
    exit 0
fi

source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS

function display_help() {
    uecho "$scriptname: manipuler des noms de fichier pour les dater

USAGE
    $scriptname (FILE|DIR)s...
    $scriptname -k SRCs... [DESTDIR]
    $scriptname -m SRCs... [DESTDIR]
    $scriptname -c COMMAND [ARGs...]

OPTIONS
    --create-or-rename
        Créer un nouveau fichier (option -f) ou créer un nouveau répertoire
        (option -d), ou renommer un fichier existant sans le changer de
        répertoire. C'est l'option par défaut.
    -f, --create-file
        Créer un nouveau fichier. Cette option est ignorée si le fichier ou le
        répertoire spécifié existe. C'est la valeur par défaut
    -d, --create-dir
        Créer un nouveau répertoire. Cette option est ignorée si le fichier ou
        le répertoire spécifié existe.
    -k, --copy
        Copier le fichier SRC vers la destination DESTDIR qui vaut par défaut le
        répertoire courant si un seul fichier source est spécifié. Si plusieurs
        fichiers sources sont spécifiés, il faut absolument spécifier un
        répertoire destination. Dans la destination, les fichiers sont nommés
        avec la date en préfixe.
    -m, --move
        Déplacer le fichier SRC vers la destination DESTDIR qui vaut par défaut
        le répertoire courant si un seul fichier source est spécifié. Si
        plusieurs fichiers sources sont spécifiés, il faut absolument spécifier
        un répertoire destination. Dans la destination, les fichiers sont nommés
        avec la date en préfixe.
    -c, --cmd
        Les arguments sont une commande à lancer. Dans les argument, PLACEHOLDER
        est remplacé par la date. Si le PLACEHOLDER n'est trouvé dans aucun des
        arguments, alors ajouter la date à la fin de la commande. Par exemple,
        voici comment émuler l'option -k
            $scriptname -c cp src dest/~~
    --string
        Remplacer dans chaque argument le placeholder par la date et afficher le
        résultat.
    -P, --placeholder PLACEHOLDER
        Dans le nom spécifié, remplacer PLACEHOLDER par la date. Par défaut,
        PLACEHOLDER vaut ~~. Si le nom spécifié ne contient pas le PLACEHOLDER,
        il est placé au début.
    -@, --force-date DATE
        Si une date du format attendu est déjà présente dans le nom du fichier,
        forcer son remplacement par la valeur spécifiée. Par défaut, le fichier
        n'est pas modifié si la date est déjà présente.
        Si le premier ou le dernier argument de ce script ressemblent à une date
        (ie. d/m[/y] ou @d[/m[/y]]) et qu'aucun fichier de ce nom n'existe dans
        le répertoire courant, activer automatiquement cette option, sauf si
        l'option --no-auto-force-date est spécifiée aussi. Ainsi, les commandes
        suivantes sont équivalentes:
            $scriptname -@ d/m/y file
            $scriptname d/m/y file
            $scriptname file @d/m/y
    -F, --format FORMAT
        Spécifier le format de la date à insérer dans le nom du fichier. Par
        défaut, FORMAT vaut 'YYMMDD-'
        Les formats valides sont:
            YYYY année sur 4 chiffres
            YY   année sur 2 chiffres
            MM   mois sur 2 chiffres
            DD   jour sur 2 chiffres
        Tous les autres caractères sont pris tels-quels
    -s, --short
        Equivalent à -F YYMMDD --autof
    -l, --long
        Equivalent à -F YYYMMDD --autof
    --autof
        Option non (pas encore) documentée"
}

function create() {
    case "$create" in
    file) touch "$1";;
    dir) mkdir -p "$1";;
    esac
}
function have_ph() {
    [[ "$1" == *"$placeholder"* ]]
}
function replace_ph() {
    local fy fm fd
    if [ -n "$force_date" ]; then
        fd="${force_date:0:2}"
        fm="${force_date:3:2}"
        fy="${force_date:6:4}"
    fi
    awkrun ph="$placeholder" \
           force_date="$force_date" fy:str="$fy" fm:str="$fm" fd:str="$fd" \
           format="$format" autof:int="$autof" '
function short2long(s,        cy, py, sy, ly) {
  cy = strftime("%Y") + 0
  cy = cy - cy % 100
  py = cy - 100
  sy = substr(s, 1, 2) + 0
  if (sy >= 80) ly = py + sy
  else ly = cy + sy
  return ly substr(s, 3)
}

BEGIN {
  if (format ~ /YYYYMMDD/) type = "long"
  else if (format ~ /YYMMDD/) type = "short"
  else type = "custom"

  if (force_date != "") {
    gsub(/%/, "%%", format)
    gsub(/YYYY/, fy, format)
    gsub(/YY/, substr(fy, 3, 2), format)
    gsub(/MM/, fm, format)
    gsub(/DD/, fd, format)
  } else {
    gsub(/%/, "%%", format)
    gsub(/YYYY/, "%Y", format)
    gsub(/YY/, "%y", format)
    gsub(/MM/, "%m", format)
    gsub(/DD/, "%d", format)
  }
  date = strftime(format)
}
{
  if (index($0, "/") != 0) {
    match($0, /\/[^/]*$/)
    dir = substr($0, 1, RSTART)
    name = substr($0, RSTART + 1)
  } else {
    dir = ""
    name = $0
  }

  if (autof) {
    pos = index(name, ph)
    if (pos == 0) {
      if (force_date != "") {
        if (type == "long") {
          if (name ~ /^[0-9]{8}/) {
            name = substr(name, 9)
            print dir date name
          } else if (name ~ /^[0-9]{6}/) {
            name = substr(name, 7)
            print dir date name
          } else {
            print dir date "-" name
          }
        } else if (type == "short") {
          if (name ~ /^[0-9]{8}/) {
            name = substr(name, 9)
            print dir date name
          } else if (name ~ /^[0-9]{6}/) {
            name = substr(name, 7)
            print dir date name
          } else {
            print dir date "-" name
          }
        } else if (type == "custom") {
          print dir date "-" name
        }
      } else {
        if (type == "long") {
          if (name ~ /^[0-9]{8}/) print dir name
          else if (name ~ /^[0-9]{6}/) print dir short2long(name)
          else print dir date "-" name
        } else if (type == "short") {
          if (name ~ /^[0-9]{8}/) print dir substr(name, 3)
          else if (name ~ /^[0-9]{6}/) print dir name
          else print dir date "-" name
        } else if (type == "custom") {
          print dir date "-" name
        }
      }
    } else if (pos == 1) {
      print dir date "-" substr(name, length(ph) + 1)
    } else if (pos == length(name) - length(ph) + 1) {
      print dir substr(name, 1, length(name) - length(ph)) "-" date
    } else {
      print dir substr(name, 1, pos - 1) date substr(name, pos + length(ph))
    }
  } else {
    pos = index(name, ph)
    if (pos == 0) {
      if (force_date != "") {
        if (type == "long") {
          if (name ~ /^[0-9]{8}/) name = substr(name, 9)
          print dir date name
        } else if (type == "short") {
          if (name ~ /^[0-9]{6}/) name = substr(name, 7)
          print dir date name
        } else if (type == "custom") {
          print dir date name
        }
      } else {
        if (type == "long") {
          if (name ~ /^[0-9]{8}/) print dir name
          else print dir date name
        } else if (type == "short") {
          if (name ~ /^[0-9]{6}/) print dir name
          else print dir date name
        } else if (type == "custom") {
          print dir date name
        }
      }
    } else {
      print dir substr(name, 1, pos - 1) date substr(name, pos + length(ph))
    }
  }
}' <<<"$1"
}

action=create
create=
case "$scriptname" in
ddir) action=create; create=dir;;
dfile) action=create; create=file;;
dcopy) action=copy;;
dmove) action=move;;
dcmd) action=cmd;;
esac
placeholder=
auto_force_date=1
force_date=
format=
autof=
parse_opts "${PRETTYOPTS[@]}" \
    --help '$exit_with display_help' \
    --create-or-rename action=create \
    -d,--create-dir create=dir \
    -f,--create-file create=file \
    -k,--copy action=copy \
    -m,--move action=move \
    -c,--cmd action=cmd \
    --string action=string \
    -P:,--placeholder: placeholder= \
    --no-auto-force-date auto_force_date= \
    -@:,--force-date: force_date= \
    -F:,--format: format= \
    -s,--short '$format=YYMMDD; autof=1' \
    -l,--long '$format=YYYYMMDD; autof=1' \
    --autof autof=1 \
    @ args -- "$@" && set -- "${args[@]}" || die "$args"

if [ -z "$force_date" -a -n "$auto_force_date" ]; then
    # si le premier ou le dernier argument ressemblent à une date
    f="$1"; f2="${f#@}"
    if [[ "$f" == */* ]] && [ -z "${f//[0-9\/]}" -a ! -e "$f" ]; then
        force_date="$f"
        shift
    elif [[ "$f" == @* ]] && [ -z "${f2//[0-9\/]}" -a ! -e "$f2" ]; then
        force_date="$f2"
        shift
    elif [ $# -gt 1 ]; then
        l="${@:$#}"; l2="${l#@}"
        if [[ "$l" == */* ]] && [ -z "${l//[0-9\/]}" -a ! -e "$l" ]; then
            force_date="$l"
            set -- "${@:1:$(($# - 1))}"
        elif [[ "$l" == @* ]] && [ -z "${l2//[0-9\/]}" -a ! -e "$l2" ]; then
            force_date="$l2"
            set -- "${@:1:$(($# - 1))}"
        fi
    fi
fi

[ -n "$create" ] || create=file
[ -n "$placeholder" ] || placeholder="~~"
[ -n "$force_date" ] && setx force_date=parse_date "$force_date"
if [ -z "$format" ]; then
    format=YYMMDD
    autof=1
fi

if [ "$action" == cmd ]; then
    # Il faut lancer une commande spécifiée
    [ $# -gt 0 ] || set -- echo
    args=()
    found=
    for arg in "$@"; do
        if have_ph "$arg"; then
            check_interaction -c && evalx qvals "${args[@]}" "$arg" // estepi "Commande:"
            setx arg=replace_ph "$arg" "$force_date"
            read_value "Veuillez confirmer le nom" arg "$arg"
            found=1
        fi
        args=("${args[@]}" "$arg")
    done
    if [ -z "$found" ]; then
        setx arg=replace_ph "" "$force_date"
        read_value "Veuillez entrer le nom du fichier" arg "$arg"
        args=("${args[@]}" "$arg")
    fi

    if show_debug; then
        evalx qvals "${args[@]}" // edebug "Commande effective:"
        ask_yesno "Voulez-vous continuer?" X || die
    fi
    "${args[@]}"

elif [ "$action" == copy -o "$action" == move ]; then
    if [ $# -eq 0 ]; then
        die "Vous devez spécifier les fichiers"
    elif [ $# -eq 1 ]; then
        src="$1"
        destdir=.
        srcs=("$src")
    else
        args=("$@")
        setx destdir=last_value args
        array_copy_firsts srcs args
    fi
    if [ ! -d "$destdir" ]; then
        ewarn "$destdir: répertoire introuvable"
        ask_yesno "Voulez-vous le créer?" X || die
        mkdir -p "$destdir" || die
    fi
    if [ "$action" == copy ]; then
        cmdinfo="Copie"
        cmd=(cp -i)
    elif [ "$action" == move ]; then
        cmdinfo="Déplacement"
        cmd=(mv -i)
    fi
    for src in "${srcs[@]}"; do
        setx src=abspath "$src"
        setx srcname=basename -- "$src"
        if [ -e "$src" ]; then
            estep "$cmdinfo de $(ppath "$src")"
            setx destname=replace_ph "$srcname" "$force_date"
            setx dest=abspath "$destdir/$destname"
            [ "$src" != "$dest" ] || ewarn "La source et la destination sont identiques"
            read_value "Veuillez confirmer le nom" destname "$destname"

            setx dest=abspath "$destdir/$destname"
            [ "$src" != "$dest" ] && "${cmd[@]}" "$src" "$destdir/$destname"
        else
            ewarn "$src: fichier introuvable"
        fi
    done

elif [ "$action" == create ]; then
    if [ $# -gt 0 ]; then
        for src in "$@"; do
            setx src=abspath "$src"
            setx dir=dirname -- "$src"
            setx srcname=basename -- "$src"
            if [ -e "$src" ]; then
                setx destname=replace_ph "$srcname" "$force_date"
                setx dest=abspath "$dir/$destname"
                confirm_do=1
                if [ "$src" == "$dest" ]; then
                    if [ -n "$force_date" ]; then
                        ewarn "La source et la destination sont identiques"
                    else
                        estepi "$(ppath "$src"): aucun renommage nécessaire"
                        confirm_do=
                    fi
                fi
                if [ -n "$confirm_do" ]; then
                    estep "Renommage de $(ppath "$src")"
                    read_value "Veuillez confirmer le nom" destname "$destname"

                    setx dest=abspath "$dir/$destname"
                    [ "$src" != "$dest" ] && mv -i "$src" "$dir/$destname"
                fi
            else
                setx destname=replace_ph "$srcname" "$force_date"
                setx dest=abspath "$dir/$destname"
                if [ -d "$dest" ]; then
                    ewarn "$srcname --> $(ppath "$dest"): répertoire existant"
                elif [ -e "$dest" ]; then
                    ewarn "$srcname --> $(ppath "$dest"): fichier existant"
                else
                    estep "$srcname --> Création de $(ppath "$dest")"
                fi
                read_value "Veuillez confirmer le nom" destname "$destname"

                setx dest=abspath "$dir/$destname"
                create "$dir/$destname"
            fi
        done
    else
        setx destname=replace_ph "" "$force_date"
        confirm_action=entrer
        if [ -d "$destname" ]; then
            ewarn "$destname: répertoire existant"
            confirm_action=confirmer
        elif [ -e "$destname" ]; then
            ewarn "$destname: fichier existant"
            confirm_action=confirmer
        else
            case "$create" in
            file) estep "Création d'un nouveau fichier";;
            dir) estep "Création d'un nouveau répertoire";;
            esac
        fi

        read_value "Veuillez $confirm_action le nom" destname "$destname"
        create "$destname"
    fi

elif [ "$action" == string ]; then
    if [ $# -gt 0 ]; then
        for string in "$@"; do
            replace_ph "$string" "$force_date"
        done
    else
        replace_ph "" "$force_date"
    fi

else
    die "$action: action non implémentée"
fi
