#!/bin/bash

# Command Line Only Visual Novel interpreter
# Copyright 2010-2017 Harri Olin <harri.olin@gmail.com>

# Supports VNDS script format (unpacked)
# graphics&sound unsupported

# usage:
# clovn [save]
# at VN main dir (containing script dir and info.txt)
# if started outside of main dir, shows list of VNs from
# /usr/share/games/vn and prompt to select VN

# keys:
# a: set autoadvance time in seconds, 0 to disable, integers only
# s: save game
# d: prints debug info
# keys don't work at autoadvance and savegame prompts

# Bugs: many
# report any at harri.olin@gmail.com

# does not support variables in text or as source in setvar command
# screen width set only at start of script
# if variable name contains strange chars (like -) setvar using it won't work (setvar a=b etc)

# Ver 1.01
# changed unprintable variable names to use md5sum instead of hexdump
# - note: breaks saves as variable names changed
# check if groff and tput are available before using
# explode tr [:alnum:] etc as busybox tr doesn't necessarily support
# fix choice count for buxybox as grep -o returned only 1 match

# Ver 1.02
# added some double quotes, no longer prints dir contets on *
# added _ to allowed chars in var names, possible save breakage
# fix check if var in lvars (check for space before var name)

# Ver 1.03
# if started outside of vn dir, present selection from
# /usr/share/games/vn
# if clovn started with relative path, convert to absolute
# add .save to savegames
# savegame selector if saves present

if [[ "${0:0:1}" == "." ]]; then
  CLOVN="$PWD/$0"
else
  CLOVN="$0"
fi 

if [ ! -e info.txt ]; then
  cd /usr/share/games/vn
  GAMES=$(ls */info.txt 2>/dev/null|cut -d "/" -f 1)
  let G=0
  for A in $GAMES; do
    let G=G+1
    GAME[$G]="$A"
    echo " $G: $A"
  done
  while [[ "$KEY" -lt 1 ]] || [[ "$KEY" -gt $G ]]; do
    read -p "select VN> " -u 1 KEY
    if [[ "$KEY" == "Q" ]] || [[ "$KEY" == "q" ]]; then
      exit 0
    fi
    KEY=$(echo "$KEY"|tr -c -d "1234567890")
  done
  cd ${GAME[$KEY]}
fi

if [ ! -e info.txt ]; then
  echo "Error: Title not Found!"
  echo "Make sure info.txt is at cwd"
  exit 1
fi

GROFF=""

if [[ "$(which groff 2>/dev/null)" == "" ]]; then
  GROFF=no
fi

VNNAME=$(cat info.txt|cut -d "=" -f 2|tr -d -c abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX1234567890)

SAVEDIR=~/.clovn/$VNNAME

if [ ! -d $SAVEDIR ]; then
  mkdir -p $SAVEDIR
fi

if [[ "$(which tput 2>/dev/null)" == "" ]]; then
  WIDTH=100
else 
  WIDTH=$(tput cols)
  let WIDTH=$WIDTH-2
fi

SCRIPT="$1"

if [[ "$SCRIPT" == "" ]] && [[ "$(ls $SAVEDIR/*.save 2>/dev/null)" != "" ]]; then
  SAVES=$(cd $SAVEDIR; ls *.save 2>/dev/null)
  LONGEST="0"
  for LEN in $SAVES; do
    if [[ "$LONGEST" -lt "${#LEN}" ]]; then
      LONGEST=${#LEN}
    fi
  done
  let G=0
  for A in $SAVES; do
    let G=G+1
    SAVE[$G]="$A"
    echo -n " $G: ${A%*.save}  "
    if [[ "${#A}" -lt "$LONGEST" ]]; then
      let SPACE=$LONGEST-${#A}
    else
      SPACE=0
    fi
    for (( X=0; X <= SPACE; X++ )); do
      echo -n " "
    done
    date -r $SAVEDIR/$A
  done
  while [[ "$KEY" -lt 0 ]] || [[ "$KEY" -gt $G ]]; do
    read -p "select save to continue or 0 to start from beginning> " -u 1 KEY
    if [[ "$KEY" == "Q" ]] || [[ "$KEY" == "q" ]]; then
      exit 0
    fi
    KEY=$(echo "$KEY"|tr -c -d "1234567890")
  done
  SCRIPT="${SAVE[$KEY]}"
fi

LINENR=0


if [ "$TIMEOUT" == "" ]; then
  TIMEOUT=0
fi


if [ "$SCRIPT" == "" ]; then
  FILE=script/main.scr
else
  if [ -e $SAVEDIR/$SCRIPT ]; then
    FILE=$SAVEDIR/$SCRIPT
  elif [ -e script/$SCRIPT ]; then
    FILE=script/$SCRIPT
  else
    FILE=$SCRIPT
  fi
fi

if [ ! -e $FILE ]; then
  echo "Error: File not Found!"
  echo "Make sure you are at main dir of VN and typed save name correctly"
  exit 2
fi

LABEL=$2
SKIPTO=$3

exec 3<$FILE

for LVAR in $LVARS; do
  SAVESTATE[$LVAR]="${!LVAR}"
done


if [ ! -e $SAVEDIR/global.var ]; then
  touch $SAVEDIR/global.var
fi

. $SAVEDIR/global.var

function savegame {
  read -u 1 -p "save> " SAVEFILE
  SAVEFILE=$(echo "$SAVEFILE"|tr -d -c abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX1234567890+-_.)
  SAVEFILE=$SAVEFILE.save
  if [ "$SAVEFILE" != "" ]; then
    if [ -e $SAVEDIR/$SAVEFILE ]; then
      read -u 1 -n 1 -s -p "overwrite? [yn]" SAVEOVER
      if [ "$SAVEOVER" == "y" ] || [ "$SAVEOVER" == "Y" ]; then
        rm -f $SAVEDIR/$SAVEFILE
        for LVAR in $LVARS; do
            echo "setvar $LVAR = ${!LVAR}" >> $SAVEDIR/$SAVEFILE
        done
        echo "jump $FILE - $LINENR" >> $SAVEDIR/$SAVEFILE
        echo " saved"
      else
        echo " not saved"
      fi
    else
      for LVAR in $LVARS; do
        echo "setvar $LVAR = ${!LVAR}" >> $SAVEDIR/$SAVEFILE
      done
      echo "jump $FILE - $LINENR" >> $SAVEDIR/$SAVEFILE
      echo "saved"
    fi
  fi
}

function printdebug {
  echo File: $FILE Label: $LABEL Lineno: $LINENR Timeout: $TIMEOUT
  echo "Lvars: $LVARS"
  echo -n "Lvars: "
  for LVAR in $LVARS; do
    echo -n " $LVAR=${!LVAR}"
  done
  echo
}

function readkey {
  KEY=""
  if [ "$TIMEOUT" == "0" ]; then
    read -s -n 1 -u 1 KEY
  else
    read -s -n 1 -u 1 -t $TIMEOUT KEY
  fi
  case $KEY in
    s | S )
    savegame
    readkey
    ;;
    a | A )
    ISAAOK="no"
    TIMEOUTOLD=$TIMEOUT
    while [ "$ISAAOK" != "yes" ]; do
      read -u 1 -p "autoadvance> " TIMEOUT
      if [ "$(echo "$TIMEOUT"|tr -d 1234567890)" == "" ] && [ "$TIMEOUT" != "" ]; then
        ISAAOK="yes"
      elif [ "$TIMEOUT" == "" ]; then
        ISAAOK="yes"
        TIMEOUT=$TIMEOUTOLD
      fi
    done
    export TIMEOUT
    readkey
    ;;
    d | D )
    printdebug
    readkey
    ;;
    * )
    ;;
  esac
}

function textout {
  if [ "${1:$WIDTH:1}" == "" ] ; then
    echo " $1"
  else
    if [[ $GROFF ]]; then
      echo " $1"
    else
      (echo ".ll ${WIDTH}em"; echo ".nr LL ${WIDTH}em"; echo ".in 0"; echo ".pl 1100i"; echo ".po 1"; echo "\&$1";echo ".\\\""; echo ".pl \n(nlu+10")|groff -T latin1 -P -f|tr -d "\f"
    fi
  fi
}

if [ "$LABEL" == "-" ]; then
  let SKIPTO-=1
  while [ "$SKIPTO" != "$LINENR" ] && read -u 3 LINE ; do
    let LINENR+=1
  done
elif [ "$LABEL" != "" ]; then
  ISOK="no"
  while [ "$ISOK" != "yes" ] && read -u 3 LINE ; do
    let LINENR+=1
    if [ "$LINE" == "label $LABEL" ]; then
      ISOK="yes"
    fi
  done
fi

empty=0
SKIPUNTILFI=0

while read -u 3 LINE; do
  let LINENR+=1
  COMMAND=$(echo "$LINE"|cut -d " " -f 1)
  if [ "$SKIPUNTILFI" != "0" ]; then
    if [ "$COMMAND" == "fi" ]; then
      let SKIPUNTILFI=$SKIPUNTILFI-1
    elif [ "$COMMAND" == "if" ]; then
      let SKIPUNTILFI=$SKIPUNTILFI+1
    fi
  else
    case "$COMMAND" in
      text )
        MOD=$(echo "$LINE"|cut -b 6)
        case "$MOD" in
          "@" )
            TEXT=$(echo "$LINE"|cut -d " " -s -f 2-|cut -b 2-)
            textout "$TEXT"
          ;;
          "~" )
            textout "";
          ;;
          "!" )
            textout "";
            readkey
          ;;
          * )
            TEXT=$(echo "$LINE"|cut -d " " -s -f 2-)
            textout "$TEXT"
            readkey
          ;;
        esac
      ;;
      choice )
        TEXT=$(echo "$LINE"|cut -d " " -f 2-)
        CHOICES=$(echo "$TEXT" | tr -d -c "|" | wc -c | sed s/\ //g)
        let CHOICES=$CHOICES+1
        LOOPS=$((CHOICES + 1))
        ISOK="no"
        for (( i = 1 ; i != $LOOPS ; i++ )) ; do
          CHOICE=$(echo "$TEXT"|cut -d "|" -f $i)
          textout "$i: $CHOICE";
        done
        while [ "$ISOK" != "yes" ]; do
          read -p "> " -u 1 selected
          if [ "$selected" == "1" ] || [ "$selected" == "2" ] || [ "$selected" == "3" ] || [ "$selected" == "4" ] || [ "$selected" == "5" ] || [ "$selected" == "6" ] || [ "$selected" == "7" ] || [ "$selected" == "8" ] || [ "$selected" == "9" ]; then
            if [ "$selected" -ge "1" ] && [ "$selected" -le "$CHOICES" ]; then
              ISOK="yes"
            fi
          elif [ "$selected" == "s" ] || [ "$selected" == "S" ]; then
            savegame
            for (( i = 1 ; i != $LOOPS ; i++ )) ; do
              CHOICE=$(echo "$TEXT"|cut -d "|" -f $i)
              textout "$i: $CHOICE";
            done
          elif [ "$selected" == "d" ] || [ "$selected" == "D" ]; then
            printdebug
            for (( i = 1 ; i != $LOOPS ; i++ )) ; do
              CHOICE=$(echo "$TEXT"|cut -d "|" -f $i)
              textout "$i: $CHOICE";
            done
          elif [ "$selected" == "a" ] || [ "$selected" == "A" ]; then
            ISAAOK="no"
            TIMEOUTOLD=$TIMEOUT
            while [ "$ISAAOK" != "yes" ]; do
              read -u 1 -p "autoadvance> " TIMEOUT
              if [ "$(echo "$TIMEOUT"|tr -d 1234567890)" == "" ] && [ "$TIMEOUT" != "" ]; then
                ISAAOK="yes"
              elif [ "$TIMEOUT" == "" ]; then
                ISAAOK="yes"
                TIMEOUT=$TIMEOUTOLD
              fi
            done
            export TIMEOUT
            for (( i = 1 ; i != $LOOPS ; i++ )) ; do
              CHOICE=$(echo "$TEXT"|cut -d "|" -f $i)
              textout "$i: $CHOICE";
            done
          fi
        done
      ;;
      jump )
        TEXT=$(echo "$LINE"|cut -d " " -f 2-)
        echo
        $CLOVN $TEXT
        exit 0;
      ;;
      if ) 
        TEXT=$(echo $LINE|cut -d " " -f 2-)
        LEFT=$(echo $TEXT|cut -d " " -f 1)
        OPER=$(echo $TEXT|cut -d " " -f 2)
        RIGHT=$(echo $TEXT|cut -d " " -f 3)
        if [ "$(echo "$LEFT"|tr -d "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")" != "" ]; then
          LEFT="var$(echo "$LEFT"|md5sum|cut -d " " -f 1)"
        fi
        if [ "${!LEFT}" == "" ] ; then
          LEFT=empty;
        fi
        case $OPER in
          "==" )
          if [ "${!LEFT}" == "$RIGHT" ] ; then 
            true
          else
            SKIPUNTILFI=1;
          fi
          ;;
          "!=" )
          if [ "${!LEFT}" != "$RIGHT" ] ; then 
            true
          else
            SKIPUNTILFI=1;
          fi
          ;;
          ">=" )
          if [ "${!LEFT}" -ge "$RIGHT" ] ; then 
            true
          else
            SKIPUNTILFI=1;
          fi
          ;;
          "<=" )
          if [ "${!LEFT}" -le "$RIGHT" ] ; then 
            true
          else
            SKIPUNTILFI=1;
          fi
          ;;
          ">" )
          if [ "${!LEFT}" -gt "$RIGHT" ] ; then 
            true
          else
            SKIPUNTILFI=1;
          fi
          ;;
          "<" )
          if [ "${!LEFT}" -lt "$RIGHT" ] ; then 
            true
          else
            SKIPUNTILFI=1;
          fi
          ;;
        esac
      ;;
      delay )
# no delays in console version
#        TEXT=$(echo $LINE|cut -d " " -f 2-)
#        for (( i = 0 ; i != $TEXT ; i++ )) ; do
#          sleep 0.01666666;
#        done
      ;;
      goto )
        TEXT=$(echo $LINE|cut -d " " -f 2-)
        $CLOVN $FILE $TEXT
        exit 0;
      ;;
      setvar )
        TEXT=$(echo $LINE|cut -d " " -f 2-)
        LEFT=$(echo $TEXT|cut -d " " -f 1)
        OPER=$(echo $TEXT|cut -d " " -f 2)
        RIGHT=$(echo $TEXT|cut -d " " -f 3)
        if [ "$(echo "$LEFT"|tr -d "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")" != "" ]; then
          LEFT="var$(echo "$LEFT"|md5sum|cut -d " " -f 1)"
        fi
        if [ "$RIGHT" == "" ]; then
          RIGHT="0"
        fi
        case $OPER in
          "+" )
          let ${LEFT}=${!LEFT}+$RIGHT
          export $LEFT
          if ! echo $LVARS|grep -q " $LEFT"; then 
            LVARS="$LVARS $LEFT";
          fi
          ;;
          "-" )
          let ${LEFT}=${!LEFT}-$RIGHT
          export $LEFT
          if ! echo $LVARS|grep -q " $LEFT"; then 
            LVARS="$LVARS $LEFT";
          fi
          ;;
          "=" )
          let ${LEFT}=$RIGHT
          export $LEFT
          if ! echo $LVARS|grep -q " $LEFT"; then 
            LVARS="$LVARS $LEFT";
          fi
          ;;
          "~" )
          if [ "$LEFT" == "~" ]; then
            for LVAR in $LVARS; do
              let $LVAR=0
            done
          else
            let $LEFT=0
            export $LEFT
          fi
        esac
        export LVARS
      ;;
      gsetvar )
        TEXT=$(echo $LINE|cut -d " " -f 2-)
        LEFT=$(echo $TEXT|cut -d " " -f 1)
        OPER=$(echo $TEXT|cut -d " " -f 2)
        RIGHT=$(echo $TEXT|cut -d " " -f 3)
        if [ "$(echo "$LEFT"|tr -d "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")" != "" ]; then
          LEFT="var$(echo "$LEFT"|md5sum|cut -d " " -f 1)"
        fi
        case $OPER in
          "+" )
          let ${LEFT}=${!LEFT}+$RIGHT
          ;;
          "-" )
          let ${LEFT}=${!LEFT}-$RIGHT
          ;;
          "=" )
          let ${LEFT}=$RIGHT
          ;;
        esac
        echo $LEFT=${!LEFT} >> $SAVEDIR/global.var
      ;;
      * )
      ;;
    esac;
  fi
done

