#!/usr/bin/env bash
# VOICEVOX Installer Script

# set -x # Debug mode: output verbose log
set -euo pipefail
IFS=$'\n\t'

_readarray(){
  [[ -z "$1" ]] && return 1
  [[ "$1" =~ [[:space:]] ]] && return 1
  var="$1"
  eval "$var"'=()'
  while IFS=$'\n' read -r r; do
    eval "$var"'+=("'"$r"'")'
  done
}

cat << 'BANNER'
+-+-+-+-+-+-+-+-+
|V|O|I|C|E|V|O|X|
+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |I|n|s|t|a|l|l|e|r|
+-+-+-+-+-+-+-+-+-+-+-+-+-+
|f|o|r| |L|i|n|u|x|
+-+-+-+-+-+-+-+-+-+
BANNER

NAME=$(basename "${NAME:-linux-nvidia-appimage}")
VERSION=$(basename "${VERSION:-}")
REPO_URL=${REPO_URL:-https://github.com/VOICEVOX/voicevox}

# Install directory
APP_DIR=${APP_DIR:-$HOME/.voicevox}
# force install if [ ${FORCE_INSTALL} = 1 ]
FORCE_INSTALL=${FORCE_INSTALL:-}
# keep archive if [ ${KEEP_ARCHIVE} = 1 ]
KEEP_ARCHIVE=${KEEP_ARCHIVE:-}
REUSE_LIST=${REUSE_LIST:-}
SKIP_VERIFY=${SKIP_VERIFY:-}
IGNORE_RTCOND=${IGNORE_RTCOND:-}

DESKTOP_ENTRY_INSTALL_DIR=${DESKTOP_ENTRY_INSTALL_DIR:-$HOME/.local/share/applications}
ICON_INSTALL_DIR=${ICON_INSTALL_DIR:-$HOME/.local/share/icons}
MIME_INSTALL_DIR=${MIME_INSTALL_DIR:-$HOME/.local/share/mime}

if [ "$FORCE_INSTALL" != "1" ] && [ -f "${APP_DIR}/VOICEVOX.AppImage" ]; then
    echo "[*] VOICEVOX already installed in '${APP_DIR}/VOICEVOX.AppImage'."
    while true; do
        read -r -p "[*] Replace?(y/n): " yn
        case "$yn" in
            [Yy]*)
                break
                ;;
            [Nn]*)
                exit 0
                ;;
            *)
                echo "[*] Please answer y(es) or n(o)."
                ;;
        esac
    done
fi

echo "[+] Checking installer prerequisites..."

if ! command -v curl &> /dev/null; then
    cat << EOS && exit 1
[!] Command 'curl' not found

Required to download VOICEVOX

Ubuntu/Debian:
    sudo apt install curl

CentOS/Fedora:
    sudo dnf install curl
Or
    sudo yum install curl

Arch Linux:
    sudo pacman -S curl
EOS
fi

COMMAND_7Z=${COMMAND_7Z:-}
if [ -n "${COMMAND_7Z}" ]; then
    # use env var
    :
elif command -v 7z &> /dev/null; then
    # Ubuntu/Debian p7zip-full
    COMMAND_7Z=7z
elif command -v 7zr &> /dev/null; then
    # Ubuntu/Debian p7zip
    COMMAND_7Z=7zr
elif command -v 7za &> /dev/null; then
    # CentOS/Fedora
    COMMAND_7Z=7za
else
    cat << 'EOS' && exit 1
[!] Command '7z', '7zr' or '7za' not found

Required to extract compressed files

Ubuntu/Debian:
    sudo apt install p7zip

CentOS (Enable EPEL repository):
    sudo dnf install epel-release && sudo dnf install p7zip
Or
    sudo yum install epel-release && sudo yum install p7zip

Fedora:
    sudo dnf install p7zip
Or
    sudo yum install p7zip

Arch Linux:
    sudo pacman -S p7zip

MacOS:
    brew install p7zip
EOS
fi
echo "[-] 7z command: ${COMMAND_7Z}"

echo "[+] Checking runtime prerequisites..."

PATH=${PATH}:/usr/local/sbin:/usr/sbin:/sbin
if ! command -v ldconfig &> /dev/null; then
    cat << EOS && exit 1
[!] Command 'ldconfig' not found

Required to check existence of required libraries.
You must add a directory of contain ldconfig command to PATH environment variable.

EOS
fi

if { ldconfig -p | grep 'libsndfile\.so';} &>/dev/null; then
    echo "[-] libsndfile: OK"
elif [ -d /usr/local/Cellar/libsndfile ]; then
    echo "[-] libsndfile: OK"
else
    cat << 'EOS'
[!] libsndfile: not found

Required to run VOICEVOX ENGINE

Ubuntu/Debian:
    sudo apt install libsndfile1

CentOS/Fedora:
    sudo dnf install libsndfile
Or
    sudo yum install libsndfile

Arch Linux
    sudo pacman -S libsndfile

MacOS:
    brew install libsndfile
EOS
    if [ "${IGNORE_RTCOND}" != "1" ]; then
        exit 1
    fi
fi

LATEST_RELEASE_URL=$REPO_URL/releases/latest

if [ -z "${VERSION}" ]; then
    echo "[+] Checking the latest version..."

    # releases/tag/{version}
    RELEASE_TAG_URL=$(curl -fsSL -o /dev/null -w '%{url_effective}' "${LATEST_RELEASE_URL}")

    # extract version (release tag name) from URL
    VERSION=$(echo "${RELEASE_TAG_URL}" | sed 's/.*\/\(.*\)$/\1/')
    echo "[-] Install version: ${VERSION} (latest)"
else
    echo "[-] Install version: ${VERSION}"
fi

RELEASE_URL=${REPO_URL}/releases/download/${VERSION}
ARCHIVE_LIST_URL=${RELEASE_URL}/${NAME}.7z.txt

echo "[-] Install directory: ${APP_DIR}"
mkdir -p "${APP_DIR}"

cd "${APP_DIR}"

# Download archive list
if [ "$REUSE_LIST" != "1" ]; then
    echo "[+] Downloading ${ARCHIVE_LIST_URL}..."
    curl --fail -L -o "list.txt" "${ARCHIVE_LIST_URL}"
fi

echo
echo "[+] Listing of split archives..."
_readarray ARCHIVE_LIST < "list.txt"

if [ -z "$(echo "${ARCHIVE_LIST[0]}" | awk '$0=$1')" ]; then
    # No size/hash information
    # filename
    _readarray ARCHIVE_NAME_LIST < <(
        for line in "${ARCHIVE_LIST[@]}"; do
            echo "$line"
        done
    )
    _readarray ARCHIVE_SIZE_LIST < <(
        for index in "${!ARCHIVE_LIST[@]}"; do
            echo "x"
        done
    )
    _readarray ARCHIVE_HASH_LIST <(
        for index in "${!ARCHIVE_LIST[@]}"; do
            echo "x"
        done
    )
else
    # filename<TAB>size<TAB>hash
    _readarray ARCHIVE_NAME_LIST < <(
        for line in "${ARCHIVE_LIST[@]}"; do
            echo "$line"
        done | awk '$0!=""{print $1}'
    )
    _readarray ARCHIVE_SIZE_LIST < <(
        for line in "${ARCHIVE_LIST[@]}"; do
            echo "$line"
        done | awk '$0!=""{print $2}'
    )
    _readarray ARCHIVE_HASH_LIST < <(
        for line in "${ARCHIVE_LIST[@]}"; do
            echo "$line"
        done | awk '$0!=""{print $3}' |
            tr '[:lower:]' '[:upper:]'
    )
fi
echo

for index in "${!ARCHIVE_NAME_LIST[@]}"; do
    echo "${index}." \
        "${ARCHIVE_NAME_LIST[index]}" \
        "${ARCHIVE_SIZE_LIST[index]}" \
        "${ARCHIVE_HASH_LIST[index]}"
done
echo

# Download archives
for index in "${!ARCHIVE_LIST[@]}"; do
    FILENAME=${ARCHIVE_NAME_LIST[index]}
    SIZE=${ARCHIVE_SIZE_LIST[index]}
    HASH=${ARCHIVE_HASH_LIST[index]}

    URL=${RELEASE_URL}/${FILENAME}

    echo "[+] Downloading ${URL}..."
    if [ ! -f "${FILENAME}" ]; then
        curl --fail -L -C - -o "${FILENAME}.tmp" "${URL}"
        mv "${FILENAME}.tmp" "${FILENAME}"
    fi

    # File verification (size, md5 hash)
    if [ "$SKIP_VERIFY" = "1" ]; then
        echo "[-] File verification skipped"
    else
        if [ "$SIZE" != "x" ]; then
            echo "[+] Verifying size == ${SIZE}..."
            if stat --version &>/dev/null; then
                DOWNLOADED_SIZE=$(stat --printf="%s" "${FILENAME}")
            else
                DOWNLOADED_SIZE=$(stat -f%z "${FILENAME}")
            fi

            if [ "$DOWNLOADED_SIZE" = "$SIZE" ]; then
                echo "[-] Size OK"
            else
                cat << EOS && exit 1
[!] Invalid size: ${DOWNLOADED_SIZE} != ${SIZE}

Remove the corrupted file and restart installer!

    rm $(realpath "${FILENAME}")

EOS
            fi
        fi

        if [ "$HASH" != "x" ]; then
            echo "[+] Verifying hash == ${HASH}..."
            DOWNLOADED_HASH=$(md5sum "${FILENAME}" | awk '$0=$1' | tr '[:lower:]' '[:upper:]')
            if [ "$DOWNLOADED_HASH" = "$HASH" ]; then
                echo "[-] Hash OK"
            else
                cat << EOS && exit 1
[!] Invalid hash: ${DOWNLOADED_HASH} != ${HASH}

Remove the corrupted file and restart installer!

    rm $(realpath "${FILENAME}")

EOS
            fi
        fi
    fi
done

# Extract archives
echo "[+] Extracting archive..."
FIRST_ARCHIVE=${ARCHIVE_NAME_LIST[0]}
"${COMMAND_7Z}" x "${FIRST_ARCHIVE}" -y

# Get AppImage filename from 7z archive
APPIMAGE=$("${COMMAND_7Z}" l -slt -ba "${FIRST_ARCHIVE}" | grep 'Path = ' | head -n1 | sed 's/Path = \(.*\)/\1/')
chmod +x "${APPIMAGE}"

# Dump version
echo "[+] Dumping version..."
echo "${VERSION}" > VERSION

# Create uninstaller
echo "[+] Creating uninstaller..."
cat << EOS > uninstaller_linux.sh && chmod +x uninstaller_linux.sh
#!/usr/bin/env bash

set -euo pipefail
IFS=\$'\n\t'

cat << 'BANNER'
+-+-+-+-+-+-+-+-+
|V|O|I|C|E|V|O|X|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |U|n|i|n|s|t|a|l|l|e|r|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|f|o|r| |L|i|n|u|x|
+-+-+-+-+-+-+-+-+-+
BANNER

VOICEVOX_INSTALLED_FILES=(
    "${DESKTOP_ENTRY_INSTALL_DIR}/voicevox.desktop"
    "${ICON_INSTALL_DIR}/voicevox.png"
    "${ICON_INSTALL_DIR}/hicolor/0x0/apps/voicevox.png"
    "${MIME_INSTALL_DIR}/packages/voicevox.xml"
)

VOICEVOX_INSTALLED_DIR=(
    "${APP_DIR}"
)

echo "[+] Uninstalling VOICEVOX..."
for i in "\${VOICEVOX_INSTALLED_FILES[@]}"; do
    [ -e "\$i" ] || continue
    echo "[+] Removing '\${i}'..."
    if [ -f "\$i" ]; then
        rm -f "\$i"
    else
        echo "[!] '\$i' is not a file"
        exit 1
    fi
done

for i in "\${VOICEVOX_INSTALLED_DIR[@]}"; do
    [ -e "\$i" ] || continue
    echo "[+] Removing '\${i}'..."
    if [ -d "\$i" ]; then
        rm -rf "\$i"
    else
        echo "[!] '\$i' is not a directory"
        exit 1
    fi
done

echo "[+] Done! VOICEVOX has been uninstalled."

EOS

# Remove archives
if [ "${KEEP_ARCHIVE}" != "1" ]; then
    echo "[+] Removing split archives..."

    for filename in "${ARCHIVE_NAME_LIST[@]}"; do
        echo "[+] Removing ${filename}..."
        rm -f "${filename}"
    done
fi

# Remove archive list
echo "[+] Removing archive list (list.txt)..."
rm -f "list.txt"

# Extract desktop entry
echo "[+] Extacting desktop entry..."

"./${APPIMAGE}" --appimage-extract '*.desktop'
"./${APPIMAGE}" --appimage-extract 'usr/share/icons/**'
"./${APPIMAGE}" --appimage-extract '*.png' # symbolic link to icon

# Install desktop entry
echo "[+] Installing desktop entry..."

DESKTOP_FILE=$(find squashfs-root -maxdepth 1 -name '*.desktop' | head -1)
chmod +x "${DESKTOP_FILE}"

ESCAPED_APP_DIR=$(echo "$APP_DIR" | sed 's/\//\\\//g')
sed "s/Exec=.*/Exec=${ESCAPED_APP_DIR}\/${APPIMAGE} %U/" "${DESKTOP_FILE}" > _
mv _ "${DESKTOP_FILE}"

mkdir -p "${DESKTOP_ENTRY_INSTALL_DIR}"
mv "${DESKTOP_FILE}" "${DESKTOP_ENTRY_INSTALL_DIR}"

# Install icon
echo "[+] Installing icon..."

mkdir -p "${ICON_INSTALL_DIR}"
cp -r squashfs-root/usr/share/icons/* "${ICON_INSTALL_DIR}"
cp squashfs-root/*.png "${ICON_INSTALL_DIR}"

# Register file association
echo "[+] Registering file association..."

mkdir -p "${MIME_INSTALL_DIR}/packages"
cat << EOS > "${MIME_INSTALL_DIR}/packages/voicevox.xml"
<?xml version="1.0" encoding="utf-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
    <mime-type type="application/x-voicevox">
        <comment>VOICEVOX Project file</comment>
        <comment xml:lang="ja">VOICEVOX プロジェクトファイル</comment>
        <sub-class-of type="application/json" />
        <glob pattern="*.vvproj" />
        <icon name="voicevox" />
    </mime-type>
    <mime-type type="application/x-voicevox-plugin-package">
        <comment>VOICEVOX Plugin package</comment>
        <comment xml:lang="ja">VOICEVOX プラグインパッケージ</comment>
        <sub-class-of type="application/zip" />
        <glob pattern="*.vvpp" />
        <icon name="voicevox" />
    </mime-type>
    <mime-type type="application/x-voicevox-plugin-package-part">
        <comment>VOICEVOX Plugin package (part)</comment>
        <comment xml:lang="ja">VOICEVOX プラグインパッケージ(分割)</comment>
        <sub-class-of type="application/zip" />
        <glob pattern="*.vvppp" />
        <icon name="voicevox" />
    </mime-type>
</mime-info>
EOS

# Update file association database
echo "[+] Updating file association database..."
if command -v update-mime-database &> /dev/null; then
    update-mime-database "${MIME_INSTALL_DIR}"
else
    echo "[-] Skipped: Command 'update-mime-database' not found"
fi

# Update desktop file database
echo "[+] Updating desktop file database..."
if command -v update-desktop-database &> /dev/null; then
    update-desktop-database "${DESKTOP_ENTRY_INSTALL_DIR}"
else
    echo "[-] Skipped: Command 'update-desktop-database' not found"
fi

# Remove extract dir
echo "[+] Removing temporal directory..."
rm -rf squashfs-root

echo "[+] All done! VOICEVOX ${VERSION} has been installed under '${APP_DIR}'."