# Copyright: 2021-2023 Paul Obermeier (obermeier@poSoft.de)
# Distributed under BSD license.

set pawtDir [file join [pwd] ".."]
set auto_path [linsert $auto_path 0 $pawtDir]

namespace eval pawtTest {

    namespace ensemble create

    namespace export AddImgDicts
    namespace export AddImgPhotos
    namespace export CheckFloatMatrix
    namespace export CheckFloatList
    namespace export CheckList
    namespace export CheckMatrix
    namespace export CheckBoolean
    namespace export CheckFloat
    namespace export CheckNumber
    namespace export CheckString
    namespace export CheckFile
    namespace export CreateDictError
    namespace export CreateDictUnsupported
    namespace export CreateList-byte
    namespace export CreateList-short
    namespace export CreateList-int
    namespace export CreateList-float
    namespace export CreateList-double
    namespace export CreateMatrix-byte
    namespace export CreateMatrix-short
    namespace export CreateMatrix-int
    namespace export CreateMatrix-float
    namespace export CreateMatrix-double
    namespace export CreatePhotoError
    namespace export CreatePhotoUnsupported
    namespace export GetGreyListTD-byte
    namespace export GetGreyListTD-short
    namespace export GetGreyListTD-int
    namespace export GetGreyListTD-float
    namespace export GetGreyListTD-double
    namespace export GetGreyListBU-byte
    namespace export GetGreyListBU-short
    namespace export GetGreyListBU-int
    namespace export GetGreyListBU-float
    namespace export GetGreyListBU-double
    namespace export GetGreyMatrixTD-byte
    namespace export GetGreyMatrixTD-short
    namespace export GetGreyMatrixTD-int
    namespace export GetGreyMatrixTD-float
    namespace export GetGreyMatrixTD-double
    namespace export GetGreyMatrixBU-byte
    namespace export GetGreyMatrixBU-short
    namespace export GetGreyMatrixBU-int
    namespace export GetGreyMatrixBU-float
    namespace export GetGreyMatrixBU-double
    namespace export GetRgbListTD-byte
    namespace export GetRgbListTD-short
    namespace export GetRgbListTD-int
    namespace export GetRgbListTD-float
    namespace export GetRgbListTD-double
    namespace export GetRgbListBU-byte
    namespace export GetRgbListBU-short
    namespace export GetRgbListBU-int
    namespace export GetRgbListBU-float
    namespace export GetRgbListBU-double
    namespace export GetRgbMatrixTD-byte
    namespace export GetRgbMatrixTD-short
    namespace export GetRgbMatrixTD-int
    namespace export GetRgbMatrixTD-float
    namespace export GetRgbMatrixTD-double
    namespace export GetRgbMatrixBU-byte
    namespace export GetRgbMatrixBU-short
    namespace export GetRgbMatrixBU-int
    namespace export GetRgbMatrixBU-float
    namespace export GetRgbMatrixBU-double
    namespace export ShowImgDict


    #
    # Prcedures to generate 8-bit byte test data.
    #

    proc GetGreyListTD-byte {} {
        return { \
              0  50 100 150 200 255 \
            255 200 150 100 50    0 \
        }
    }

    proc GetGreyListBU-byte {} {
        return { \
            255 200 150 100 50    0 \
              0  50 100 150 200 255 \
        }
    }

    proc GetGreyMatrixTD-byte {} {
        return { \
            {   0  50 100 150 200 255 } \
            { 255 200 150 100 50    0 } \
        }
    }

    proc GetGreyMatrixBU-byte {} {
        return { \
            { 255 200 150 100 50    0 } \
            {   0  50 100 150 200 255 } \
        }
    }

    proc GetRgbListTD-byte {} {
        return { \
              0   0   0   255 255 255   255   0 0     0 255 0     0   0 255   128 128 128 \
            128 128 128     0   0 255     0 255 0   255   0 0   255 255 255     0   0   0 \
        }
    }

    proc GetRgbListBU-byte {} {
        return { \
            128 128 128     0   0 255     0 255 0   255   0 0   255 255 255     0   0   0 \
              0   0   0   255 255 255   255   0 0     0 255 0     0   0 255   128 128 128 \
        }
    }

    proc GetRgbMatrixTD-byte {} {
        return { \
            {   0   0   0   255 255 255   255   0 0     0 255 0     0   0 255   128 128 128 } \
            { 128 128 128     0   0 255     0 255 0   255   0 0   255 255 255     0   0   0 } \
        }
    }

    proc GetRgbMatrixBU-byte {} {
        return { \
            { 128 128 128     0   0 255     0 255 0   255   0 0   255 255 255     0   0   0 } \
            {   0   0   0   255 255 255   255   0 0     0 255 0     0   0 255   128 128 128 } \
        }
    }

    #
    # Prcedures to generate 16-bit short test data.
    #

    proc GetGreyListTD-short {} {
        return { \
                0 13000 26000 39000 52000 65535 \
            65535 52000 39000 26000 13000     0 \
        }
    }

    proc GetGreyListBU-short {} {
        return { \
            65535 52000 39000 26000 13000     0 \
                0 13000 26000 39000 52000 65535 \
        }
    }

    proc GetGreyMatrixTD-short {} {
        return { \
            {     0 13000 26000 39000 52000 65535 } \
            { 65535 52000 39000 26000 13000     0 } \
        }
    }

    proc GetGreyMatrixBU-short {} {
        return { \
            { 65535 52000 39000 26000 13000     0 } \
            {     0 13000 26000 39000 52000 65535 } \
        }
    }

    proc GetRgbListTD-short {} {
        return { \
                0     0     0   65535 65535 65535   65535     0 0       0 65535 0       0     0 65535   30000 30000 30000 \
            30000 30000 30000       0     0 65535       0 65535 0   65535     0 0   65535 65535 65535        0    0     0 \
        }
    }

    proc GetRgbListBU-short {} {
        return { \
            30000 30000 30000       0     0 65535       0 65535 0   65535     0 0   65535 65535 65535        0    0     0 \
                0     0     0   65535 65535 65535   65535     0 0       0 65535 0       0     0 65535   30000 30000 30000 \
        }
    }

    proc GetRgbMatrixTD-short {} {
        return { \
            {     0     0     0   65535 65535 65535   65535     0 0       0 65535 0       0     0 65535   30000 30000 30000 } \
            { 30000 30000 30000       0     0 65535       0 65535 0   65535     0 0   65535 65535 65535        0    0     0 } \
        }
    }

    proc GetRgbMatrixBU-short {} {
        return { \
            { 30000 30000 30000       0     0 65535       0 65535 0   65535     0 0   65535 65535 65535        0    0     0 } \
            {     0     0     0   65535 65535 65535   65535     0 0       0 65535 0       0     0 65535   30000 30000 30000 } \
        }
    }

    #
    # Prcedures to generate 32-bit int test data.
    #

    proc GetGreyListTD-int {} {
        return { \
                     0  800000000 1600000000 2400000000 3200000000 4294967295 \
            4294967295 3200000000 2400000000 1600000000  800000000          0 \
        }
    }

    proc GetGreyListBU-int {} {
        return { \
            4294967295 3200000000 2400000000 1600000000  800000000          0 \
                     0  800000000 1600000000 2400000000 3200000000 4294967295 \
        }
    }

    proc GetGreyMatrixTD-int {} {
        return { \
            {          0  800000000 1600000000 2400000000 3200000000 4294967295 } \
            { 4294967295 3200000000 2400000000 1600000000  800000000          0 } \
        }
    }

    proc GetGreyMatrixBU-int {} {
        return { \
            { 4294967295 3200000000 2400000000 1600000000  800000000          0 } \
            {          0  800000000 1600000000 2400000000 3200000000 4294967295 } \
        }
    }

    proc GetRgbListTD-int {} {
        return { \
                     0          0          0   4294967295 4294967295 4294967295   4294967295          0 0           0 4294967295 0           0           0 4294967295   2000000000 2000000000 2000000000 \
            2000000000 2000000000 2000000000            0          0 4294967295            0 4294967295 0   4294967295         0 0   4294967295 4294967295 4294967295            0          0          0 \
        }
    }

    proc GetRgbListBU-int {} {
        return { \
            2000000000 2000000000 2000000000            0          0 4294967295            0 4294967295 0   4294967295          0 0   4294967295 4294967295 4294967295            0          0          0 \
                     0          0          0   4294967295 4294967295 4294967295   4294967295          0 0            0 4294967295 0            0          0 4294967295   2000000000 2000000000 2000000000 \
        }
    }

    proc GetRgbMatrixTD-int {} {
        return { \
            {          0          0          0   4294967295 4294967295 4294967295   4294967295          0 0            0 4294967295 0            0          0 4294967295   2000000000 2000000000 2000000000 } \
            { 2000000000 2000000000 2000000000            0          0 4294967295            0 4294967295 0   4294967295          0 0   4294967295 4294967295 4294967295            0          0          0 } \
        }
    }

    proc GetRgbMatrixBU-int {} {
        return { \
            { 2000000000 2000000000 2000000000            0          0 4294967295            0 4294967295 0   4294967295          0 0   4294967295 4294967295 4294967295            0          0          0 } \
            {          0          0          0   4294967295 4294967295 4294967295   4294967295          0 0            0 4294967295 0            0          0 4294967295   2000000000 2000000000 2000000000 } \
        }
    }

    #
    # Prcedures to generate 32-bit float test data.
    #

    proc GetGreyListTD-float {} {
        return { \
            0.0 0.2 0.4 0.6 0.8 1.0 \
            1.0 0.8 0.6 0.4 0.2 0.0 \
        }
    }

    proc GetGreyListBU-float {} {
        return { \
            1.0 0.8 0.6 0.4 0.2 0.0 \
            0.0 0.2 0.4 0.6 0.8 1.0 \
        }
    }

    proc GetGreyMatrixTD-float {} {
        return { \
            { 0.0 0.2 0.4 0.6 0.8 1.0 } \
            { 1.0 0.8 0.6 0.4 0.2 0.0 } \
        }
    }

    proc GetGreyMatrixBU-float {} {
        return { \
            { 1.0 0.8 0.6 0.4 0.2 0.0 } \
            { 0.0 0.2 0.4 0.6 0.8 1.0 } \
        }
    }

    proc GetRgbListTD-float {} {
        return { \
            0.0 0.0 0.0   1.0 1.0 1.0   1.0 0.0 0.0   0.0 1.0 0.0   0.0 0.0 1.0   0.5 0.5 0.5 \
            0.5 0.5 0.5   0.0 0.0 1.0   0.0 1.0 0.0   1.0 0.0 0.0   1.0 1.0 1.0   0.0 0.0 0.0 \
        }
    }

    proc GetRgbListBU-float {} {
        return { \
            0.5 0.5 0.5   0.0 0.0 1.0   0.0 1.0 0.0   1.0 0.0 0.0   1.0 1.0 1.0   0.0 0.0 0.0 \
            0.0 0.0 0.0   1.0 1.0 1.0   1.0 0.0 0.0   0.0 1.0 0.0   0.0 0.0 1.0   0.5 0.5 0.5 \
        }
    }

    proc GetRgbMatrixTD-float {} {
        return { \
            { 0.0 0.0 0.0   1.0 1.0 1.0   1.0 0.0 0.0   0.0 1.0 0.0   0.0 0.0 1.0   0.5 0.5 0.5 } \
            { 0.5 0.5 0.5   0.0 0.0 1.0   0.0 1.0 0.0   1.0 0.0 0.0   1.0 1.0 1.0   0.0 0.0 0.0 } \
        }
    }

    proc GetRgbMatrixBU-float {} {
        return { \
            { 0.5 0.5 0.5   0.0 0.0 1.0   0.0 1.0 0.0   1.0 0.0 0.0   1.0 1.0 1.0   0.0 0.0 0.0 } \
            { 0.0 0.0 0.0   1.0 1.0 1.0   1.0 0.0 0.0   0.0 1.0 0.0   0.0 0.0 1.0   0.5 0.5 0.5 } \
        }
    }

    #
    # Prcedures to generate 64-bit double test data.
    #

    proc GetGreyListTD-double {} {
        return [GetGreyListTD-float]
    }

    proc GetGreyListBU-double {} {
        return [GetGreyListBU-float]
    }

    proc GetGreyMatrixTD-double {} {
        return [GetGreyMatrixTD-float]
    }

    proc GetGreyMatrixBU-double {} {
        return [GetGreyMatrixBU-float]
    }

    proc GetRgbListTD-double {} {
        return [GetRgbListTD-float]
    }

    proc GetRgbListBU-double {} {
        return [GetRgbListBU-float]
    }

    proc GetRgbMatrixTD-double {} {
        return [GetRgbMatrixTD-float]
    }

    proc GetRgbMatrixBU-double {} {
        return [GetRgbMatrixBU-float]
    }

    proc CreateList-byte {} {
        set imgSize 256
        set numPixel [expr $imgSize * $imgSize]
        for { set y 0 } { $y < $imgSize } { incr y } {
            set color 0
            for { set x 0 } { $x < $imgSize } { incr x } {
                lappend imgList $color
                incr color
            }
        }
        return $imgList
    }

    proc CreateList-short {} {
        set imgSize 256
        set numPixel [expr $imgSize * $imgSize]
        set color 0
        for { set y 0 } { $y < $imgSize } { incr y } {
            for { set x 0 } { $x < $imgSize } { incr x } {
                lappend imgList $color
                incr color
            }
        }
        return $imgList
    }

    proc CreateList-int {} {
        return [CreateList-short]
    }

    proc CreateList-float {} {
        set imgSize 256
        set numPixel [expr $imgSize * $imgSize]
        set color 0
        for { set y 0 } { $y < $imgSize } { incr y } {
            for { set x 0 } { $x < $imgSize } { incr x } {
                lappend imgList [expr { double ($color) }]
                incr color
            }
        }
        return $imgList
    }

    proc CreateList-double {} {
        return [CreateList-float]
    }

    proc CreateMatrix-byte {} {
        set imgSize 256
        set numPixel [expr $imgSize * $imgSize]
        for { set y 0 } { $y < $imgSize } { incr y } {
            set color 0
            for { set x 0 } { $x < $imgSize } { incr x } {
                lappend rowList $color
                incr color
            }
            lappend imgMatrix $rowList
            set rowList [list]
        }
        return $imgMatrix
    }

    proc CreateMatrix-short {} {
        set imgSize 256
        set numPixel [expr $imgSize * $imgSize]
        set color 0
        for { set y 0 } { $y < $imgSize } { incr y } {
            for { set x 0 } { $x < $imgSize } { incr x } {
                lappend rowList $color
                incr color
            }
            lappend imgMatrix $rowList
            set rowList [list]
        }
        return $imgMatrix
    }

    proc CreateMatrix-int {} {
        return [CreateMatrix-short]
    }

    proc CreateMatrix-float {} {
        set imgSize 256
        set numPixel [expr $imgSize * $imgSize]
        set color 0
        for { set y 0 } { $y < $imgSize } { incr y } {
            for { set x 0 } { $x < $imgSize } { incr x } {
                lappend rowList [expr { double($color) }]
                incr color
            }
            lappend imgMatrix $rowList
            set rowList [list]
        }
        return $imgMatrix
    }

    proc CreateMatrix-double {} {
        return [CreateMatrix-float]
    }

    proc CreateDictError { width height } {
        set fillList [lrepeat [expr $width * $height] 255 0 0]
        return [pawt CreateImage -pixeltype "byte" -numchan 3 -width $width -height $height -list $fillList]
    }

    proc CreateDictUnsupported { width height } {
        set fillList [lrepeat [expr $width * $height] 255 255 0]
        return [pawt CreateImage -pixeltype "byte" -numchan 3 -width $width -height $height -list $fillList]
    }

    proc _CreatePhoto { width height color } {
        set phImg [image create photo -width $width -height $height]
        set scanline [list]
        for { set x 0 } { $x < $width } { incr x } {
            lappend scanline $color
        }
        set data [list]
        lappend data $scanline
        for { set y 0 } { $y < $height } { incr y } {
            $phImg put $data -to 0 $y
        }
        return $phImg
    }

    proc CreatePhotoError { width height } {
        return [_CreatePhoto $width $height "#FF0000"]
    }

    proc CreatePhotoUnsupported { width height } {
        return [_CreatePhoto $width $height "#FFFF00"]
    }

    proc CheckString { expected value msg { printCheck true } } {
        # Check, if two string values are identical.
        #
        # expected   - Expected string value.
        # value      - Test string value.
        # msg        - Message for test case.
        # printCheck - Print message for successful test case.
        #
        # Returns true, if both string values are identical.
        # If "printCheck" is set to true, a line prepended with `"Check:"` and the
        # message supplied in "msg" is printed to standard output.
        # If the check fails, return false and print message prepended with `"Error:"`.
        #
        # See also: CheckList CheckFloatList CheckMatrix CheckBoolean CheckNumber

        if { $expected ne $value } {
            puts "Error: $msg (Expected: \"$expected\" Have: \"$value\")"
            return false
        }
        if { $printCheck } {
            puts "Check: $msg (Expected: \"$expected\" Have: \"$value\")"
        }
        return true
    }

    proc CheckBoolean { expected value msg { printCheck true } } {
        # Check, if two boolean values are identical.
        #
        # expected   - Expected boolean value.
        # value      - Test boolean value.
        # msg        - Message for test case.
        # printCheck - Print message for successful test case.
        #
        # Returns true, if both boolean values are identical.
        # If $printCheck is set to true, a line prepended with `"Check:`" and the
        # message supplied in $msg is printed to standard output.
        # If the check fails, return false and print message prepended with `"Error:`".
        #
        # See also: CheckNumber CheckList CheckFloatList CheckMatrix CheckString

        if { [expr bool($expected)] != [expr bool($value)] } {
            puts "Error: $msg (Expected: $expected Have: $value)"
            return false
        }
        if { $printCheck } {
            puts "Check: $msg (Expected: $expected Have: $value)"
        }
        return true
    }

    proc CheckNumber { expected value msg { printCheck true } } {
        # Check, if two numerical values are identical.
        #
        # expected   - Expected numeric value.
        # value      - Test numeric value.
        # msg        - Message for test case.
        # printCheck - Print message for successful test case.
        #
        # Returns true, if both numeric values are identical.
        # If $printCheck is set to true, a line prepended with `"Check:"` and the
        # message supplied in $msg is printed to standard output.
        # If the check fails, return false and print message prepended with `"Error:"`.
        #
        # See also: CheckBoolean CheckFloat CheckList CheckFloatList CheckMatrix CheckString

        if { $expected != $value } {
            puts "Error: $msg (Expected: $expected Have: $value)"
            return false
        }
        if { $printCheck } {
            puts "Check: $msg (Expected: $expected Have: $value)"
        }
        return true
    }

    proc CheckFloat { expected value msg precision { printCheck true } } {
        # Check, if two numerical values are identical.
        #
        # expected   - Expected numeric value.
        # value      - Test numeric value.
        # msg        - Message for test case.
        # precision  - Precision of float comparison.
        # printCheck - Print message for successful test case.
        #
        # Returns true, if both numeric values are identical.
        # If $printCheck is set to true, a line prepended with `"Check:"` and the
        # message supplied in $msg is printed to standard output.
        # If the check fails, return false and print message prepended with `"Error:"`.
        #
        # See also: CheckBoolean CheckList CheckFloatList CheckMatrix CheckString

        set diff [expr { $expected - $value }]
        if { $diff < 0.0 } {
            set diff [expr { -1.0 * $diff }]
        }
        if { $diff >= [expr { pow( 10.0, -$precision) }] } {
            puts "Error: $msg (Expected: $expected Have: $value)"
            return false
        }
        if { $printCheck } {
            puts "Check: $msg (Expected: $expected Have: $value)"
        }
        return true
    }

    proc CheckList { expected value msg { printCheck true } } {
        # Check, if two lists are identical.
        #
        # expected   - Expected list.
        # value      - Test list.
        # msg        - Message for test case.
        # printCheck - Print message for successful test case.
        #
        # Returns true, if both lists are identical.
        # If $printCheck is set to true, a line prepended with `"Check:"` and the
        # message supplied in $msg is printed to standard output.
        # If the check fails, return false and print message prepended with `"Error:"`.
        #
        # See also: CheckMatrix CheckBoolean CheckNumber CheckString

        if { [llength $expected] != [llength $value] } {
            puts "Error: $msg (List length differ. Expected: [llength $expected] Have: [llength $value])"
            return false
        }
        set index 0
        foreach exp $expected val $value {
            if { $exp != $val } {
                puts "Error: $msg (Values differ at index $index. Expected: $exp Have: $val)"
                return false
            }
            incr index
        }
        if { $printCheck } {
            if { [llength $value] <= 4 } {
                puts "Check: $msg (Expected: $expected Have: $value)"
            } else {
                puts "Check: $msg (Lists are identical. List length: [llength $value])"
            }
        }
        return true
    }

    proc CheckFloatList { expected value msg precision { printCheck true } } {
        # Check, if two lists with floating point numbers are identical.
        #
        # expected   - Expected list.
        # value      - Test list.
        # msg        - Message for test case.
        # precision  - Precision of float comparison.
        # printCheck - Print message for successful test case.
        #
        # Returns true, if both lists are identical.
        # If $printCheck is set to true, a line prepended with `"Check:"` and the
        # message supplied in $msg is printed to standard output.
        # If the check fails, return false and print message prepended with `"Error:"`.
        #
        # See also: CheckMatrix CheckBoolean CheckNumber CheckString

        if { [llength $expected] != [llength $value] } {
            puts "Error: $msg (List length differ. Expected: [llength $expected] Have: [llength $value])"
            return false
        }
        set index 0
        foreach exp $expected val $value {
            if { ! [CheckFloat $exp $val $msg $precision false] } {
                puts "Error: $msg (Values differ at index $index. Expected: $exp Have: $val)"
                return false
            }
            incr index
        }
        if { $printCheck } {
            if { [llength $value] <= 4 } {
                puts "Check: $msg (Expected: $expected Have: $value)"
            } else {
                puts "Check: $msg (Lists are identical. List length: [llength $value])"
            }
        }
        return true
    }

    proc CheckMatrix { expected value msg { printCheck true } } {
        # Check, if two matrices are identical.
        #
        # expected   - Expected matrix.
        # value      - Test matrix.
        # msg        - Message for test case.
        # printCheck - Print message for successful test case.
        #
        # Returns true, if both matrices are identical.
        # If $printCheck is set to true, a line prepended with `"Check:"` and the
        # message supplied in $msg is printed to standard output.
        # If the check fails, return false and print message prepended with `"Error:"`. 
        #
        # See also: CheckList CheckFloatList CheckBoolean CheckNumber CheckString

        if { [llength $expected] != [llength $value] } {
            puts "Error: $msg (Matrix rows differ. Expected: [llength $expected] Have: [llength $value])"
            return false
        }
        set row 0
        foreach expRow $expected valRow $value {
            set col 0
            foreach exp $expRow val $valRow {
                if { $exp != $val } {
                    puts "Error: $msg (Values differ at row/col $row/$col. Expected: $exp Have: $val)"
                    return false
                }
                incr col
            }
            incr row
        }
        if { $printCheck } {
            puts "Check: $msg (Matrices are identical. Matrix rows: [llength $value])"
        }
        return true
    }

    proc CheckFloatMatrix { expected value msg precision { printCheck true } } {
        # Check, if two matrices are identical.
        #
        # expected   - Expected matrix.
        # value      - Test matrix.
        # msg        - Message for test case.
        # precision  - Precision of float comparison.
        # printCheck - Print message for successful test case.
        #
        # Returns true, if both matrices are identical.
        # If $printCheck is set to true, a line prepended with `"Check:"` and the
        # message supplied in $msg is printed to standard output.
        # If the check fails, return false and print message prepended with `"Error:"`. 
        #
        # See also: CheckList CheckFloatList CheckBoolean CheckNumber CheckString

        if { [llength $expected] != [llength $value] } {
            puts "Error: $msg (Matrix rows differ. Expected: [llength $expected] Have: [llength $value])"
            return false
        }
        set row 0
        foreach expRow $expected valRow $value {
            set col 0
            foreach exp $expRow val $valRow {
                if { ! [CheckFloat $exp $val $msg $precision false] } {
                    puts "Error: $msg (Values differ at row/col $row/$col. Expected: $exp Have: $val)"
                    return false
                }
                incr col
            }
            incr row
        }
        if { $printCheck } {
            puts "Check: $msg (Matrices are identical. Matrix rows: [llength $value])"
        }
        return true
    }

    proc CheckFile { fileName1 fileName2 msg { printCheck true } } {
        # Check, if two files are identical.
        #
        # fileName1  - First file name.
        # fileName2  - Second file name.
        # msg        - Message for test case.
        # printCheck - Print message for successful test case.
        #
        # Returns true, if both files are identical.
        # If "printCheck" is set to true, a line prepended with `"Check:"` and the
        # message supplied in "msg" is printed to standard output.
        # If the check fails, return false and print message prepended with `"Error:"`.
        #
        # See also: CheckList CheckFloatList CheckMatrix CheckBoolean CheckNumber

        if { ! [file isfile $fileName1] } {
            puts "Error: $msg (File not existent: \"$fileName1\")"
            return false
        }
        if { ! [file isfile $fileName2] } {
            puts "Error: $msg (File not existent: \"$fileName2\")"
            return false
        }

        if { [file size $fileName1] != [file size $fileName2] } {
            puts "Error: $msg (Expected size: [file size $fileName1] Have: [file size $fileName2])"
            return false
        } 

        set fp1 [open $fileName1 "r"]
        set fp2 [open $fileName2 "r"]

        fconfigure $fp1 -translation binary
        fconfigure $fp2 -translation binary

        set retVal  0
        set bufSize 2048

        set str1 [read $fp1 $bufSize]
        while { 1 } {
            set str2 [read $fp2 $bufSize]
            if { $str1 ne $str2 } {
                # Files differ
                set retVal 0
                break
            }
            set str1 [read $fp1 $bufSize]
            if { $str1 eq "" } {
                # Files are identical
                set retVal 1
                break
            }
        }
        close $fp1
        close $fp2

        if { $retVal == 0 } {
            puts "Error: $msg (Files differ: \"[file tail $fileName1]\" \"[file tail $fileName2]\")"
            return false
        } else {
            if { $printCheck } {
                puts "Check: $msg (Files are identical: \"[file tail $fileName1]\" \"[file tail $fileName2]\")"
            }
        }
        return true
    }

    proc ShowImgDict { imgDict msg args } {
        variable sTop

        set opts [dict create \
            -zoom  1 \
            -title $msg \
        ]

        foreach { key value } $args {
            if { [dict exists $opts $key] } {
                if { $value eq "" } {
                    error "ShowImgDict: No value specified for key \"$key\"."
                }
                dict set opts $key $value
            }
        }

        if { ! [info exists sTop] } {
            set sTop 0
            wm withdraw .
        } else {
            incr sTop
        }
        set top .ts_$sTop
        toplevel $top
        wm title $top [dict get $opts "-title"]

        set photo [pawt GetImageAsPhoto imgDict]
        set photoScaled [image create photo]
        $photoScaled copy $photo -zoom [dict get $opts "-zoom"]

        label $top.l0 -text "$msg ([image width $photo] x [image height $photo] pix)"
        grid  $top.l0 -row 0 -column 0 -padx 30
        label $top.l1 -image $photoScaled
        grid  $top.l1 -row 1 -column 0 -padx 30 -pady 10

        bind $top <Escape> exit
        wm protocol $top WM_DELETE_WINDOW exit
        focus $top
    }

    proc AddImgDicts { args } {
        variable sRow

        set opts [dict create \
            -window 1 \
            -zoom   1 \
            -title "AddImgDicts" \
        ]

        foreach { key value } $args {
            if { [dict exists $opts $key] } {
                if { $value eq "" } {
                    error "AddImgDicts: No value specified for key \"$key\"."
                }
                dict set opts $key $value
            } else {
                lappend imgDicts $key
                lappend msgs     $value
            }
        }

        set winNum [dict get $opts -window]
        set top .ta_$winNum
        if { ! [info exists sRow($winNum)] } {
            set sRow($winNum) 0
            wm withdraw .
            toplevel $top
            wm title $top [dict get $opts "-title"]
        }
        set col 0
        if { [info exists imgDicts] } {
            foreach imgDict $imgDicts msg $msgs {
                set photo [pawt GetImageAsPhoto imgDict]
                set photoScaled [image create photo]
                $photoScaled copy $photo -zoom [dict get $opts "-zoom"]

                set row1 $sRow($winNum)
                set row2 [expr $sRow($winNum) + 1]

                label $top.l_$row1-$col -text "$msg ([image width $photo] x [image height $photo] pix)"
                grid  $top.l_$row1-$col -row $row1 -column $col -padx 10

                label $top.l_$row2-$col -image $photoScaled
                grid  $top.l_$row2-$col -row $row2 -column $col -padx 10 -pady 10

                incr col
            }
        }
        incr sRow($winNum) 2

        bind $top <Escape> exit
        wm protocol $top WM_DELETE_WINDOW exit
        focus $top
    }

    proc AddImgPhotos { args } {
        variable sRow

        set opts [dict create \
            -window 1 \
            -zoom   1 \
            -title "AddImgPhotos" \
        ]

        foreach { key value } $args {
            if { [dict exists $opts $key] } {
                if { $value eq "" } {
                    error "AddImgPhotos: No value specified for key \"$key\"."
                }
                dict set opts $key $value
            } else {
                lappend imgPhotos $key
                lappend msgs      $value
            }
        }

        set winNum [dict get $opts -window]
        set top .tp_$winNum
        if { ! [info exists sRow($winNum)] } {
            set sRow($winNum) 0
            wm withdraw .
            toplevel $top
            wm title $top [dict get $opts "-title"]
        }
        set col 0
        if { [info exists imgPhotos] } {
            foreach photo $imgPhotos msg $msgs {
                set photoScaled [image create photo]
                set pixMsg ""
                if { $photo ne "" } {
                    $photoScaled copy $photo -zoom [dict get $opts "-zoom"]
                    set pixMsg "([image width $photo] x [image height $photo] pix)"
                }

                set row1 $sRow($winNum)
                set row2 [expr $sRow($winNum) + 1]

                label $top.l_$row1-$col -text "$msg $pixMsg"
                grid  $top.l_$row1-$col -row $row1 -column $col -padx 10

                label $top.l_$row2-$col -image $photoScaled
                grid  $top.l_$row2-$col -row $row2 -column $col -padx 10 -pady 10

                incr col
            }
        }
        incr sRow($winNum) 2

        bind $top <Escape> exit
        wm protocol $top WM_DELETE_WINDOW exit
        focus $top
    }
}
