#******************************************************************************
#
# Copyright:   2018-2022 Paul Obermeier (obermeier@poSoft.de)
#
#              See the file "Mawt_License.txt" for information on
#              usage and redistribution of this file, and for a
#              DISCLAIMER OF ALL WARRANTIES.
#
# Description: Tcl module with miscellaneous utility
#              procedures related to the FFMPEG module.
#
#******************************************************************************

namespace eval mawt {
    variable ns [namespace current]

    namespace ensemble create

    namespace export VideoExport
    namespace export VideoImport

    proc VideoExport { movieFile imgFilePattern args } {
        # Export video frames into a series of image files.
        #
        # movieFile      - Movie file name.
        # imgFilePattern - Pattern for image output files.
        #                  Must contain a printf like integer format specifier.
        # args           - Options described below.
        #
        # -start <int>     - Start frame. Default: First video frame.
        # -end <int>       - End frame. Default: Last video frame.
        # -format <string> - Format string for "photo write" command. Default: PNG.
        #                    Example: -format "JPEG -quality 75".
        # -verbose <bool>  - Print verbose messages. Default: false.
        #
        # See also: VideoImport

        set startFrame -1
        set endFrame   -1
        set verbose     0
        set format     "PNG"

        foreach { key value } $args {
            switch -exact $key {
                "-start" {
                    set startFrame [expr {int($value)}]
                }
                "-end" {
                    set endFrame [expr {int($value)}]
                }
                "-format" {
                    set format $value
                }
                "-verbose" {
                    set verbose [expr {bool($value)}]
                }
            }
        }

        set videoObj [mawt Video new $movieFile "r"]
        set numBytes [$videoObj Start]
        set tx [$videoObj GetWidth]
        set ty [$videoObj GetHeight]
        set vectorObj [mawt Vector new $numBytes]
        image create photo VideoFrame -width $tx -height $ty
        set numFrames [$videoObj GetNumFrames]    
        if { $startFrame < 0 } {
            set startFrame 0
        }
        if { $endFrame < 0 || $endFrame > $numFrames } {
            set endFrame [expr {$numFrames - 1}]
        }

        if { $verbose } {
            puts "Input file  : $movieFile"
            puts "Output files: $imgFilePattern"
            puts "Start       : $startFrame"
            puts "End         : $endFrame"
            puts "Image format: $format"
        }
        set numExportedImgs 0
        for { set i $startFrame } { $i <= $endFrame } { incr i } {
            $videoObj Lock
            $videoObj GetNextImage [$vectorObj Get] 0
            $videoObj Unlock
            $vectorObj ToPhoto VideoFrame $tx $ty
            set outFileName [format $imgFilePattern $i]
            set retVal [catch { VideoFrame write $outFileName -format $format } errMsg]
            if { $retVal != 0 } {
                puts "Error saving image:\n$errMsg"
                return false
            }
            incr numExportedImgs
        }
        $vectorObj destroy
        $videoObj destroy
        return $numExportedImgs
    }

proc VideoImport { imgFilePattern movieFile args } {
        # Generate a video by importing a series of image files.
        #
        # imgFilePattern - Pattern for image input files or a list of image input files.
        #                  If file pattern, the file names must contain a printf like
        #                  integer format specifier.
        # movieFile      - Movie output file name.
        # args           - Options described below.
        #
        # -start <int>     - Start frame. Default: First image file.
        # -end <int>       - End frame. Default: Last image file.
        # -format <string> - Format string for "image create photo" command. Default: None.
        #                    Example: -format "raw -map minmax".
        # -fps <int>       - Frames per second. Default: 25.
        # -bitrate <float> - Bitrate in MBit per second. Default: 4.0.
        # -flip <bool>     - Flip images. Default: false.
        # -verbose <int>   - Print verbose messages. Default: No.
        #                    No: 0. Once: 1. Per image: 2.
        #
        # Note, that options `-start` and `-end` refer either to the number in the file pattern or
        # the range of the file list. In the latter case note that the first list index is zero.
        #
        # See also: VideoExport

        set startFrame -1
        set endFrame   -1
        set flip        0
        set verbose     0
        set format      ""

        foreach { key value } $args {
            switch -exact $key {
                "-start" {
                    set startFrame [expr {int($value)}]
                }
                "-end" {
                    set endFrame [expr {int($value)}]
                }
                "-format" {
                    set format $value
                }
                "-flip" {
                    set flip [expr {bool($value)}]
                }
                "-verbose" {
                    set verbose [expr {int($value)}]
                }
            }
        }

        if { $startFrame < 0 } {
            set startFrame 0
        }
        if { [llength $imgFilePattern] > 1 } {
            if { $endFrame < 0 } {
                set endFrame [expr {[llength $imgFilePattern] - 1}]
            }
            set imgFileList [lrange $imgFilePattern $startFrame $endFrame]
        } else {
            set inFileName [format $imgFilePattern $startFrame]
            if { ! [file exists $inFileName] } {
                error "Image file $inFileName does not exist."
            }
            if { $endFrame < 0 } {
                for { set i $startFrame } { true } { incr i } {
                    set inFileName [format $imgFilePattern $i]
                    if { ! [file exists $inFileName] } {
                        set endFrame [expr {$i -1}]
                        break
                    }
                    lappend imgFileList $inFileName
                }
            } else {
                for { set i $startFrame } { $i <= $endFrame } { incr i } {
                    set inFileName [format $imgFilePattern $i]
                    if { ! [file exists $inFileName] } {
                        error "Image file $inFileName does not exist."
                    }
                    lappend imgFileList $inFileName
                }
            }
        }

        if { $verbose >= 1 } {
            if { [llength $imgFilePattern] > 1 } {
                puts "Input files : List of [llength $imgFilePattern] files"
            } else {
                puts "Input files : $imgFilePattern"
            }
            puts "Start       : $startFrame"
            puts "End         : $endFrame"
            puts "Image format: $format"
            puts "Output file : $movieFile"
        }
        set numImportedImgs 0
        foreach inFileName $imgFileList {
            if { $verbose >= 2 } {
                puts [format "  %4d of %4d: Reading image $inFileName ..." \
                     [expr $numImportedImgs + 1] [expr {$endFrame - $startFrame + 1}]]
            }
            if { $format eq "" } {
                set phImg [image create photo -file $inFileName]
            } else {
                set phImg [image create photo -file $inFileName -format "$format"]
            }
            set w [image width  $phImg]
            set h [image height $phImg]
            if { ! [info exists videoObj] } {
                set videoObj [mawt Video new $movieFile "w" -width $w -height $h {*}$args]
                set vectorObj [mawt Vector new [expr {$w * $h * 3}]]
            }
            $vectorObj FromPhoto $phImg 3 1 0
            image delete $phImg
            $videoObj WriteNextImage [$vectorObj Get] $w $h $flip
            incr numImportedImgs
        }
        if { $numImportedImgs > 0 } {
            $videoObj Close
            $vectorObj destroy
            $videoObj destroy
        }
        if { $verbose >= 2 } {
            puts "Finishing video $movieFile"
        }
        return $numImportedImgs
    }
}
