#!/bin/sh
# Dependencies: synfig, imagemagick

# 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/>.


# animbuild.sh: Builds a spritesheet png file from a sifz animation.

# Trap some signals for graceful exit
exit_trap()
{
    exit 1
}

trap exit_trap 1 2 3 9 15

base_srcdir="$1"    # datasrc dir
base_outputdir="$2" # generated data dir
output_name="$3"    # relative name of output spritesheet
                        #(e.g. Hikou/default.png)

workingdir="$4" # Temporary working directory
metadatadir="$5"

inputs="$6" # Input sifz animations description
pngdest="$base_outputdir$output_name" # Output png spritesheet

scale_mult="$7"
scale_div="$8"


# Compute the size of the overall spritesheet based on the frame size and amount
compute_spritesheet_size()
{
    _frame_separator=4
    _sizes="256 512 1024 2048 4096"

    #Frame full width including separator (4px)
    _frame_width="$(expr "$1" + "$_frame_separator")"

    #Frame full height including separator (4px)
    _frame_height="$(expr "$2" + "$_frame_separator")"

    _frames_amount="$3"

    for imgwidth in $_sizes
    do
        for imgheight in $_sizes
        do
            tiles=$(expr "$imgwidth" / "$_frame_width")
            _rows=$(expr "$imgheight" / "$_frame_height")

            if [ "$(expr "$tiles" \* "$_rows")" -ge "$_frames_amount" ] ; then
                break 2
            fi

            # Break the loop to switch to the next image_width increment
            if [ "$imgheight" -ge "$imgwidth" ] ; then
                break
            fi
        done
    done
}

# Compute the scaled up width or height of animation frames to be rendered.
# The scaled up value is then rounded to the nearest integer, so it can be
# used as a image file dimension. This would cause rounding errors
# in the scaling of frame body coordinates by hnm_descbuild.
# In turn, the scaled_correction variable is computed to allow correcting them
compute_scaled_size()
{
    # Retrieve the size ('w' width or 'h' height) from a synfig animation
    _original_size="$(synfig "$2" --canvas-info $1 2> /dev/null |
                        tail -n1                                |
                        cut -d '=' -f 2)"

    # Compute the scaled size with 4 digits precision
    _scaled_real="$(echo "scale=4; $_original_size * $scale_mult / $scale_div" |
                        bc)"

    # Compute the rounded value to provide actual frame image sizes,
    # and the correction factor to avoid rounding errors in body coordinates
    scaled_size="$(echo "scale=1; size=$_scaled_real + 0.5; scale=0; size / 1" |
                        bc)"

    scaled_correction="$(echo "scale=4; $_scaled_real / $scaled_size" | bc)"
}

mkdir -p "$workingdir"
mkdir -p "$(dirname "$pngdest")"

# Render animation frames: All sifz animations must have the same width / height
i=1
input_i=$(printf '%s;' "$inputs" | cut -d\; -f1)
input_i=$(printf '%s' "$input_i" | tr -d ' \n[]}') #Trim
while ! [ -z "$input_i" ] ; do

    # Browse input sifz descriptions
    printf '%s\n' "$input_i" |
    while IFS='-' read -r sifz_file start end ; do
        mkdir "${workingdir}/tmp${i}"

        # Scaling of the animation ?
        if [ "$scale_mult" = 0 -o "$scale_div" = 0 ]; then # No scaling
            printf '1\n' > "${workingdir}/correct_width"
            printf '1\n' > "${workingdir}/correct_height"
            synfig "${base_srcdir}${sifz_file}" -t png \
                -o "${workingdir}/tmp${i}/render.png" > /dev/null 2>&1
        else
            compute_scaled_size "w" "${base_srcdir}${sifz_file}"
            updated_width=$scaled_size
            printf '%s\n' "$scaled_correction" > "${workingdir}/correct_width"

            compute_scaled_size "h" "${base_srcdir}${sifz_file}"
            updated_height=$scaled_size
            printf '%s\n' "$scaled_correction" > "${workingdir}/correct_height"

            synfig "${base_srcdir}${sifz_file}"                 \
                -t png -w "$updated_width" -h "$updated_height" \
                -o "${workingdir}/tmp${i}/render.png" > /dev/null 2>&1
        fi

        # Register the needed frames
        if [ "$end" = ":" ] ; then
            # Select from 'start' to the last frame
            added_frames=$(ls ${workingdir}/tmp${i}/* |
                           sed -n "${start}~1p")
        else
            # Select from 'start' to 'end'
            added_frames=$(ls ${workingdir}/tmp${i}/* |
                           sed -n "${start},${end}p")
        fi

        printf '%s\n' "$added_frames" >> "${workingdir}/frames"
    done

    # Next input
    i=$(($i+1))
    input_i=$(printf '%s;' "$inputs" | cut -d\; -f$i)
    input_i=$(printf '%s' "$input_i" | tr -d ' \n[]}') #Trim
done

# Compute the required image size
    # Number of frames in the animation:
frames_amount=$(< "${workingdir}/frames" wc -l)

    # To extract width & height (constants to all frames):
first_frame=$(head -1 "${workingdir}/frames")

    # All animations are assumed to have the same width/height
frame_width=$(convert "$first_frame" -format "%[w]" info:)
frame_height=$(convert "$first_frame" -format "%[h]" info:)

    # Fills in $imgwidth, $imgheight and $tiles:
compute_spritesheet_size "$frame_width" "$frame_height" "$frames_amount"

# Retrieve the animation scale error corrections, from the last animation
# (All animations are assumed to have the same width/height)
correction_width=$(cat "${workingdir}/correct_width")
correction_height=$(cat "${workingdir}/correct_height")

# Print needed frames
input_frames=$(cat "${workingdir}/frames")
montage $input_frames -background none -geometry +2+2 \
    -tile ${tiles}x "${workingdir}/tiles.png"

# Resize canvas
convert "${workingdir}/tiles.png" -background none -gravity NorthWest \
    -chop 2x2+0+0 -extent ${imgwidth}x${imgheight} "$pngdest"

# Save animation metadata
# (for subsequent animations description generation)

    #Replace '/' by '_':
metadata_name=$(printf '%s' "$output_name" | tr '/' '_')

    #Remove extension:
metadata_name=$(printf '%s' "$metadata_name" | cut -d '.' -f 1)

row_format="animation_name %s\n"
row_format="${row_format}frames_amount %d\n"
row_format="${row_format}frame_width %d\n"
row_format="${row_format}frame_height %d\n"
row_format="${row_format}tiles_per_column %d\n"
row_format="${row_format}correction_width %s\n"
row_format="${row_format}correction_height %s\n"

printf "$row_format"        \
       "$output_name"       \
       "$frames_amount"     \
       "$frame_width"       \
       "$frame_height"      \
       "$tiles"             \
       "$correction_width"  \
       "$correction_height" >> "${metadatadir}/${metadata_name}"

printf '  %-48sOK\n' "$output_name"
rm -rf "$workingdir"
