#!/bin/bash

# TODO:
# Scale up, not only down??
# Fail on remove empty sticker pack dir
# Test trusted source
# Preview
# Fix add/del
# Test other output formats
# Thumbnails: https://github.com/matrix-org/synapse/issues/13039 (manually upload small version of the sticker (32x32?) and uses this as thumbnail_url)
# Fix choice between apng or gif

shopt -s nocasematch    # Case insensitive

STPKG_CONF_FILE=~/.config/stpkg.sh
BASE_DIR="$PWD"

declare -A animatedFiletype=(
    ["image/gif"]="ඞ"
    ["image/apng"]="ඞ"
)

# Detect install
if [ "x$1" = "x-install" ]; then
    INSTALL="yes"
fi

if [ "x$STPKG_COLORS" = "x" ]; then
    STPKG_COLORS="yes"
    error() { __red    "$*\n" >/dev/stderr; }
    warn()  { __yellow "$*\n" >/dev/stderr; }
    info()  { __green  "$*\n" >/dev/stderr; }

    __red()     { echo -ne '\e[31m'$*'\e[0m'; }
    __green()   { echo -ne '\e[32m'$*'\e[0m'; }
    __yellow()  { echo -ne '\e[33m'$*'\e[0m'; }

    __bold()      { echo -ne '\033[1m'$*'\e[0m'; }
    __italic()    { echo -ne '\033[3m'$*'\e[0m'; }
    __underline() { echo -ne '\033[4m'$*'\e[0m'; }
else
    STPKG_COLORS="no"
    error() { echo 'ERROR:' $* >/dev/stderr; }
    warn()  { echo 'WARN:'  $* >/dev/stderr; }
    info()  { echo 'INFO:'  $* >/dev/stderr; }

    alias __red=echo
    alias __green=echo
    alias __yellow=echo

    alias __bold=echo
    alias __italic=echo
    alias __underline=echo
fi

die() {
    error $*
    exit 1
}

# Be paranoid
REQUIREMENTS=""
require() {
    for CMD in $*; do
        local __cmd=`which $CMD`
        [ -z $__cmd ] && die "Failed to find the '$CMD' command"
        REQUIREMENTS="$CMD $REQUIREMENTS"
    done
}
require \
    md5sum mktemp column uuidgen convert montage identify curl ffmpeg \
    sed tr git sponge ls find jq chmod cat expr iconv bc
__grep=`which egrep`
if [ -z $__grep ]; then
    __grep=`which grep`
    [ -z $__grep ] && die "Failed to find 'grep' or 'egrep'"
    warn "Cound not find 'egrep', will use 'grep' instead." \
         "Please, note that some functionalities may not work with regular expressions."
fi

SELF=`readlink -f "$0"`
MD5=`md5sum $SELF`

if [ "x$STPKG_NO_MSG" = "xyes" -a ! "x$1" = "x-install" -a ! "x$1" = "xupdate" ]; then
    # Do not silence errors
    warn()  { return 0; }
    info()  { return 0; }
fi

########################
# Some basic functions #
########################

__exit() { cd "$BASE_DIR"; exit $1; }
__EXIT() { cd "$BASE_DIR"; }
trap __exit EXIT
alias exit=__exit

__usage() {
    local BASE_NAME=$(basename $0)
    echo `__bold "$BASE_NAME usage:"`
    local BASE_NAME=`__green "$BASE_NAME"`
cat << EOF
    $BASE_NAME -install [`__yellow "-sshfs 'username@server' -local mnt_point -pickerrepo 'foo@git.fr:stickerpicker.git'"`] [`__yellow "install_path"`]
    $BASE_NAME show
    $BASE_NAME help
    $BASE_NAME update
    $BASE_NAME list [`__yellow "-p -e -np"`] [`__yellow "bash_regex"`]
    $BASE_NAME add|del <`__yellow "pack"`>
    $BASE_NAME pack [`__yellow "-t token"`] [`__yellow "name"`] <`__yellow "pack_folder"`>
    $BASE_NAME edit [`__yellow "-t token"`] <`__yellow "pack_name"`> add|del <`__yellow "sticker"`> [`__yellow "file"`]
    $BASE_NAME display [`__yellow "-dl folder"`] <`__yellow "pack_name"`> [`__yellow "sticker_regex"`]

`__bold Flags:`
    `__yellow "-e"` / `__yellow "-ne"`    The pack is enabled / disabled
    `__yellow "-p"` / `__yellow "-np"`    The pack is present / not present in the sticker repo
    `__yellow "-t"`          Matrix user token, let empty to fill it on stdin
    `__yellow "-dl"`         Specify a folder to dl, may exists or not

`__bold Configuration variables from $(echo '~/.config/stpkg.sh'):`
    `__green STPKG_INSTALL`  [`__italic 'path'`]      The cloned stickerpicker project location. [$(__yellow `__italic '~/html/stickerpicker'`)]
    `__green STPKG_BASE`     [`__italic 'path'`]      Where the sticker project is located.
    `__green STPKG_COLORS`   [`__italic "yes|no"`]    Use colors for output. [$(__yellow `__italic "yes"`)]
    `__green STPKG_NO_MSG`   [`__italic "yes|no"`]    Disable messages, no 'info' or 'warn'. [$(__yellow `__italic 'no'`)]
    `__green STPKG_TOKEN`    [`__italic 'token'`]     The Matrix access token. $(__red `__bold \! Sensitive data \!`)
    `__green STPKG_HOMESERV` [`__italic 'homeserv'`]  The Matrix home server. [$(__yellow `__italic 'iiens.net'`)]

`__bold Notes:`
    The `__italic "$(__yellow 'stpkg -install [path]')"` must be called only once. The default install
    path is `__italic "$(__yellow '~/html/stickerpicker')"`. This command will give you a `__italic $(__yellow STPKG_INSTALL)`
    and a `__italic $(__yellow STPKG_BASE)` env that you must put in your bashrc for the rest of the
    stpkg commands to work.

`__bold Requirements:`
    The stpkg command requires `__italic $(__yellow 'egrep')`. If `__italic $(__yellow 'egrep')` is not found, `__italic $(__yellow 'grep')` will be
    used instead. Here is a list of all the requirements (that should be checked automatically):
    $REQUIREMENTS
EOF
    exit 0
}

slugify() {
    echo "$1" | \
        iconv -c -t ascii//TRANSLIT | \
        sed -e 's/[~^]+//g' -e 's/[^a-zA-Z0-9]+/-/g' -e 's/^-+|-+$//g' | \
        tr A-Z a-z
}

default_index() {
    jq -n "{ \"homeserver_url\": \"https://matrix.iiens.net\", \"packs\": [ \"scalar-privacy_pam.json\" ] }"
}

validate_token_internal() {
    curl -s -X POST "https://$STPKG_HOMESERV/_matrix/media/r0/upload?access_token=$STPKG_TOKEN" | jq -r ".errcode"
}

validate_token() {
    local RES=`validate_token_internal`
    [ "x$RES" = "xM_UNKNOWN_TOKEN" ] && die "The token is incorrect or doesn't exist on '$STPKG_HOMESERV'"
    [ ! "x$RES" = "xM_UNKNOWN" ]     && die "Got an 'yet implemented' error in Token Validation"
    info "Token is valid on homeserver '$STPKG_HOMESERV'"
}

validate_homeserv() {
    curl "https://$STPKG_HOMESERV" >/dev/null 2>&1
    [ $? -ne 0 ] && die "Homeserver '$STPKG_HOMESERV' doesn't exist or is inaccessible"
}

file_get_width() {
    local WIDTH=$(identify -format "%w" "$1[0]") >/dev/null
    echo "$WIDTH"
}

file_get_height() {
    local HEIGHT=$(identify -format "%h" "$1[0]") >/dev/null
    echo "$HEIGHT"
}

__get_dest_dimensions() {
    local INIT_WIDTH=$1
    local INIT_HEIGHT=$2
    local MAX_WIDTH=$3
    local MAX_HEIGHT=$4
    local DEST_SEP="$5"
    local DEFAULT_DIST="$6"

    local WIDTH_RATIO="$(echo "scale=10; $INIT_WIDTH/$STPKG_MAX_WIDTH" | bc)"
    local HEIGHT_RATIO="$(echo "scale=10; $INIT_HEIGHT/$STPKG_MAX_HEIGHT" | bc)"

    if [ $(echo "$HEIGHT_RATIO >= $WIDTH_RATIO" | bc) = "1" ]; then
        if [ $(echo "$HEIGHT_RATIO > 1" | bc) = "1" ]; then
            local DEST_HEIGHT=$STPKG_MAX_HEIGHT
            local DEST_WIDTH="$DEFAULT_DIST"
        else
            local DEST_HEIGHT=$INIT_HEIGHT
            local DEST_WIDTH=$INIT_WIDTH
        fi
    elif [ $(echo "$WIDTH_RATIO > 1" | bc) = "1" ]; then
        local DEST_HEIGHT="$DEFAULT_DIST"
        local DEST_WIDTH=$STPKG_MAX_WIDTH
    else
        local DEST_HEIGHT=$INIT_HEIGHT
        local DEST_WIDTH=$INIT_WIDTH
    fi

    echo "${DEST_WIDTH}${DEST_SEP}${DEST_HEIGHT}"
}


is_apng() { identify apng:"$1" | grep -Fq "$1[1]" && echo "1" || echo "0"; }
file_get_type(){
    ret="`xdg-mime query filetype "$1"`"
    if [[ "x$ret" == "ximage/png" ]]; then
        if [[ "x`is_apng "$1"`" == "x1" ]]; then
            ret="image/apng"
        fi
    fi
    echo -n "$ret"
}
file_get_ext() { xdg-mime query filetype "$1" | sed 's+^.*/++'; }
file_get_ext_from_type() { echo "$1" | sed 's+^.*/++'; }
file_get_name() { echo -n "$1" | sed 's/.[^.]*$//'; }

__upload_file() {
    local TYPE=$1   # The type of file (png/gif)
    local FILE=$2   # The file
    local NAME=$3   # The pretty name for the sticker

    MXC=$(curl -s -X POST -H \
        "Content-Type: $TYPE" --data-binary "@$FILE" "https://$STPKG_HOMESERV/_matrix/media/r0/upload?access_token=$STPKG_TOKEN" | \
        tee /tmp/toto | jq -r ".content_uri")
    [ $? -ne 0 ] && die "Failed to upload sticker $NAME for pack $PACK_NAME to $STPKG_HOMESERV"
}

create_sticker_json() {
    local PACK_NAME=$1  # Pack name
    local NAME=$2       # Pretty name for the sticker
    local WIDTH=$3      # The WIDTH!
    local HEIGHT=$4     # The HEIGHT!
    local TYPE=$5       # The type of the picture (png/gif)
    local MXC=$6        # The MXC URL

    echo -n "{\"body\":\"$NAME\",\"info\":{\"mimetype\":\"$TYPE\",\"h\":$HEIGHT," \
            "\"w\":$WIDTH,\"thumbnail_url\":\"$MXC\"},\"msgtype\":\"m.sticker\",\"url\":\"$MXC\"" \
            ",\"id\":\"$PACK_NAME-$NAME\"}"
}

mxc_to_https() {
    # The transformation is the following
    # `mxc://<serv>/<id>` => `https://<serv>/_matrix/media/r0/download/<serv>/<id>`
    # It seems that every requests should be made to matrix.org...
    # `mxc://<serv>/<id>` => `https://matrix.org/_matrix/media/r0/download/<serv>/<id>`
    local SERV=`echo "$1" | awk -F/ '{print $3}'`
    local ID=`echo "$1" | awk -F/ '{print $4}'`
    echo "https://matrix.org/_matrix/media/r0/download/$SERV/$ID"
}

################################
# The special install function #
################################

__install() {
    # Parse options
    local PARSE_FLAGS=1
    while [ $PARSE_FLAGS -eq 1 ]; do
        case "$1" in
            -pickerrepo)
                STPKG_STICKER_REPO="$2"
                shift 2
                ;;
            -sshfs)
                local SSHFS="$2"
                shift 2
                ;;
            -local)
                local MNT="$2"
                shift 2
                ;;
            *)
                local PARSE_FLAGS=0
                ;;
        esac
    done
    STPKG_STICKER_REPO=${STPKG_STICKER_REPO:="https://github.com/maunium/stickerpicker.git"}

    # Create the ~/.config/stpkg.sh
    [ ! -d ~/.config ] && mkdir ~/.config
cat > $STPKG_CONF_FILE << EOF
if [ ! "x\$INSTALL" = "xyes" ]; then
STPKG_BASE="$BASE_DIR"
STPKG_HOMESERV='matrix.org'
STPKG_TOKEN=''
STPKG_COLORS='yes'
STPKG_NO_MSG='no'
STPKG_MAX_HEIGHT=128
STPKG_MAX_WIDTH=256
STPKG_GENERATE_PREVIEW="no"
STPKG_IMAGE_TARGET_FILETYPE="image/png"
STPKG_ANIMATED_TARGET_FILETYPE="image/gif"
STPKG_REUPLOAD_SAME_FILE="no"

__do_mount() { :; }

EOF
    chmod 00600 $STPKG_CONF_FILE

    # Set install dir. Oh boi, tricky things going around here because we
    # handle the sshfs ourself.
    if [ ! "x$SSHFS" = "x" ]; then
        info "Detected a sshfs install"
        [ "x$MNT" = "x" ] && die "You must specify a mount point with '-local /mount/pount' when using the '-sshfs' option"
        local STPKG_INSTALL=$1
        local STPKG_INSTALL=$MNT${STPKG_INSTALL:="/html/stickerpicker/"}

        # The sshfs hook
cat >> $STPKG_CONF_FILE << EOF
STPKG_SSHFS='yes'
STPKG_INSTALL="$STPKG_INSTALL"
SSHFS_USER="$SSHFS"
SSHFS_MNT="`echo "$MNT" | sed "s+$HOME+~+g"`"
fi

__do_mount() {
    mountpoint $MNT >/dev/null 2>&1
    [ \$? -ne 0 -a ! "x\$INSTALL" = "xyes" ] && {
        [ ! -d "\$STPKG_INSTALL" ] && {
            sshfs $SSHFS: $MNT \
                || die "Failed to mount $SSHFS:~/ to $MNT";
        }
        [ ! -d "\$STPKG_INSTALL" ] \
            && die 'Failed to mount $SSHFS:~/ to $MNT, sshfs problems?'
    }
}
EOF

        # Do the sshfs hook
        [ -f "$MNT" ] && die "The destination mount pount already exists"
        mkdir "$MNT"
        mountpoint $MNT >/dev/null 2>&1
        [ $? -eq 0 ] && die "The '$MNT' folder is already a mountpoint"
        sshfs $SSHFS: $MNT || die "Failed to mount $SSHFS:~/ to $MNT"
    else
        local STPKG_INSTALL=$1
        local STPKG_INSTALL=${STPKG_INSTALL:="~/html/stickerpicker/"}
        echo "STPKG_INSTALL=$STPKG_INSTALL" >> $STPKG_CONF_FILE
    fi

    # Check install dir
    [ -d "$STPKG_INSTALL" ] \
        && die "Folder '$STPKG_INSTALL' already exists." \
               "If you already installed the sticker picker you can use '$0 update'"

    # Clone and copy packs
    info "Will install sticker picker in folder: $STPKG_INSTALL"

    git clone "$STPKG_STICKER_REPO" "$STPKG_INSTALL" \
        || die "Failed to git clone the stickerpicker project. Your config file ($STPKG_CONF_FILE) is probably corrupted, you should clean it by hand"

    cd $STPKG_INSTALL/   || die "Failed to cd to '$STPKG_INSTALL/'"
    cp packs/* web/packs || die "Failed to copy default packs to '$STPKG_INSTALL/web/packs/'"

    default_index > $STPKG_INSTALL/web/packs/index.json || die "Failed to create default index.json file"

    info "You may edit the $STPKG_CONF_FILE to setup your home server and token (variable STPKG_TOKEN)"
}

if [ "x$1" = "x-install" ]; then
    [ "x$STPKG_NO_MSG" = "xyes" ] \
        && echo "!!! STPKG_NO_MSG is turn on, you won't see what's going on during the install"
    shift
    REPO_DIR="$(dirname $SELF)"
    [ "$REPO_DIR" = "$BASE_DIR" ] || die \
        "You must run this script from the root of the sticker repo." \
        "You are in '$BASE_DIR' and should be in '$REPO_DIR'"
    __install $*
    exit 0
fi

#########################
# Cmd handler functions #
#########################

__show() {
    local COLOR_STCOLOR=`[ "x$STPKG_COLORS" = "xyes" ] && echo '__green' || echo '__yellow'`
    local COLOR_STNOMSG=`[ "x$STPKG_NO_MSG" = "xyes" ] && echo '__green' || echo '__yellow'`
    local COMMITS=`git -C $STPKG_BASE rev-list --count HEAD 2>/dev/null`
    local REVISION=`git -C $STPKG_BASE rev-list --count master 2>/dev/null`
    local REVISION=`expr $COMMITS - $REVISION`
    local BRANCH=`git -C $STPKG_BASE branch 2>&1 | grep --color=auto "\*" | sed -e "s/* //" -e "s/$/ /"`

    local SAFE=0
    local UNSAFE=0
    local UNSAFE_CMD=""
    for CMD in $REQUIREMENTS; do
        [[ "`which $CMD`" =~ ^(/usr/bin|/bin|/sbin|/usr/sbin) ]] \
            && local SAFE=`expr $SAFE + 1` \
            || {
                local UNSAFE=`expr $UNSAFE + 1`
                local UNSAFE_CMD="$CMD $UNSAFE_CMD"
            }
    done
    [ $UNSAFE -gt 0 ] && local UNSAFE=`__yellow $UNSAFE` || local UNSAFE=`__green $UNSAFE`

    if [ "x$STPKG_TOKEN" = "x" ]; then
        local TOKEN=`__yellow 'absent' `
    else
        local TOKEN=`validate_token_internal`
        case "$TOKEN" in
            M_UNKNOWN_TOKEN)    local TOKEN=`__red '[invalid token]'`;;
            M_UNKNOWN)          local TOKEN=`__green '[valid]'`;;
            *)                  local TOKEN=`__yellow '[unimplemented]'`;;
        esac
        local TOKEN="$TOKEN `__red "Sensitive data, I won't show it!"`"
    fi

    ## SSHFS stuff
    if [ "x$STPKG_SSHFS" = "xyes" ]; then
cat << EOF
`__bold stpkg with sshfs install:`
    sshfs             `[ -d $STPKG_INSTALL ] && __green 'mounted' || { __yellow 'umounted'; echo " (should not be the case at this point)"; }`
    user              $SSHFS_USER
    mountpoint        $SSHFS_MNT
EOF
    fi

    ## Version stuff
cat << EOF
`__bold stpkg version:`
    branch            $BRANCH
    commits           $COMMITS
    revision          $REVISION
`__bold stpkg options and variables:`
    `__green STPKG_INSTALL`     `__italic $STPKG_INSTALL`
    `__green STPKG_BASE`        `__italic $STPKG_BASE`
    `__green STPKG_COLORS`      `__italic $($COLOR_STCOLOR $STPKG_COLORS)`
    `__green STPKG_NO_MSG`      `__italic $($COLOR_STNOMSG $STPKG_NO_MSG)`
    `__green STPKG_HOMESERV`    `__italic $STPKG_HOMESERV`
    `__green STPKG_TOKEN`       $TOKEN
`__bold required commands due to the '"require"' function:`
    safe (system)     `__green $SAFE`
    unsafe (user)     $UNSAFE
EOF
    [ ! -z "$UNSAFE_CMD" ] && {
        echo -ne '    '`__yellow unsafe commands`'   '
        for CMD in $UNSAFE_CMD; do echo -n "$CMD "; done
        echo ''
    }
}

__update() {
    info "Updating packs..."

    info "... update the sticker repo"
    local LOCATION_OPT="--git-dir=$STPKG_BASE/.git --work-tree=$STPKG_BASE/"
    git $LOCATION_OPT fetch  >/dev/null 2>&1 || die "Failed to fetch from sticker repo"
    git $LOCATION_OPT rebase >/dev/null 2>&1 || die "Failed to rebase... what did you do to your master branch?"

    info "... update the sticker picker repo"
    local LOCATION_OPT="--git-dir=$STPKG_INSTALL/.git --work-tree=$STPKG_INSTALL/"
    git $LOCATION_OPT fetch  >/dev/null 2>&1 || die "Failed to fetch from stickerpicker repo"
    git $LOCATION_OPT rebase >/dev/null 2>&1 || die "Failed to rebase... what did you do to the master branch of the sticker picker?"

    info "... copy the Json pack files in the sticker picker folder"
    cp $STPKG_BASE/packs/*/*.json $STPKG_INSTALL/web/packs/ || die "Failed to copy packs Json files"

    local NEW_MD5=`md5sum $(readlink -f "$STPKG_BASE/stpkg")`
    if [ ! "$NEW_MD5" = "$MD5" ]; then
        info "... creating update script"
        local UPDATE=`mktemp --suffix=.sh`
cat > $UPDATE << EOF
`declare -f __yellow`
`declare -f __green`
`declare -f __red`
`declare -f die`
__yellow "... Update stpkg ... "
cp "$STPKG_BASE/stpkg" "$SELF" && __green "success!" || __red "failed!"
rm "$UPDATE" || die "Failed to delete the update script"
echo ""
EOF
        exec bash "$UPDATE" \
            || die "Failed to run the update script." \
                   "You will need to copy manually '$STPKG_BASE/stpkg' to '$SELF' manually."
    fi

    info "Update finished!"
    exit 0
}

__display() {
    if [ "x$1" = "x-dl" ]; then
        local DL_SWITCH="yes"
        local DL_FOLDER="$2"
        if [ ! -d "$DL_FOLDER" ]; then
            info "Need to create folder '$DL_FOLDER'";
            mkdir "$DL_FOLDER" || die "Failed to create folder '$DL_FOLDER'";
        else
            warn "Folder '$DL_FOLDER' already exists"
        fi
        info "Will use the '$DL_FOLDER' to dl found stickers"
        shift 2
    fi

    local FILE="${STPKG_INSTALL}web/packs/$1.json"
    [ ! -r "$FILE" ] && die "Pack '$1' is not available (check with 'pack list'). The corresponding file should be '$FILE'"
    echo "Display the sticker pack $(__green `jq '.title' < "$FILE"`):"

    local FIRST="yes"
    jq '.stickers[] | "\(.body) \(.info.thumbnail_url) \(.info.mimetype)"' < "$FILE" | while IFS= read LINE; do
        local NAME=`echo "$LINE" | awk -F '"| ' '{print $2}'`
        local URL=` echo "$LINE" | awk -F '"| ' '{print $3}'`
        local TYPE=`echo "$LINE" | awk -F '"| ' '{print $4}' | awk -F '/' '{print $2}'`
        [ ! "x$2" = "x" ] && { [[ "$NAME" =~ $2 ]] || continue; }
        local URL=`mxc_to_https "$URL"`

        # Header if first
        if [ "x$FIRST" = "xyes" ]; then
            local FIRST="no"
            echo "Name Download_URL Type"
        fi

        echo -ne "$NAME $URL $TYPE"
        if [ "x$DL_SWITCH" = "xyes" ]; then
            curl "$URL" --create-dirs --output "$DL_FOLDER/$NAME.$TYPE" >/dev/null 2>&1 \
                && echo " `__green DL`" \
                || echo " `__red Failed`"
        else
            echo ""
        fi
    done | column -t
}

__list() {
    # Handle -e and -p flags
    local PARSE_FLAGS=1
    while [ $PARSE_FLAGS -eq 1 ]; do
        case "$1" in
            -e)
                local FILTER_ENABLED="yes"
                shift
                ;;
            -ne)
                local FILTER_DISABLED="yes"
                shift
                ;;
            -np)
                local FILTER_NOT_PRESENT="yes"
                shift
                ;;
            -p)
                local FILTER_PRESENT="yes"
                shift
                ;;
            *)
                local PARSE_FLAGS=0
                ;;
        esac
    done

    [ "x$FILTER_PRESENT" = "xyes" -a "x$FILTER_NOT_PRESENT" = "xyes" ] \
        && die "The -p and -np flags are incompatible"
    [ "x$FILTER_ENABLED" = "xyes" -a "x$FILTER_DISABLED" = "xyes" ] \
        && die "The -e and -ne flags are incompatible"

    # Get the regex
    local REG="$*"

    # A sticker pack can be:
    # - enabled and present in the picker folder
    # - disabled and present in the picker folder
    # - orphan when the Json file is present in the picker folder but not in the pack repo
    # Note that the Json files from the sticker repo are copied at update time
    # in the sticker repo picker folder.
    # A pack can be 'orphan' and 'enabled', or 'orphan' and 'disabled'. But
    # 'enabled' and 'disabled' are exclusive tags.

    ls -d1 $STPKG_INSTALL/web/packs/*.json | grep -v index.json | while IFS= read -r LINE; do
        local LINE=`basename $LINE .json`

        [ ! "x$REG" = "x" ] && ! [[ "$LINE" =~ $REG ]] && continue

        local PRESENT=`find $STPKG_BASE/packs/ -type f -name "$LINE.json"`
        [ "x$FILTER_PRESENT"     = "xyes" -a   "x$PRESENT" = "x" ] && continue
        [ "x$FILTER_NOT_PRESENT" = "xyes" -a ! "x$PRESENT" = "x" ] && continue
        local PRESENT=`[ ! "x$PRESENT" = "x" ] && echo 'p' || echo 'a'`

        local ENABLED=`jq -r ".packs | index(\"$LINE.json\") | ." < $STPKG_INSTALL/web/packs/index.json`
        [ "x$FILTER_ENABLED"  = "xyes" -a   "x$ENABLED" = "xnull" ] && continue
        [ "x$FILTER_DISABLED" = "xyes" -a ! "x$ENABLED" = "xnull" ] && continue
        local ENABLED=`[ "x$ENABLED" = "xnull" ] && echo 'd' || echo 'e'`

        echo -ne "$PRESENT$ENABLED $LINE\n"
    done
}

# Usage: process_sticker_file <filename> <&JSON>
process_sticker_file() {
    FILE="$1"
    local -n JSON_REF="$2"

    if [ "x$STPKG_TOKEN" = "x" ]; then
        read -sp "`__yellow 'Enter your access token:'`" STPKG_TOKEN
        validate_token # Validate token, will exit if invalid
    fi

    # Get the names and extensions
    local INIT_TYPE=$(file_get_type "$FILE")
    local INIT_EXT=$(file_get_ext_from_type "$INIT_TYPE")
    local NAME=$(file_get_name "$FILE")
    local TEMP=`mktemp --suffix=.stpkg`

    if [[ ! ${animatedFiletype[$INIT_TYPE]} ]]; then
        local DEST_TYPE="png"
    else
        local DEST_TYPE="$INIT_TYPE"
    fi
    local DEST="$DEST_FOLDER/$NAME.$DEST_TYPE"

    if [ "x${STPKG_NOCONVERT_FILE}" = "xyes" ]; then
        #
        # Don't convert image, upload it as it is
        #
        local DEST_TYPE=$INIT_TYPE
        cp ${FILE} ${TEMP}

    else
        if [[ ${animatedFiletype[$INIT_TYPE]} ]]; then
            #
            # Convert animated image
            #
            if [[ "$INIT_TYPE" == "image/apng" ]] && [[ "$STPKG_ALLOW_APNG" == "yes" ]]; then
                # FIXME: properly crop apngs

                local DEST_TYPE="image/apng"

                # Get sizes
                local INIT_WIDTH=$(file_get_width "${FILE}")
                local INIT_HEIGHT=$(file_get_height "${FILE}")
                local DEST_DIMENSIONS=$(__get_dest_dimensions $INIT_WIDTH $INIT_HEIGHT $STPKG_MAX_WIDTH $STPKG_MAX_HEIGHT ":" "-1")

                ffmpeg -y -loglevel warning -f apng -i "${FILE}" -plays 0 -vf scale=${DEST_DIMENSIONS} -f apng "${TEMP}" >/dev/null
            else
                local DEST_TYPE=$STPKG_ANIMATED_TARGET_FILETYPE
                local DEST_EXT=$(file_get_ext_from_type "$DEST_TYPE")

                local backgroundColor="`convert "${FILE}[0]" -format "%[pixel:u.p{0,0}]" info:`"
                convert "${INIT_EXT}:${FILE}" -dispose previous -background "${backgroundColor}" -trim -layers TrimBounds -coalesce "$DEST_EXT:${TEMP}" 1>&2 \
                    || die "$NAME failed trimming the sticker"

                # Get sizes
                local INIT_WIDTH=$(file_get_width "${TEMP}")
                local INIT_HEIGHT=$(file_get_height "${TEMP}")
                local DEST_DIMENSIONS=$(__get_dest_dimensions $INIT_WIDTH $INIT_HEIGHT $STPKG_MAX_WIDTH $STPKG_MAX_HEIGHT "x" "")

                convert "${TEMP}" -coalesce -dispose previous -resize ${DEST_DIMENSIONS} "$DEST_EXT:${TEMP}" 1>&2 \
                    || die "$NAME failed resizing the sticker"

                # Can't optimize the gif because discord doesn't know how to display them properly (for bridged channels)
                #convert "${TEMP}" -coalesce -layers Optimize "$DEST_EXT:${TEMP}" 1>&2 \
            fi

        else
            #
            # Convert fixed image
            #
            local DEST_TYPE=$STPKG_IMAGE_TARGET_FILETYPE
            local DEST_EXT=$(file_get_ext_from_type "$DEST_TYPE")

            convert "$FILE" -trim "${INIT_EXT}:${TEMP}"

            # Get sizes
            local INIT_WIDTH=$(file_get_width "$TEMP")
            local INIT_HEIGHT=$(file_get_height "$TEMP")
            local DEST_DIMENSIONS=$(__get_dest_dimensions $INIT_WIDTH $INIT_HEIGHT $STPKG_MAX_WIDTH $STPKG_MAX_HEIGHT "x" "")

            convert "$TEMP" -bordercolor none -border 1 -background none -gravity center -trim +repage -resize ${DEST_DIMENSIONS} "${DEST_EXT}:${TEMP}" 1>&2 \
                || die "$NAME failed converting the sticker"
        fi

    fi

    # Get real final dimensions for json
    DEST_WIDTH=$(file_get_width "$TEMP")
    DEST_HEIGHT=$(file_get_height "$TEMP")

    # Upload the transformed file
    local MXC
    __upload_file "$DEST_TYPE" "$TEMP" "$NAME"
    rm "$TEMP"

    JSON_REF=$(create_sticker_json "$PACK_NAME" "$NAME" "$DEST_WIDTH" "$DEST_HEIGHT" "$DEST_TYPE" "$MXC")
}

__edit() {
    # <pack name> <add|del> <sticker> [image file to use]
    local PACK_NAME="$1"
    local ACTION="$2"
    local STICKER_NAME="$3"
    [ -z "$PACK_NAME" -o -z "$ACTION" -o -z "$STICKER_NAME" ] && die "Command argument are invalid, check the usage"
    shift 3

    local JSON_FILE="${STPKG_INSTALL}web/packs/$PACK_NAME.json"
    [ ! -r "$JSON_FILE" ] && die "Can't read file '$JSON_FILE' associated to the '$PACK_NAME' pack"

    local TEMP=`mktemp --suffix=.stpkg`
    cp $JSON_FILE $TEMP

    case "$ACTION" in
        add)
            [ $# -eq 0 ] && die "You must specify an image file for the add action"
            local FILE="$1"
            [ ! -r "$FILE" ] && die "Failed to find file '$FILE'"

            local ST_JSON=""
            process_sticker_file "$FILE" ST_JSON

            jq "del(.stickers[] | select(.body == \"$STICKER_NAME\"))" < "$TEMP" | sponge "$TEMP"
            jq ".stickers += [$ST_JSON]" < $TEMP | sponge $TEMP
            ;;

        del)
            [ $# -ne 0 ] && die "Extra argument are present: $*"
            jq "del(.stickers[] | select(.body == \"$STICKER_NAME\"))" < "$TEMP" | sponge "$TEMP"
            ;;

        *)
            die "Unknown action '$ACTION', should be 'add' or 'del'"
            ;;
    esac

    # TODO [OPTIONAL] Update the preview
    # TODO Add a dry mode, to only preview the change
    # TODO If the pack was already added to the picker, re-add it to take into
    #      account the modifications
    #jq < $TEMP ### to test/review the change
    cp $TEMP $JSON_FILE
}

__add() {
    [ "x$1" = "x" ] && die "You must specify a pack for the 'add' command"
    info "Enable pack '$1'"

    local PRESENT=`find $STPKG_INSTALL/web/packs/ -type f -name "$1.json" | grep -v index.json`
    [ "x$PRESENT" = "x" ] && die "Pack '$1' is not present"

    # Use a temp file because of sponge
    local TEMP=`mktemp --suffix=.stpkg`
    cp $STPKG_INSTALL/web/packs/index.json $TEMP
    jq ".packs += [\"$1.json\"]" < $TEMP | sponge $TEMP
    cp $TEMP $STPKG_INSTALL/web/packs/index.json
}

__del() {
    [ "x$1" = "x" ] && die "You must specify a pack 'del' command"
    info "Disable pack '$1'"
    local TEMP=`mktemp --suffix=.stpkg`
    cp $STPKG_INSTALL/web/packs/index.json $TEMP
    jq "{ \"homeserver_url\": .homeserver_url, \"packs\": .packs | map(select(. != \"$1.json\")) }" \
        < $TEMP | sponge $TEMP
    cp $TEMP $STPKG_INSTALL/web/packs/index.json
}

__default() {
    info "Reset the index.json in the sticker picker install directory"
    default_index > $STPKG_INSTALL/web/packs/index.json || die "Failed to create default index.json file"
}

__pack() {
    # The pack source folder and the pack name
    if [ $# -eq 1 ]; then
        # The pack name is not specified
        local PACK_FOLDER="$1"
        local PACK_NAME=${PACK_FOLDER##*/}
    else if [ $# -eq 2 ]; then
        # The pack name is specified
        local PACK_NAME="$1"
        local PACK_FOLDER="$2"
    else
        die "Invalid number of arguments for the 'pack' command"
    fi fi
    local PACK_NAME=`slugify $PACK_NAME`
    info "Will pack the folder '$PACK_FOLDER' into '$PACK_NAME'"
    [ ! -d "$PACK_FOLDER" ] && die "Source folder '$PACK_FOLDER' doesn't exist"

    # Check packs
    local DEST_FOLDER="$STPKG_BASE/packs/$PACK_NAME"
    [ -d "$DEST_FOLDER" ] \
        && { warn "Old folder for pack $PACK_NAME exists, using checksums to reupload necessary stickers"; } \
        || { { info "Create folder for pack $PACK_NAME" ; mkdir "$DEST_FOLDER"; } \
           || die "Failed to create destination folder for pack '$PACK_NAME'"; }
    #[ -d "$DEST_FOLDER" ] \
    #    && { { warn "Delete old folder content for pack $PACK_NAME" ; rm "$DEST_FOLDER"/*; } \
    #       || die "Failed to remove old folder pack content"; } \
    #    || { { info "Create folder for pack $PACK_NAME" ; mkdir "$DEST_FOLDER"; } \
    #       || die "Failed to create destination folder for pack '$PACK_NAME'"; }
    local DEST_INDEX="$DEST_FOLDER/$PACK_NAME.json"
    touch $DEST_INDEX
    [ ! -f $DEST_INDEX ] && die "Failed to create the index file '$DEST_INDEX'"

    # Populate all stickers
    local TEMP_JSON=`mktemp --suffix=.stpkg`
    echo -n "{\"title\":\"$PACK_NAME\",\"id\":\"`uuidgen`\",\"stickers\":[" > $TEMP_JSON

    cd $PACK_FOLDER
    local TOTAL_FILES=`find . -maxdepth 1 -type f | wc -l`
    local CURRENT_FILE=1
    local FIRST_IN_ARRAY=""

    # Check checksums
    local SUM_FILE="$DEST_FOLDER/.$PACK_NAME.checksums"
    if [ ! -f "$SUM_FILE" ]; then
        touch $SUM_FILE
    fi
    local -A hashes unchangedFiles updatedFiles deletedFiles
    local TEMP_CHECK=`mktemp --suffix=.stpkg`
    md5sum -c $SUM_FILE &>/dev/null >$TEMP_CHECK
    while read -r _line; do
        _hash="`echo "$_line" | sed 's+^\([^ ]*\)  \(.*\)$+\1+'`"
        _file="`echo "$_line" | sed 's+^\([^ ]*\)  \(.*\)$+\2+'`"
        hashes["$_file"]="$_hash"
    done < $SUM_FILE

    while read -r i; do
        if [ "x$i" = "x" ]; then continue; fi
        if [ "x$STPKG_REUPLOAD_SAME_FILE" = "xyes" ]; then
            updatedFiles["$i"]="${hashes["$i"]}"
        else
            unchangedFiles["$i"]="${hashes["$i"]}"
        fi
    done < <(cat $TEMP_CHECK | grep ': OK$' | sed 's/\(.*\): OK/\1/')

    while read -r i; do
        if [ "x$i" = "x" ]; then continue; fi
        updatedFiles["$i"]="${hashes["$i"]}"
    done < <(cat $TEMP_CHECK | grep ': FAILED$' | sed 's/\(.*\): FAILED/\1/')

    while read -r i; do
        if [ "x$i" == "x" ]; then continue; fi
        deletedFiles["$i"]="${hashes["$i"]}"
        echo "add $i to deleted"
    done < <(cat $TEMP_CHECK | grep ': FAILED open or read$' | sed 's/\(.*\): FAILED open or read/\1/')

    local ST_JSON=""
    echo "----- Pack -----"

    local COLUMN_SEP="🌝"
{
    for FILE in *; do
        # Add a ',' only if it's not the first in the array
        echo -n "$FIRST_IN_ARRAY" >> $TEMP_JSON
        local STICKER_STATUS=""
        local STICKER_NAME="`file_get_name "$FILE"`"

        if [[ "x${unchangedFiles[$FILE]}" != "x" ]]; then
            STICKER_STATUS="Unchanged"
            ST_JSON="`jq --arg STICKER_NAME "$STICKER_NAME" '.stickers[] | select(.body == $STICKER_NAME)' < "$DEST_INDEX"`"
        else
            if [[ "x${updatedFiles[$FILE]}" != "x" ]]; then
                STICKER_STATUS="`__yellow Updated`"
                sed -i "/[0-9a-f]\{32\}  $FILE/d" $SUM_FILE
            else
                STICKER_STATUS="`__green New`"
                if [[ `md5sum "$FILE" |  sed -e 's/\([0-9a-f]\{32\}\)  .*$/\1/'` = `cat $SUM_FILE | grep "$FILE" | sed -e 's/\([0-9a-f]\{32\}\)  .*$/\1/'` ]]; then
                    die "New file is replacing existing file, that should not be happening"
                fi
            fi

            process_sticker_file "$FILE" ST_JSON
            md5sum "$FILE" >> $SUM_FILE
        fi

        echo "$ST_JSON" >> "$TEMP_JSON"

        # For the report
        [ "x$FIRST_IN_ARRAY" = "x" ] && echo -e "StickerName${COLUMN_SEP}MXC${COLUMN_SEP}Type${COLUMN_SEP}Width${COLUMN_SEP}Height${COLUMN_SEP}Status${COLUMN_SEP}"
        FIRST_IN_ARRAY=","
        local report="`echo -ne "$ST_JSON" | jq -r --arg COLUMN_SEP "$COLUMN_SEP" '[.body, .url, .info.mimetype, .info.w, .info.h]|join($COLUMN_SEP)'`${COLUMN_SEP}${STICKER_STATUS}"
        echo "$report"
    done

    for i in "${!deletedFiles[@]}"; do
        local STICKER_NAME="$(file_get_name "$i")"
        ST_JSON="`jq --arg STICKER_NAME "$STICKER_NAME" '.stickers[] | select(.body == $STICKER_NAME)' < "$DEST_INDEX"`"
        sed -i "/[0-9a-f]\{32\}  $i/d" $SUM_FILE
        local report="`echo -ne "$ST_JSON" | jq -r --arg COLUMN_SEP "$COLUMN_SEP" '[.body, .url, .info.mimetype, .info.w, .info.h]|join($COLUMN_SEP)'`$COLUMN_SEP`__red Deleted`"
        echo "$report"
    done
}   | column -t -s "$COLUMN_SEP"
    echo "----------------"

    echo -ne "# $PACK_NAME\n(no preview)" > "$DEST_FOLDER/README.md"
    find $DEST_FOLDER -type f \( ! -name "preview.png" -and ! -name "$PACK_NAME.json" -and ! -name '.*.checksums' \) -exec rm {} \;

    echo -n "]}" >> $TEMP_JSON
    cat $TEMP_JSON | jq > $DEST_INDEX

    # Sort checksums to get better diffs
    cat $SUM_FILE | sort | sponge $SUM_FILE

    info "Pack created, you can now commit it and create a MR to share it with other users"
}

__do_command() {
    case "$1" in
        show|update|list|add|del|default|pack|display|edit)
            # Source conf file, may have the sshfs hook
            if [ -f $STPKG_CONF_FILE ]; then
                source $STPKG_CONF_FILE
            else
                error "Conf file \"$STPKG_CONF_FILE\" doesn't exist. Did you use the install command?"
                error "Help:"
                __usage
            fi

            __do_mount

            # Get some exec / paths #
            STPKG_INSTALL=${STPKG_INSTALL:="~/html/stickerpicker/"}
            if [ ! -d "$STPKG_INSTALL" ]; then
                error "STPKG_INSTALL folder ($STPKG_INSTALL) doesn't exist. Did you use the install script?"
                __usage
            fi
            info "Using STPKG_INSTALL: $STPKG_INSTALL`[ "x$STPKG_SSHFS" = "xyes" ] && echo ' (this is an sshfs install)'`"

            [ "x$STPKG_BASE" = "x" ] && die \
                "The STPKG_BASE env var is mandatory, it must point to the sticker repo. It should have been given by the install script"
            ;;&

        edit|pack)
            local CURRCOMMAND="$1"
            shift

            # Home server
            STPKG_HOMESERV=${STPKG_HOMESERV:="matrix.org"}
            STPKG_HOMESERV=`echo "$STPKG_HOMESERV" | iconv -c -t ascii//TRANSLIT | sed -e 's/^http:\/\/|^https:\/\///g' -e 's/^-+|-+$//g'`

            validate_homeserv # Check if homeserv exists

            # Get the token
            if [ "x$1" = "x-t" ]; then
                STPKG_TOKEN="$2"
                shift 2
            fi

            if [[ "$STPKG_TOKEN" != "" ]]; then
              validate_token
            fi

            local PARSE_FLAGS=1
            while [ $PARSE_FLAGS -eq 1 ]; do
                case "$1" in
                    # Overwrite max width
                    -w|-width)
                        STPKG_MAX_WIDTH="$2"
                        shift 2
                        ;;

                    # Overwrite max height
                    -h|-height)
                        STPKG_MAX_HEIGHT="$2"
                        shift 2
                        ;;

                    # Do we need to resize/convert the files we are given?
                    -noconvert)
                        STPKG_NOCONVERT_FILE="yes"
                        shift
                        ;;

                    # What filetype do we want our image stickers to be?
                    -ti)
                        STPKG_IMAGE_TARGET_FILETYPE="$2"
                        shift 2
                        ;;

                    # What filetype do we want our animated stickers to be?
                    -ta)
                        STPKG_ANIMATED_TARGET_FILETYPE="$2"
                        shift 2
                        ;;

                    # Generate preview of the sticker pack
                    -preview)
                        STPKG_GENERATE_PREVIEW="yes"
                        die "Asking for a preview generation, while this feature has been dropped for the moment"
                        shift
                        ;;

                    # Do not reupload unchanged stickers
                    -reupload)
                        STPKG_REUPLOAD_SAME_FILE="yes"
                        shift
                        ;;

                    *)
                        local PARSE_FLAGS=0
                        ;;
                esac
            done

            # Default values
            STPKG_MAX_WIDTH=${STPKG_MAX_WIDTH:="256"}
            STPKG_MAX_HEIGHT=${STPKG_MAX_HEIGHT:="128"}
            STPKG_NOCONVERT_FILE=${STPKG_NOCONVERT_FILE:="no"}
            STPKG_GENERATE_PREVIEW=${STPKG_GENERATE_PREVIEW:="no"}
            STPKG_IMAGE_TARGET_FILETYPE=${STPKG_IMAGE_TARGET_FILETYPE:="image/png"}
            STPKG_ANIMATED_TARGET_FILETYPE=${STPKG_ANIMATED_TARGET_FILETYPE:="image/gif"}
            STPKG_REUPLOAD_SAME_FILE=${STPKG_REUPLOAD_SAME_FILE:="no"}

            __$CURRCOMMAND $*
            exit
            ;;&

        *)
            ;;
    esac
    __$*
}

######################
# Parse command line #
######################
[ "x$1" = "x" ] && __usage
case "$1" in
    show)               __do_command show      ;;
    update)             __do_command update    ;;
    list)       shift;  __do_command list $*   ;;
    add)        shift;  __do_command add $*    ;;
    del)        shift;  __do_command del $*    ;;
    default)            __do_command default   ;;
    pack)       shift;  __do_command pack $*   ;;
    display)    shift;  __do_command display $*;;
    edit)       shift;  __do_command edit $*   ;;
    *)                  __do_command usage     ;;
esac
