#!/bin/sh
# Dependencies: getopt, synfig, inkscape, imagemagick, pngquant, xz

# Copyright (C) 2010-2023 Duncan Deveaux
#
# This file is part of Hikou no mizu.
#
# Hikou no mizu is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Hikou no mizu is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.


datasrcdir=.            #Input data source dir
datadir=../data         #Output built data dir
tmpdir=hnm_tmp          #Temporary working dir
descbuild=./descbuild   #Path to the descbuild utility
                            #(see datasrc/descbuild.cpp)

# Clean up if the script is terminated
cleanup()
{
    printf "Cleaning up...\n"
    rm -rf "$tmpdir"
}

# Trap some signals for graceful exit
cleanup_and_exit()
{
    printf "\n"
    cleanup
    exit 1
}

trap cleanup_and_exit 1 2 3 9 15

# labelprint: Print a label with fixed length
# printok: Print OK when a task is done
labelprint()
{
    printf '%-50s' "$1"
}

printok()
{
    printf "OK\n"
}

# Show help and usage message
help()
{
    printf 'Usage: %s [options...]\n' "$(basename "$0")"
    printf 'Generate the data of Hikou no mizu from source.\n\n'
    printf 'Options:\n'

    printf '  %-25s %s\n' "-h, --help"                          \
        "Print a help message"

    printf '  %-25s %s\n' "-c, --conf_only"                     \
        "Rebuild only the configuration files located in datasrc/cfg/"

    printf '  %-25s %s%s\n' "--descbuild=[file]"                \
        "Specify the location of the descbuild utility "        \
        "(distributed with datasrc/)"

    printf '  %-25s %s\n' "--datasrc=[dir]"                     \
        "Speficy the location of the input datasrc/ directory"

    printf '  %-25s %s\n' "--output=[dir]"                      \
        "Specify the location of the output data/ directory"

    printf '  %-25s %s%s\n' '${HNM_PARALLEL} (env)'             \
        "Allow n>=0 simultaneous tasks of animation building; " \
        "unlimited for n=0. Default is 1"
}

# Show the dependencies
dependencies()
{
    printf 'Dependencies:\n'
    printf '  %-25s\n'    "getopt"
    printf '  %-25s %s\n' "synfig"      "<https://synfig.org>"
    printf '  %-25s %s\n' "inkscape"    "<https://inkscape.org>"
    printf '  %-25s %s\n' "imagemagick" "<https://imagemagick.org>"
    printf '  %-25s %s\n' "pngquant"    "<https://pngquant.org>"
    printf '  %-25s %s\n' "xz"          "<https://tukaani.org/xz>"
}

# Print a dependency not found message with optional suffix and exit
dep_notfound()
{
    printf "'%s' command not found%s.\n" "$1" "$2"
    dependencies
    exit 1
}

# =================== #
#  = INITIALIZATION = #
# =================== #

# Welcome message
printf 'Hikou no mizu data builder\n'

# Dependency check
magick_suffix=" (imagemagick)"
command -v getopt   > /dev/null 2>&1 || dep_notfound "getopt"
command -v synfig   > /dev/null 2>&1 || dep_notfound "synfig"
command -v inkscape > /dev/null 2>&1 || dep_notfound "inkscape"
command -v convert  > /dev/null 2>&1 || dep_notfound "convert" "$magick_suffix"
command -v montage  > /dev/null 2>&1 || dep_notfound "montage" "$magick_suffix"
command -v pngquant > /dev/null 2>&1 || dep_notfound "pngquant"
command -v xz       > /dev/null 2>&1 || dep_notfound "xz"

# Parse environment variables
simultaneous_tasks=1 # Number of parallel tasks of animation rendering:
                     # use the HNM_PARALLEL environment variable
if ! [ -z "$HNM_PARALLEL" ] ; then
    if printf '%s' "$HNM_PARALLEL" | grep -E -q '^[0-9]+$' ; then
        simultaneous_tasks=$HNM_PARALLEL
    else
        printf "%s%s%s\n" "Malformed number of parallel processes, "  \
                          "fallback to synchronous animation "        \
                          "building (HNM_PARALLEL=1)"
        help
    fi
else
    printf "%s%s%s\n" "The environment variable HNM_PARALLEL "        \
                    "may be set to define the number of tasks which " \
                    "will be spawned for spritesheet generations"
fi

# Parse options
conf_only=1 # Only generate configuration files

arguments=$(getopt    \
                -o hc \
                -l help,conf_only,descbuild:,datasrc:,output: -- "$@")
if [ "$?" != "0" ]; then
    help
    exit 1
fi

eval set -- "$arguments"
while :
do
    case "$1" in
        -h | --help)      help ; exit 0   ; shift   ;;
        -c | --conf_only) conf_only=0     ; shift   ;;
        --descbuild)      descbuild="$2"  ; shift 2 ;;
        --datasrc)        datasrcdir="$2" ; shift 2 ;;
        --output)         datadir="$2"    ; shift 2 ;;
        --)               shift           ; break   ;;
    esac
done

# Input data source check
if [ ! -d "$datasrcdir" ] ; then # Data source directory not found
    printf 'The source directory "%s" does not exist.\n' "$datasrcdir"
    exit 1
fi

# Create output directory
labelprint "Creating ${datadir}/..."
mkdir -p "$datadir" ; printok


# ============================== #
# = CONFIGURATION FILES, FONTS = #
# ============================== #

labelprint 'Copying configuration files and fonts...'

rm -rf "${datadir}/fonts" "${datadir}/cfg" # Clean any pre-existing content
cp -r "${datasrcdir}/fonts" "${datasrcdir}/cfg" "$datadir" ; printok

if [ "$conf_only" = "0" ] ; then
    exit 0
fi


# ================ #
# = SPRITESHEETs = #
# ================ #

# Create temporary working directory
mkdir "$tmpdir"

# Retrieve base path to sifz animation files
animationpath=$(grep ':ANIMATIONPATH=' "${datasrcdir}/buildinfo.cfg" |
                cut -d\= -f2)

# Clean any pre-existing content
rm -rf "${datadir}/${animationpath}"

# Create temporary metadata and animation description directories,
# as well as each character's data directory
metadata_path="${tmpdir}/anim_metadata"
description_path="${tmpdir}/anim_description"
mkdir -p "$metadata_path"
mkdir -p "$description_path"

# Extract characters list
characters_list=$(ls -d "${datasrcdir}/${animationpath}"/*/ |
                  xargs -L 1 -d '\n' basename)
for character in $characters_list
do
    # Create data directory
    mkdir -p "${datadir}/${animationpath}/${character}"

    # Populate temporary animation description directory
    lowercase_name=$(printf '%s' "$character" | tr [:upper:] [:lower:])
    character_path="${datasrcdir}/${animationpath}/${character}/"
    cp "${character_path}/${lowercase_name}.xml" "$description_path"
done


# Browse sifz animations' list

# Number of animations to render:
total_animations=$(grep "^[^#:]" "${datasrcdir}/buildinfo.cfg" | wc -l)
ixanim=1 # Current animation being rendered, index

printf 'Rendering %d animations spritesheets...\n' "$total_animations"
printf "%d parallel rendering task(s) will be spawned...\n" \
    $simultaneous_tasks

grep "^[^#:]" "${datasrcdir}/buildinfo.cfg" |
while IFS=',' read -r rel_outputpath inputlist
do
    printf '"%s" "%s" "%s" "%s" "%d" "%d"\n'                         \
        "$rel_outputpath" "${tmpdir}/anim${ixanim}" "$metadata_path" \
        "$inputlist" 15 8 >> "${tmpdir}/commands"
    ixanim=$(($ixanim+1))
done

# Launching spritesheet rendering commands in parallel
< "${tmpdir}/commands" xargs -P $simultaneous_tasks -L 1           \
    "${datasrcdir}/animbuild.sh" "${datasrcdir}/${animationpath}/" \
    "${datadir}/${animationpath}/"

# Generate static game-readable animation description file
labelprint 'Building static animation description files...'

# 1. Update the description files to make them game-readable
metadatas_list=$(ls "$metadata_path")
for metadata_file in $metadatas_list
do
    character_lowercase=$(printf '%s' $metadata_file |
                          cut -d '_' -f 1 | tr [:upper:] [:lower:])
    "${descbuild}" "${metadata_path}/${metadata_file}" \
                   "${description_path}/${character_lowercase}.xml"
done

# 2. Move the generated game-readable description files
#    to the output data directory
for character in $characters_list
do
    lowercase_name=$(printf '%s' "$character" | tr [:upper:] [:lower:])
    cp "${description_path}/${lowercase_name}.xml" \
       "${datadir}/${animationpath}/${character}/data.xml"
done
printok


# =============== #
# = UI GRAPHICS = #
# =============== #

# Uses inkscape to render svgz files
svgz_render()
{
    output_path="${datadir}/${1}.png"
    if ! [ -z "$2" ] ; then
        output_path="${2}.png"
    fi

    inkscape --export-filename="$output_path" \
        "${datasrcdir}/${1}.svgz" > /dev/null 2>&1
}

# Uses inkscape to render svg files
svg_render()
{
    output_path="${datadir}/${1}.png"
    if ! [ -z "$2" ] ; then
        output_path="${2}.png"
    fi

    inkscape --export-filename="$output_path" \
        "${datasrcdir}/${1}.svg" > /dev/null 2>&1
}

# Resize the canvas of an image
canvas_resize()
{
    output_path="${1}"
    if ! [ -z "$4" ] ; then
        output_path="${4}"
    fi

    convert "$1" -background none \
        -gravity NorthWest -extent ${2}x${3} "$output_path"
}

# Decompresses and renders xcf files, and resizes the canvas
xcf_render()
{
    output_path="${datadir}/${1}.png"
    if ! [ -z "$4" ] ; then
        output_path="${4}.png"
    fi

    cp "${datasrcdir}/${1}.xcf.xz" "$tmpdir"

    filename="$(basename "$1")"
    xz --decompress "${tmpdir}/${filename}.xcf.xz"

    convert "${tmpdir}/${filename}.xcf"[1] -background none \
        -gravity NorthWest -extent ${2}x${3} "$output_path"
}

# Clean any pre-existing content
rm -rf "${datadir}/gfx/ui" \
       "${datadir}/gfx/icon.png" \
       "${datadir}/gfx/characters/reduced.png"

# Generate UI graphics
labelprint 'Generating ui backgrounds...'

mkdir -p "${datadir}/gfx/ui"
cp "${datasrcdir}/gfx/ui/ui.png" "${datadir}/gfx/ui"

svgz_render "gfx/ui/title" &&
    canvas_resize "${datadir}/gfx/ui/title.png" 2048 2048

svgz_render "gfx/ui/banner" &&
    canvas_resize "${datadir}/gfx/ui/banner.png" 2048 512

svg_render "gfx/icon"
svg_render "gfx/characters/reduced"

xcf_render "gfx/ui/himeji" 2048 2048
xcf_render "gfx/ui/nikko" 2048 2048
xcf_render "gfx/ui/nagano" 2048 2048

printok


# ========= #
# = FLAGS = #
# ========= #

rm -rf "${datadir}/gfx/flags" # Clean any pre-existing content
mkdir -p "${datadir}/gfx/flags"

labelprint 'Generating language flags...'

language_flags=$(find "${datasrcdir}/gfx/flags" -name "*.svg" | sort)
montage -mode concatenate -tile 3x2 -geometry +2+2 -background none -trim \
    $language_flags "${datadir}/gfx/flags/flags.png"
convert "${datadir}/gfx/flags/flags.png" -background none -gravity NorthWest \
    -chop 2x2+0+0 -extent 256x128 "${datadir}/gfx/flags/flags.png"

printok


# ========== #
# = ARENAS = #
# ========== #

rm -rf "${datadir}/gfx/arenas" # Clean any pre-existing content
mkdir -p "${datadir}/gfx/arenas"

labelprint 'Generating arenas...'

svgz_render "gfx/arenas/dojo" &&
    canvas_resize "${datadir}/gfx/arenas/dojo.png" 2048 2048

svgz_render "gfx/arenas/pradenn" &&
    canvas_resize "${datadir}/gfx/arenas/pradenn.png" 2048 2048

svgz_render "gfx/arenas/scalae" &&
    canvas_resize "${datadir}/gfx/arenas/scalae.png" 2048 2048

xcf_render "gfx/arenas/machi" 2048 2048
xcf_render "gfx/arenas/traezh" 2048 2048
xcf_render "gfx/arenas/lac" 2048 2048
xcf_render "gfx/arenas/isfjall" 2048 2048

# Evig Sol
xcf_render "gfx/arenas/evigsol/evigsol_background" 1920 1080 \
    "${tmpdir}/evigsol_background"

svgz_render "gfx/arenas/evigsol/evigsol_box" \
    "${tmpdir}/evigsol_box"

convert "${tmpdir}/evigsol_background.png" "${tmpdir}/evigsol_box.png" \
    -background none -bordercolor none -border 0x2 -append \
    "${tmpdir}/evigsol.png"

convert "${tmpdir}/evigsol.png" -background none -gravity NorthWest \
    -chop 0x2+0+0 -extent 2048x2048 "${datadir}/gfx/arenas/evigsol.png"

printok


# =========== #
# = WEAPONS = #
# =========== #

rm -rf "${datadir}/gfx/weapons" # Clean any pre-existing content
mkdir -p "${datadir}/gfx/weapons"

labelprint 'Generating weapons...'
svgz_render "gfx/weapons/shuriken"
printok


# ==================== #
# = PNG OPTIMIZATION = #
# ==================== #

labelprint 'Optimizing animation files...'
find "${datadir}/gfx/characters/" -name "*.png" -print0 |
    xargs -0 -P $simultaneous_tasks -L1 pngquant --quality 90 --ext .png --force
printok

labelprint 'Optimizing other relevant graphics...'
pngquant --quality 90 --ext .png --force \
    "${datadir}/gfx/icon.png" \
    "${datadir}/gfx/ui/banner.png" \
    "${datadir}/gfx/weapons/shuriken.png"
printok

# ========= #
# = AUDIO = #
# ========= #

# Copy sounds
rm -rf "${datadir}/audio" # Clean any pre-existing content

labelprint 'Copying audio...'
cp -r "${datasrcdir}/audio" "$datadir" ; printok


# Cleanup temporary working directory
cleanup
