#!/bin/bash

#   Zoomer - Generate a video from a picture zooming each selected detail
#   Copyright (C) 2008  Aurélio A. Heckert
#
#   This program 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.
#
#   This program 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.

#   Version 0.1

# Collect all args and create local variables:
for arg in "$@"; do
  if ( echo -n $arg | grep -q '^--.*=' ); then
    key="$( echo -n "$arg" |
            sed 's/^--\([^=]*\)=.*$/\1/; s/[^a-zA-Z0-9]/_/' )"
    val="$( echo -n "$arg" |
            sed 's/^--\([^=]*\)=\(.*\)$/\2/' )"
    eval "$key=\"$val\""
  fi
  if ( echo -n $arg | grep -q '^--[^=]\+$' ); then
    key="$( echo -n "$arg" |
            sed 's/^--\(.*\)$/\1/; s/[^a-zA-Z0-9]/_/' )"
    eval "$key=true"
  fi
done

[ "$debug" != "true" ] && debug="false"
[ "$use_zenity" != "true" ] && use_zenity="false"

if ( $debug && $use_zenity ); then
  echo "Can't use Zenity when debuging" >&2
  use_zenity="false"
fi

function show_loading() {
  if $use_zenity; then
    zenity --progress --width=400 --title=Zoomer --text="Generating Frames..." --auto-close
  else
    cat
  fi
}

term_w=$COLUMNS
test "$term_w" = "" && term_w=80
$debug && echo "Term columns: $term_w"

if ! test -e "$pic"; then
  echo "Picture missed" >&2
  exit 1
fi

eval "$( identify -format 'pic_w=%w\npic_h=%h' "$pic" )"

if ! test -e "$data_file"; then
  data_file="$data"
fi
if ! test -e "$data_file"; then
  echo "Data file missed" >&2
  exit 1
fi

[ "$video_size" = "" ] && video_size=400x300
video_w=$( echo $video_size | sed 's/^\([0-9]*\)x.*$/\1/' )
video_h=$( echo $video_size | sed 's/^.*x\([0-9]*\)$/\1/' )
if [ "$video_w" = "" -o "$video_h" = "" ]; then
  echo "Video size missed" >&2
  exit 1
fi

if [ "$output" = "" ]; then
  output="$output_video"
fi
if [ "$replace_output" = "" ]; then
  replace_output="$replace_output_video"
fi
if [ "$output" = "" ]; then
  echo "Output video file missed" >&2
  exit 1
fi

if test -e "$output" -a "$replace_output" != "true"; then
  echo -n "Replace \"$output\"? [y,n] "
  read -sn 1 resp
  resp=$( echo "$resp" | tr y Y )
  test "$resp" != "Y" && resp=N
  echo $resp
  if [ $resp = N ]; then
    echo "So, please, restart with a new name."
    exit 0
  fi
fi

function calc() {
  echo "$@" | bc -l | sed 's/^\(-\?\)\./\10./; s/^$/0/'
}

function round() {
  calc "$@" | sed 's/\..*$//'
}

function test_calc() {
  r=$( calc "$1" )
  if [ "$r" = "$2" ]; then
    err=Ok
  else
    err=Error
  fi
  echo "$1 = $2 ($err) => $( round "$@" )"
}
#test_calc '0 + 0'
#test_calc '0 + 1'
#test_calc '3 / 98700000'
#test_calc '0.102030'
#test_calc '80 * 2'
#test_calc '80.8 * 2'
#test_calc '-123'
#test_calc '-.123'
#test_calc '-.0123' '-0.0123'
#test_calc '010 + 1' 11
#test_calc '0010 + 1' 11

function beault_num() {
  sed 's/\.\(.*[^0]\)\?0*$/.\1/; s/\.$//'
}

# Video Size Delta:
vsd=$( calc "$video_h / $video_w" | beault_num )

[ "$zoom_out_pct" = "" ] && zoom_out_pct=50
zoom_out_pct=$( calc "$zoom_out_pct / 100" )

# Zoom Out Size:
if [ "$zow" = "" -o "$zoh" = "" ]; then
  zoh=$( round "$pic_h * $zoom_out_pct" )
  zow=$( round "$zoh / $vsd" )
  if [ $zow -gt $( round "$pic_w * $zoom_out_pct" ) ]; then
    zow=$( round "$pic_w * $zoom_out_pct" )
    zoh=$( round "$zow * $vsd" )
  fi
fi

[ "$frames_stoped"  = "" ] && frames_stoped=20
[ "$frames_moveing" = "" ] && frames_moveing=10

[ "$font_size" = "" ] && font_size=20

tot_details=$( wc -l $data_file | sed 's/^\([0-9]*\) .*$/\1/' )
tot_frames=$(( ($tot_details+1)*($frames_moveing+1) + $tot_details*($frames_stoped-1) ))

$debug &&
echo "Video configuration:
  Picture: $pic
  Pic Size: w:$pic_w h:$pic_h
  Data file: $data_file
  Zoom Out Size: w:$zow h:$zoh
  Frames in Detail: $frames_stoped
  Frames Moving: $frames_moveing
  Tot Details: $tot_details
  Tot Frames: $tot_frames
  Font Size : $font_size
  Output video: $output
  Output Size: w:${video_w} h:${video_h} delta:$vsd
"

work_dir=$( mktemp -d )
thunar $work_dir &

cat "$data_file" | ( # start frame generator loop

f=0
old_x=$( round "$pic_w / 2" )
old_y=$( round "$pic_h / 2" )

oldz_h=$pic_h
oldz_w=$( round "$oldz_h / $vsd" )
if [ $oldz_w -gt $pic_w ]; then
  oldz_w=$pic_w
  oldz_h=$( round "$oldz_w * $vsd" )
fi

# Variables:
#   Generated with Linear function:
#     step  : Frame Step (Natural Numbers)
#     sm_#  : Step Moving in Frame Step (0..1 scale)
#   Generated with Sigmoid function:
#     szo_# : Step Old Zoom in Frame Step (0..1 scale)
#     szn_# : Step New Zoom in Frame Step (0..1 scale)

eval "$( perl -e "
  \$e=2.7182818284;
  for ( \$step=0; \$step<=$frames_moveing; \$step++ ) {
    \$sm = \$step / $frames_moveing;
    \$szo = \$e**(-(\$sm-0.25)*20) / ( 1 + ( \$e**(-(\$sm-0.25)*20) ) );
    \$szo = 1 if \$sm == 0;
    \$szo = 0 if \$sm >= 0.5;
    \$szn = \$e**((\$sm-0.75)*20) / ( 1 + ( \$e**((\$sm-0.75)*20) ) );
    \$szn = 1 if \$sm == 1;
    \$szn = 0 if \$sm <= 0.5;
    print \"sm_\$step=\$sm \t; szo_\$step=\$szo \t; szn_\$step=\$szn\n\"
  }
" )"

function show_pct() {
  pct=$( calc "( $frame * 100 ) / $tot_frames" )
  if [ "$loading_type" = "pct" ]; then
    echo $( round $pct )
  else
    for i in $(seq $term_w ); do echo -n ' '; done
    echo -ne "\r Zooming \"$name\" - frame $frame of $tot_frames"
    echo -ne " - $( echo $pct | sed 's/\(.....\).*/\1/' )%\r"
  fi
}

function move_camera() {
  mid_step=$( round "$frames_moveing / 2" )
  for step in $( seq 0 $frames_moveing ); do
    let frame++
    eval "sm=\$sm_$step ; szo=\$szo_$step ; szn=\$szn_$step"
    if [ $step -le $mid_step ]; then
      zw=$( calc "( $oldz_w * $szo ) + ( $zow * (1-$szo) )" )
      zh=$( calc "( $oldz_h * $szo ) + ( $zoh * (1-$szo) )" )
    else
      zw=$( calc "( $zow * (1-$szn) ) + ( $zoom_w * $szn )" )
      zh=$( calc "( $zoh * (1-$szn) ) + ( $zoom_h * $szn )" )
    fi
    x=$( calc "( $old_x * (1-$sm) ) + ( $goto_x * $sm )" | beault_num )
    y=$( calc "( $old_y * (1-$sm) ) + ( $goto_y * $sm )" | beault_num )
    x1=$( round "$x - ( $zw / 2 )" )
    x2=$( round "$x + ( $zw / 2 )" )
    y1=$( round "$y - ( $zh / 2 )" )
    y2=$( round "$y + ( $zh / 2 )" )
    $debug && echo -e " frame:$frame \tstep:$step \tsm:$sm \tx:$x \ty:$y"
    $debug && echo -e " szo:$szo \tszn:$szn \tzw=$zw \tzh=$zh"
    if [ $x1 -lt 0 ]; then
      x2=$(( $x2 - $x1 ))
      x1=0
    fi
    if [ $x2 -gt $pic_w ]; then
      x1=$(( $x1 - ( $x2 - $pic_w ) ))
      x2=$pic_w
    fi
    if [ $y1 -lt 0 ]; then
      y2=$(( $y2 - $y1 ))
      y1=0
    fi
    if [ $y2 -gt $pic_h ]; then
      y1=$(( $y1 - ( $y2 - $pic_h ) ))
      y2=$pic_h
    fi
    $debug && echo -e " x1=$x1 \t x2=$x2 \t y1=$y1 \t y2=$y2"
    convert "$pic" \
            -crop $( round $zw )x$( round $zh )+$( round $x1 )+$( round $y1 ) \
            +repage -resize "${video_w}x${video_h}!" $work_dir/frame.png
    if [ $(round "$sm*100") -lt 50 ]; then
      alfa=$( round "100 - ( $sm * 200 )" )
      if [ "$old_name_png" != "" ]; then
        composite -dissolve ${alfa}x100 -gravity South \
                  "$old_name_png" $work_dir/frame.png \
                  -quality 100 $work_dir/f_$frame.jpg
      else
        convert $work_dir/frame.png -quality 100 $work_dir/f_$frame.jpg
      fi
    else
      alfa=$( round "( $sm * 200 ) - 100" )
      if [ "$name_png" != "" ]; then
        composite -dissolve ${alfa}x100 -gravity South \
                  "$name_png" $work_dir/frame.png \
                  -quality 100 $work_dir/f_$frame.jpg
      else
        convert $work_dir/frame.png -quality 100 $work_dir/f_$frame.jpg
      fi
    fi
    rm $work_dir/frame.png
    $debug || show_pct
  done
}

svg=$work_dir/nome.svg
function generate_name_file() {
  f="font-family:sans-serif; font-size:${font_size}px; font-weight:bold"
  s="stroke:#000; stroke-width:$( calc "$font_size / 10" )px"
  s="$s;stroke-linejoin:round; stroke-linecap:round"
  echo "<svg>
  <defs>
    <filter id='blur'>
      <feGaussianBlur stdDeviation='$( calc "$font_size / 15" )' />
    </filter>
  </defs>
  <text style='$f; $s; stroke-opacity:0; stroke-width:$( calc "$font_size / 2" )px'>$name</text>
  <text style='$f; $s; opacity:0.5; filter:url(#blur)'>$name</text>
  <text style='$f; fill:#FFF'>$name</text>
  </svg>" > $svg
  inkscape --export-png="$1" --export-area-drawing --without-gui $svg >/dev/null
}

frame=0

while read line; do
  eval "$( echo "$line" | sed "s/'/\´/g; s/^\([^\s]*\),\([^\s]*\) \([^\s]*\) \(.*\)$/goto_x='\1'\ngoto_y='\2'\nzoom_w='\3'\nname='\4'/" )" #'
  zoom_h=$( round "$zoom_w * $vsd" )
  $debug && echo "Zooming $name (${old_x},${old_y}:${oldz_w}x${oldz_h} -> ${goto_x},${goto_y}:${zoom_w}x${zoom_h})"
  if [ "$name" = "-" ]; then
    name_png=""
  else
    name_png="$work_dir/name_$name.png"
    generate_name_file "$name_png"
  fi
  move_camera
  last_frame=$frame
  for step in $( seq 2 $frames_stoped ); do
    let frame++
    # copy the frame
    cp $work_dir/f_$last_frame.jpg $work_dir/f_$frame.jpg
    $debug || show_pct
  done
  $debug && echo ""
  old_x=$goto_x
  old_y=$goto_y
  oldz_w=$zoom_w
  oldz_h=$zoom_h
  old_name_png="$name_png"
done

goto_x=$( round "$pic_w / 2" )
goto_y=$( round "$pic_h / 2" )
zoom_h=$pic_h
zoom_w=$( round "$zoom_h / $vsd" )
if [ $zoom_w -gt $zoom_w ]; then
  zoom_w=$pic_w
  zoom_h=$( round "$zoom_w * $vsd" )
fi
name="The End"
name_png=""
$debug && echo "Zooming The End (${old_x},${old_y}:${oldz_w}x${oldz_h} -> ${goto_x},${goto_y}:${zoom_w}x${zoom_h})"
move_camera

) | show_loading # end frame generator loop

if $debug; then
  echo "Generating the movie..."
else
  echo ""
fi

rm "$output" >&2
if ffmpeg -i "$work_dir/f_%d.jpg" "$output"; then
  $debug && echo " Done: $output"
  #rm -r $work_dir
else
  echo "Ups..." >&2
fi

