#!/bin/bash

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

Display a DV frame as hex with some labelling and color-coding. This script is part of the
dvrescue project.

Usage:
 $(basename "${0}") [options] -i file.dv

Options:
 -i INPUT   (provide an input file to examine)
 -b OFFSET  (provide a byte offset of the frame to examine, otherwise the first frame is default)
 -t PTS     (provide a timestamp to examine, otherwise the first frame is default. Ignored if '-b OFFSET' is set)
 -H         (only display DIF Blocks with a Section Type of Header)
 -S         (only display DIF Blocks with a Section Type of Subcode)
 -X         (only display DIF Blocks with a Section Type of vauX)
 -A         (only display DIF Blocks with a Section Type of Audio)
 -V         (only display DIF Blocks with a Section Type of Video)
 -E         (if -V is used, then only show Video DIF Blocks that note video error concealment)
 -O         (only display DIF Blocks with that are not defined types (Other). This indicates a rare and invalid block.)

 -f FMT     (print the output in a format. The options are: html, json, and txt)

 -T [y|n]   (for json and html outputs, enable or disable adding mediatrace data to the output, default is 'y')

 -F <path> (provide a custom ffmpeg path)
 -M <path> (provide a custom mediainfo path)
 -x <path> (provide a custom xmlstarlet path)
EOF
    exit
}

_maketemp(){
    mktemp -q -t "$(basename "${0}").XXXXXX"
    if [ "${?}" -ne 0 ]; then
        echo "${0}: Can't create temp file, exiting..."
        exit 1
    fi
}

_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 MAGENTA="$(tput setaf 5)" # Magenta - For Verbose Statements
    local NC="$(tput sgr0)"        # No Color
    local COLOR=""
    local STARTMESSAGE=()
    local ECHOOPT=""
    local VERBOSE_CHECK=false
    OPTIND=1
    while getopts "vqdwstn" opt ; do
        case "${opt}" in
            v) VERBOSE_CHECK=true ;
               COLOR="${MAGENTA}" ;
               STARTMESSAGE+="#" ;; # only output the message if DV_VERBOSE=true
            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}"
    if ! "${VERBOSE_CHECK}" || ( "${DV_VERBOSE}" && "${VERBOSE_CHECK}" ) ; then
        if [[ "${DISABLE_COLORED_LOGGING}" = "Y" ]] ; then
            >&2 echo ${ECHOOPT} "${STARTMESSAGE[@]}${MESSAGE}"
        else
            >&2 echo ${ECHOOPT} "${COLOR}${STARTMESSAGE[@]}${MESSAGE}${NC}"
        fi
    fi
}

FORMAT="txt"
ADD_MEDIATRACE="y"
SCT=""

# command-line options to set media id and original variables
OPTIND=1
while getopts ":t:b:HSXAVEOf:T:i:hF:M:x:" opt ; do
  case "${opt}" in
    t) FFMPEG_INPUT_OPTS=(-ss ${OPTARG}) ; PTS="$OPTARG" ;;
    b) FFMPEG_INPUT_OPTS=(-skip_initial_bytes ${OPTARG}) ; OFFSET="$OPTARG" ;;
    H) SCT+="H" ;;
    S) SCT+="S" ;;
    X) SCT+="X" ;;
    A) SCT+="A" ;;
    V) SCT+="V" ;;
    E) SCT+="E" ;;
    O) SCT+="O" ;;
    f) FORMAT="${OPTARG}" ;;
    T) ADD_MEDIATRACE="${OPTARG}" ;;
    i) DV_FILE="$OPTARG" ;;
    F) FFMPEG_PATH="${OPTARG}" ;;
    M) MEDIAINFO_PATH="${OPTARG}" ;;
    x) XMLSTARLET_PATH="${OPTARG}" ;;
    h) _usage ;;
    :) echo "Option -${OPTARG} requires an argument" ; exit 1 ;;
    *) echo "bad option -${OPTARG}" ; _usage ; exit 1 ;;
  esac
done
shift "$((OPTIND-1))"

if [[ ! -f "$DV_FILE" ]] ; then
  if [[ -f "${1}" ]] ; then
    DV_FILE="${1}"
  else
    _report -w "Please provide an input file to examine."
    exit 1
  fi
fi

if [[ -z "${FFMPEG_PATH}" ]] ; then
    FFMPEG_PATH="$(which ffmpeg)"
fi
if [[ ! -f "${FFMPEG_PATH}" ]] ; then
    _report -w "The ffmpeg command-line tool is not found, but needed."
    _report -w "Please install a ffmpeg CLI or provide a path to mediainfo with the -F argument."
    exit 1
fi
if [[ -z "${MEDIAINFO_PATH}" ]] ; then
    MEDIAINFO_PATH="$(which mediainfo)"
fi
if [[ ! -f "${MEDIAINFO_PATH}" ]] ; then
    _report -w "The mediainfo command-line tool is not found, but is used to add context to some outputs."
    _report -w "Please install a mediainfo CLI from https://mediaarea.net/en/MediaInfo/Download or provide a path to mediainfo with the -M argument."
fi
if [[ -z "${XMLSTARLET_PATH}" ]] ; then
    XMLSTARLET_PATH="$(which xmlstarlet)"
fi
if [[ ! -f "${XMLSTARLET_PATH}" ]] ; then
    _report -w "The xmlstarlet command-line tool is not found, but is used to add context to some outputs."
    exit 1
fi

if [[ "${FORMAT}" == "txt" ]] ; then
    COLOR_ID="\033[47m" #gray background
    COLOR_RESET="\033[0m"
    COLOR_HEADER="\033[35m"
    COLOR_SUBCODE="\033[36m"
    COLOR_VAUX="\033[33m"
    COLOR_AUDIO="\033[32m"
    COLOR_VIDEO="\033[34m"
    COLOR_OTHER="\033[31m"
    COLOR_ERROR="\033[31m"
else
    COLOR_HEADER="800080"  #purple
    COLOR_SUBCODE="008080" #teal
    COLOR_VAUX="808000"    #olive
    COLOR_AUDIO="008000"   #green
    COLOR_VIDEO="000080"   #navyblue
    COLOR_OTHER="800000"   #maroon
    COLOR_ERROR="FF0000"   #red
fi

FFMPEG_OUTPUT_OPTS=(-vframes 1)
FFMPEG_OUTPUT_OPTS+=(-map 0:v)
FFMPEG_OUTPUT_OPTS+=(-c copy)
FFMPEG_OUTPUT_OPTS+=(-f rawvideo)
if [[ "${SCT}" = "" || "${SCT}" =~ "V" ]] ; then
    VIDEO_TABLE_1="| Sq | Blo |  i | j |  k | Mx | My |  w |  h |    x |    y |"
    VIDEO_TABLE_2="|----|-----|----|---|----|----|----|----|----|------|------|"
fi

if [[ "${FORMAT}" == "json" ]] ; then
    HEADER='{"rows":['
    FOOTER="]}\n"
    ROW_START='{"cells": [\n'
    CELL_START2ID='{"id":"'
    CELL_2TITLE='", "tooltip": "'
    CELL_2STYLE='", "color": "'
    CELL_2VBL='", "vbl": "'
    CELL_2LOC='", "loc": "'
    CELL_2VALUE='", "value": "'
    CELL_END='"}'
    DELIMITER=",\n"
    ROW_END="\n]}"
elif [[ "${FORMAT}" == "html" ]] ; then
    HEADER="<html><body><table style='font-family:\"Courier New\", Courier, monospace; font-size:80%; white-space: pre-line'>"
    FOOTER="</table></body></html>\n"
    ROW_START="<tr>"
    CELL_START2ID="<td id=\""
    CELL_2TITLE="\" title=\""
    CELL_2STYLE="\" style=\"color:#"
    CELL_2VBL="\" vbl=\""
    CELL_2LOC="\" loc=\""
    CELL_2VALUE="\">"
    CELL_END="</td>"
    DELIMITER=""
    ROW_END="</tr>\n"
else
    HEADER="Offset | DIF Block                                                                                                                                                        ${VIDEO_TABLE_1}\n-------|------------------------------------------------------------------------------------------------------------------------------------------------------------------${VIDEO_TABLE_2}"
    FOOTER=""
    ROW_START=""
    CELL_START2ID=""
    CELL_2TITLE=""
    CELL_2STYLE=""
    CELL_2VALUE=""
    CELL_END=" | "
    DELIMITER=""
    RESET="${COLOR_RESET}"
    ROW_END="${RESET}\n"
fi

MEDIAINFO_STATS="$("${MEDIAINFO_PATH}" --Inform="Video;%Format%|%Height%|%ChromaSubsampling%" "${DV_FILE}")"
CODEC_NAME="$(echo "${MEDIAINFO_STATS}" | cut -d "|" -f1)"
HEIGHT="$(echo "${MEDIAINFO_STATS}" | cut -d "|" -f2)"
PIX_FMT="$(echo "${MEDIAINFO_STATS}" | cut -d "|" -f3)"

if [[ "${CODEC_NAME}" != "DV" ]] ; then
  _report -w "$DV_FILE does not seem to be a dv file, but is ${CODEC_NAME}."
  exit
fi
if [[ "${PIX_FMT}" = "4:1:1" ]] ; then
    YUV411P="1"
else
    YUV411P="0"
fi
if [[ "${HEIGHT}" = "576" ]] ; then
    PAL="1"
else
    PAL="0"
fi

DVFRAME="$(_maketemp)"

DV_BLOCK_INFO="VCOUNTER++;
    BLO=(VCOUNTER-1)%135;
    SEQ=int((VCOUNTER-1)/135);
    if(PAL == 1){SEQCOUNT=12} else {SEQCOUNT=10}
    BLO_M5=(BLO)%5;
    if(BLO_M5 == 0) {I5=2; J=2} else if (BLO_M5 == 1) {I5=6; J=1} else if (BLO_M5 == 2) {I5=8; J=3} else if (BLO_M5 == 3) {I5=0; J=0} else {I5=4; J=4};
    I=(I5+SEQ)%SEQCOUNT;
    K=int(((VCOUNTER-1)/5)%27);
    if(YUV411P == 1 && ((J < 4) || (J == 4 && K < 24))){W=32; H=8} else {W=16; H=16};
    if(YUV411P == 1){WL=32; MACROBLOCK_ROWS=6; MBX_OFFSET=(J%2)*3} else {WL=16; MACROBLOCK_ROWS=3; MBX_OFFSET=0}
    MBx=int((K+MBX_OFFSET)/MACROBLOCK_ROWS)
    if(MBx%2 == 0){MByt=K+MBX_OFFSET} else {MByt=(K+MBX_OFFSET+MACROBLOCK_ROWS-1-(((K+MBX_OFFSET)%MACROBLOCK_ROWS)*2))}
    MBy=MByt%MACROBLOCK_ROWS
    if(YUV411P == 1 && J%2 == 1){SBx=J*144-16} else {SBx=J*144}
    X=SBx+(WL*MBx)
    Y=(I*48)+(MBy*H)
    LOC=(W\":\"H\":\"X\":\"Y)
"

"${FFMPEG_PATH}" -y -v 0 "${FFMPEG_INPUT_OPTS[@]}" -i "${DV_FILE}" "${FFMPEG_OUTPUT_OPTS[@]}" "${DVFRAME}"
if [[ "${ADD_MEDIATRACE}" == "y" && ( "${FORMAT}" == "html" || "${FORMAT}" == "json" ) ]] ; then
    LOOKUP="$(_maketemp)"
    LOOKUP_PARSE_INSTRUCTIONS="{FS=OFS=\"\|\"}NR==FNR { trace[\$1]=\$2; next }"
    LOOKUP_HEADER="CELL_2TITLE trace[(FNR-1)*80]"
    LOOKUP_HEADER_3="CELL_2TITLE trace[(FNR-1)*80+3]"
    XML_QUERY=(-m "/mt:MediaTrace/mt:media/mt:block")
    XML_QUERY+=(--var off=@offset -v @offset -o "|")
    XML_QUERY+=(-m ".//mt:data[@offset>=\$off and @offset<(\$off+3)]" -v @offset -o ":" -v @name -o "=" -v . -o "&#013;&#010;" -b -n -v "(\$off+3)" -o "|")
    XML_QUERY+=(-m ".//mt:data[@offset>=(\$off+3) and @offset<(\$off+80)]" -v @offset -o ":" -v @name -o "=" -v . -o "&#013;&#010;" -b -n)
    "${MEDIAINFO_PATH}" --Details=1 --Output=XML "${DVFRAME}" | "${XMLSTARLET_PATH}" sel -N mt="https://mediaarea.net/mediatrace" -T -t "${XML_QUERY[@]}" > "${LOOKUP}"
    if [[ ! -s "${LOOKUP}" ]] ; then
        unset LOOKUP_PARSE_INSTRUCTIONS LOOKUP_HEADER LOOKUP_HEADER_3
        _report -w "The process to add MediaTrace metadata to the report did not work. :( Check that recent copies of mediainfo and xmlstarlet are installed."
    fi
fi

if [[ "${FORMAT}" == "html" || "${FORMAT}" == "json" ]] ; then
    PRINT_ID="if (OUTPUT == 1 ) printf DELIMITER ; printf     ROW_START CELL_START2ID (FNR-1)*80   ${LOOKUP_HEADER}   CELL_2VALUE substr(\$0,1,6) CELL_END DELIMITER"
    PRINT_HEADER="printf           CELL_START2ID (FNR-1)*80+3 ${LOOKUP_HEADER_3} CELL_2STYLE COLOR_HEADER  CELL_2VALUE substr(\$0,7,160) CELL_END ROW_END"
    PRINT_SUBCODE="printf          CELL_START2ID (FNR-1)*80+3 ${LOOKUP_HEADER_3} CELL_2STYLE COLOR_SUBCODE CELL_2VALUE substr(\$0,7,160) CELL_END ROW_END"
    PRINT_VAUX="printf             CELL_START2ID (FNR-1)*80+3 ${LOOKUP_HEADER_3} CELL_2STYLE COLOR_VAUX    CELL_2VALUE substr(\$0,7,160) CELL_END ROW_END"
    PRINT_AUDIO="printf            CELL_START2ID (FNR-1)*80+3 ${LOOKUP_HEADER_3} CELL_2STYLE COLOR_AUDIO   CELL_2VALUE substr(\$0,7,160) CELL_END ROW_END"
    PRINT_VIDEO="printf            CELL_START2ID (FNR-1)*80+3 ${LOOKUP_HEADER_3} CELL_2STYLE COLOR_VIDEO   CELL_2VBL VBL CELL_2LOC LOC CELL_2VALUE substr(\$0,7,160) CELL_END ROW_END"
    PRINT_VIDEO_ERROR="printf            CELL_START2ID (FNR-1)*80+3 ${LOOKUP_HEADER_3} CELL_2STYLE COLOR_ERROR   CELL_2VBL VBL CELL_2LOC LOC CELL_2VALUE substr(\$0,7,160) CELL_END ROW_END"
    PRINT_OTHER="printf            CELL_START2ID (FNR-1)*80+3 ${LOOKUP_HEADER_3} CELL_2STYLE COLOR_OTHER   CELL_2VALUE substr(\$0,7,160) CELL_END ROW_END"
else
    PRINT_ID="printf     substr(\"000000\"(FNR-1)*80,1+length(\"000000\"(FNR-1)*80)-6) \" | \" COLOR_ID substr(\$0,1,3) COLOR_RESET substr(\$0,4,3)"
    PRINT_HEADER="printf           COLOR_HEADER  substr(\$0,7,160) ROW_END"
    PRINT_SUBCODE="printf          COLOR_SUBCODE substr(\$0,7,160) ROW_END"
    PRINT_VAUX="printf             COLOR_VAUX    substr(\$0,7,160) ROW_END"
    PRINT_AUDIO="printf            COLOR_AUDIO   substr(\$0,7,160) ROW_END"
    PRINT_VIDEO=" printf           (COLOR_VIDEO   substr(\$0,7,154) RESET \" | %4i | %2i | %3i | %2i | %1i | %2i | %2i | %2i | %2i | %2i | %4i | %4i \" ROW_END, VBL, SEQ, BLO, I, J, K, MBx, MBy, W, H, X, Y)"
    PRINT_VIDEO_ERROR=" printf     (COLOR_ERROR   substr(\$0,7,154) RESET \" | %4i | %2i | %3i | %2i | %1i | %2i | %2i | %2i | %2i | %2i | %4i | %4i \" ROW_END, VBL, SEQ, BLO, I, J, K, MBx, MBy, W, H, X, Y)"
    PRINT_OTHER="printf            COLOR_OTHER   substr(\$0,7,160) ROW_END"
fi

echo -e "${HEADER}"
xxd -ps -c 80 "${DVFRAME}" | \
    awk -v ROW_START="${ROW_START}" -v CELL_START2ID="${CELL_START2ID}" -v CELL_2TITLE="${CELL_2TITLE}" -v CELL_2STYLE="${CELL_2STYLE}" \
        -v CELL_2VBL="${CELL_2VBL}" -v CELL_2LOC="${CELL_2LOC}" -v CELL_2VALUE="${CELL_2VALUE}" -v CELL_END="${CELL_END}" -v ROW_END="${ROW_END}" \
        -v COLOR_HEADER="${COLOR_HEADER}" -v COLOR_SUBCODE="${COLOR_SUBCODE}" -v COLOR_VAUX="${COLOR_VAUX}" -v COLOR_VIDEO="${COLOR_VIDEO}" -v COLOR_AUDIO="$COLOR_AUDIO" -v COLOR_OTHER="${COLOR_OTHER}" -v COLOR_ERROR="${COLOR_ERROR}" \
        -v COLOR_ID="${COLOR_ID}" -v COLOR_RESET="${COLOR_RESET}" -v RESET="${RESET}" -v CROP_ARRAY="${DIF_BLOCK_2_POS_MAP_NTSC_411_DV25[*]}" \
        -v DELIMITER="${DELIMITER}" -v YUV411P="${YUV411P}" -v PAL="${PAL}" -v SCT="${SCT}" -v VBL=0 \
"${LOOKUP_PARSE_INSTRUCTIONS}"' {diftype=substr($0,1,1);
if      (diftype == "1" && (SCT ~ "H" || SCT == "")) {'"${PRINT_ID}"' ; '"${PRINT_HEADER}"' ; OUTPUT=1 ;}
else if (diftype == "3" && (SCT ~ "S" || SCT == "")) {'"${PRINT_ID}"' ; '"${PRINT_SUBCODE}"' ; OUTPUT=1 ;}
else if (diftype == "5" && (SCT ~ "X" || SCT == "")) {'"${PRINT_ID}"' ; '"${PRINT_VAUX}"' ; OUTPUT=1 ;}
else if (diftype == "7" && (SCT ~ "A" || SCT == "")) {'"${PRINT_ID}"' ; '"${PRINT_AUDIO}"' ; OUTPUT=1 ;}
else if (diftype == "9" && (SCT ~ "V" || SCT == "")) {
    STA_VALUE=substr($0,7,1)
    if      (STA_VALUE != "0" && SCT  ~ "E") {'"${PRINT_ID}"' ; '"${DV_BLOCK_INFO}${PRINT_VIDEO_ERROR}"'; OUTPUT=1 ;}
    else if (STA_VALUE == "0" && SCT !~ "E") {'"${PRINT_ID}"' ; '"${DV_BLOCK_INFO}${PRINT_VIDEO}"';       OUTPUT=1 ;}
    else if (STA_VALUE != "0" && SCT !~ "E") {'"${PRINT_ID}"' ; '"${DV_BLOCK_INFO}${PRINT_VIDEO_ERROR}"'; OUTPUT=1 ;}
    VBL++
}
else if (diftype != "1" && diftype != "3" && diftype != "5" && diftype != "7" && diftype != "9" && (SCT ~ "O" || SCT == ""))  {'"${PRINT_ID}"' ;'"${PRINT_OTHER}"' ; OUTPUT=1 ;}
}' "${LOOKUP}" -
echo -n -e "${FOOTER}"

if [[ -f "${DVFRAME}" ]] ; then
    rm "${DVFRAME}"
fi
if [[ -f "${LOOKUP}" ]] ; then
    rm "${LOOKUP}"
fi

