#!/bin/bash
SCRIPTDIR="$(dirname $(which "${0}"))"

_usage(){
    cat <<EOF
    "$(basename "${0}")"

    Visually display and/or generate xml and sidecars of errors in DV file.
    In visual display, error concealment data  in frames will be displayed
    as yellow. This script is part of the dvrescue project.
    Usage:
    "$(basename "${0}") [options] file.dv"

    Options:
    -s TIME    (choose a custom start time (in seconds) within file)
    -m         (Inverse of standard display)
    -x         (create output xml and jpgs)
    -g         (create gif from error jpgs)
    -o OUTPUT  (select a custom location for output files)
    -h         (Displays this help)
EOF
}

OPTIND=1
while getopts ":s:mxgho:" OPT ; do
    case "${OPT}" in
        s) START_TIME="${OPTARG}";;
        m) MASK_ONLY="Y" ;;
        x) ERROR_REVIEW="Y" ;;
        g) GIF_OUTPUT="Y" ;;
        h) _usage ;;
        o) USER_OUTPUT="${OPTARG}";;
        *) echo "bad option -${OPTARG}" ; _usage ; exit 1 ;;
        :) echo "Option -${OPTARG} requires an argument" ; exit 1 ;;
    esac
done
shift $(( ${OPTIND} - 1 ))

if [[ -n "${START_TIME}" ]] ; then
  INPUT_OPTS+=(-ss "${START_TIME}")
fi

# fill color handling
_convert_array_to_macroblock(){
  LUMA_AC="ffffffffffffffffffffff"
  CHROMA_AC="ffffffffffffff"
  COLOR_POINT="${1}"
  echo "${COLOR_POINT:0:6}${LUMA_AC}${COLOR_POINT:0:6}${LUMA_AC}${COLOR_POINT:0:6}${LUMA_AC}${COLOR_POINT:0:6}${LUMA_AC}${COLOR_POINT:6:6}${CHROMA_AC}${COLOR_POINT:12:6}${CHROMA_AC}"
  # get dv color data via
  # ffmpeg -f lavfi -i color=s=720x480:r=30000/1001:color=yellow -vframes 1 -f rawvideo -c:v dvvideo -pix_fmt yuv411p - | xxd -ps -c 80 | tail -n 1 | cut -c 9-14,121-126,141-146
}

_get_iso8601(){
    date +%FT%T
}

# report formatting function
_report(){
    local RED="$(tput setaf 1)"    # Red      - For Warnings
    local GREEN="$(tput setaf 2)"  # Green    - For Declarations
    local BLUE="$(tput setaf 4)"   # Blue     - For Questions
    local NC="$(tput sgr0)"        # No Color
    local COLOR=""
    local STARTMESSAGE=""
    local ECHOOPT=""
    OPTIND=1
    while getopts "qdwstn" opt ; do
        case "${opt}" in
            q) COLOR="${BLUE}" ;;                        # question mode, use color blue
            d) COLOR="${GREEN}" ;;                       # declaration mode, use color green
            w) COLOR="${RED}" ;;                         # warning mode, use color red
            s) STARTMESSAGE+=([${SCRIPTNAME}] ) ;;       # prepend scriptname to the message
            t) STARTMESSAGE+=($(_get_iso8601) '- ' ) ;;  # prepend timestamp to the message
            n) ECHOOPT="-n" ;;                           # to avoid line breaks after echo
        esac
    done
    shift "$((OPTIND-1))"
    MESSAGE="${1}"
    echo ${ECHOOPT} "${COLOR}${STARTMESSAGE[@]}${MESSAGE}${NC}"
}

# font selection
if [[ -f "/Library/Fonts/Andale Mono.ttf" ]] ; then
  DEFAULTFONT="/Library/Fonts/Andale Mono.ttf"
elif [[ -f "/System/Library/Fonts/Supplemental/Andale Mono.ttf" ]] ; then
  DEFAULTFONT="/System/Library/Fonts/Supplemental/Andale Mono.ttf"
elif [[ -f "/System/Library/Fonts/Monaco.dfont" ]] ; then
  DEFAULTFONT="/System/Library/Fonts/Monaco.dfont"
elif [[ -f "/Library/Fonts/Microsoft/Lucida Console.ttf" ]] ; then
  DEFAULTFONT="/Library/Fonts/Microsoft/Lucida\Console.ttf"
elif [[ -f "/Library/Fonts/LetterGothicStd.otf" ]] ; then
  DEFAULTFONT="/Library/Fonts/LetterGothicStd.otf"
elif [[ -f "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf" ]] ; then
  DEFAULTFONT="/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf"
else
  _report -wt "$(basename "${0}") can't find a preferred font to use :("
fi

DV_YELLOW="5206ff1216ff9016ff"
DV_RED="d106ff7016ffda16ff"
FILL=$(_convert_array_to_macroblock "${DV_YELLOW}")
if [[ "${MASK_ONLY}" = "Y" ]] ; then
  FIND="(?<=9[0-9a-f]{5}[0][0-9a-f])[0-9a-f]{152}"
else
  FIND="(?<=9[0-9a-f]{5}[^0][0-9a-f])[0-9a-f]{152}"
fi

# filter handling
TILE_V="8"
TILE_H="4"
LESSONE="$((${TILE_V}*${TILE_H}-1))"
FILTER="scale=iw/4:ih/4,transpose=2,tile=layout=${TILE_V}x${TILE_H}:init_padding=${LESSONE}:overlap=${LESSONE}:padding=1,transpose=1,setsar=1/1"

_play_dv(){
  ffmpeg "${INPUT_OPTS[@]}" -i "${1}" -c:v copy -f rawvideo - | \
  xxd -p -c 80 | \
  perl -pe "s|${FIND}|${FILL}|g" | \
  xxd -r -p | \
  ffplay - -vf "${FILTER}"
}

_make_dvrescue_xml(){
  if [[ ! -f "${DVRESCUE_XML}" ]] ; then
    echo "Assessing $(basename "${DVFILE}")..."
    _mkdir2 "${SIDECAR_DIR}"
    dvrescue "${DVFILE}" > "${DVRESCUE_XML}"
  fi
}

_maketemp(){
    mktemp -q "/tmp/$(basename "${0}").XXXXXX"
    if [ "${?}" -ne 0 ]; then
        echo "${0}: Can't create temp file, exiting..."
        _writeerrorlog "_maketemp" "was unable to create the temp file, so the script had to exit."
        exit 1
    fi
}

_mkdir2(){
    local DIR2MAKE=""
    while [ "${*}" != "" ] ; do
        DIR2MAKE="${1}"
        if [ ! -d "${DIR2MAKE}" ] ; then
            mkdir -p "${DIR2MAKE}"
            if [ "${?}" -ne 0 ]; then
                _report -wt "${0}: Can't create directory at ${DIR2MAKE}"
                exit 1
            fi
        fi
        shift
    done
}

while [[ "${@}" != "" ]] ; do
  DVFILE="${1}"
  shift
  BASENAME="$(basename "${DVFILE}")"
  if [[ -n "${USER_OUTPUT}" ]] && [[ -d "${USER_OUTPUT}" ]] ; then
    SIDECAR_DIR="${USER_OUTPUT}/${BASENAME}_dvrescue"
  elif [[ -n "${USER_OUTPUT}" ]] ; then
    echo "User selected output is not a valid directory. Exiting"
    exit 1
  else
    SIDECAR_DIR="${DVFILE}_dvrescue"
  fi
  DVRESCUE_XML="${SIDECAR_DIR}/${BASENAME}.dvrescue.xml"

  if [[ "${ERROR_REVIEW}" = "Y" ]] || [[ "${GIF_OUTPUT}" = "Y" ]] ; then
    _make_dvrescue_xml
    echo "Making jpegs of errors within $(basename "${DVFILE}")..."
    TOTAL_FRAMES="$(xmlstarlet sel -N d="https://mediaarea.net/dvrescue" -t -m "d:dvrescue/d:media" -v "sum(d:frames/@count)" -n "${DVRESCUE_XML}")"
    COUNTER=1
    xmlstarlet sel -N d="https://mediaarea.net/dvrescue" -t -m "d:dvrescue/d:media/d:frames/d:frame[d:sta]" -v @n -o "," -v @pts -o "," -v @tc -n "${DVRESCUE_XML}" | while read error_frame ; do
      N="$(echo "${error_frame}" | cut -d "," -f1)"
      PTS="$(echo "${error_frame}" | cut -d "," -f2)"
      TC="$(echo "${error_frame}" | cut -d "," -f3)"
      TC_FILENAME_SAFE="$(echo "${TC}" | sed 's|:|-|g')"
      PTS_C="$(echo "${PTS}" | sed 's|:|-|g')"
      PTS_E="$(echo "${PTS}" | sed 's|:|\\:|g')"
      TC_E="$(echo "${TC}" | sed 's|:|\\:|g')"
      if [[ "${GIF_OUTPUT}" = "Y" ]] ; then
        _mkdir2 "${SIDECAR_DIR}/dvrescue_tmp"
        JPG_OUTPUT="${SIDECAR_DIR}/dvrescue_tmp/${BASENAME}_$(echo "$COUNTER" | awk '{ printf("%06i", $1) }').jpg"
      else
        JPG_OUTPUT="${SIDECAR_DIR}/${BASENAME}_${TC_FILENAME_SAFE}.jpg"
      fi
      ffmpeg -nostdin -ss "${PTS}" -i "${DVFILE}" -vframes:v 1 -c:v copy -f rawvideo - | \
      xxd -p -c 80 | \
      perl -pe "s|${FIND}|${FILL}|g" | \
      xxd -r -p | \
      ffmpeg -nostdin -y -i - -vf "pad=w=iw:h=ih+24:x=0:y=24:color=gray,drawbox=t=fill:w=iw*(${N}/${TOTAL_FRAMES}):h=24:c=silver:x=0:y=0,drawtext=fontfile=${DEFAULTFONT}:y=4:x=4:text='PTS=${PTS_E} TC=${TC_E} Frame=${N}'" "${JPG_OUTPUT}"
      ((COUNTER=COUNTER+1))
    done
    if [[ "${GIF_OUTPUT}" = "Y" ]] ; then
      GIF_PALETTE="$(_maketemp)"
      ffmpeg -nostdin -f image2 -i "${SIDECAR_DIR}/dvrescue_tmp/${BASENAME}_%06d.jpg" -vf palettegen "${GIF_PALETTE}.png"
      ffmpeg -nostdin -f image2 -i "${SIDECAR_DIR}/dvrescue_tmp/${BASENAME}_%06d.jpg" -i "${GIF_PALETTE}.png" -filter_complex paletteuse "${SIDECAR_DIR}/${BASENAME}_error.gif"
      rm -r "${SIDECAR_DIR}/dvrescue_tmp" "${GIF_PALETTE}.png"
    fi
  else
    _play_dv "${DVFILE}"
  fi
done
