#!/usr/bin/wish
package require tk 8.4.9
package require jpeg 0.2
proc make_image {filename  desiredSize {width 0} {height 0}} {
	if {$width == 0} {
		foreach {width height} [::jpeg::dimensions $filename] break
	}	
	if {$width > $height} {
		set dim $width
	} else {
		set dim $height
	}
	set scale 1
	while {$dim>$scale*$desiredSize} {
		incr scale
	}
	set f [open "|djpeg -fast -scale 1/$scale $filename"]
	fconfigure $f -translation binary
	set data [read $f]
	close $f
	return [image create photo -data $data]
}

proc get_data {filename var} {
	upvar $var data
	catch {array set data [jpeg::formatExif [::jpeg::getExif $filename]]}
	set data(Comment) [join [encoding convertfrom [::jpeg::getComments $filename]] "\n"]
	foreach {data(Width) data(Height)} [::jpeg::dimensions $filename] break
}

proc rotate {filename angle} {
	if {[lsearch {90 180 270} $angle]==-1} {
		return -code error "Invalid rotation angle - should be multiple of 90"
	}
	exec exiftran -[string range $angle 0 0] -i $filename 2>/dev/null
}	

proc set_comment {filename comment} {
	puts $filename
	::jpeg::replaceComment $filename [encoding convertto $comment]
}	

proc getname {index} {
	global filelist
	return [lindex $filelist $index]
}

proc clearExecBit {filename} {
	if {[file executable $filename]} {
		file attributes $filename -permissions -x
	}	

}

proc set_file {index} {
	global filecount filename exifdata filelist 
	if {[info exists filename]} {
		save_file_info
	}
	set buttonSize [expr [option get . imageSize ImageSize]/2]
	set filename [getname $index]
	clearExecBit $filename
	if {[regexp -nocase {img_(\d+)\.jpg} $filename => number]} {
		set audio snd_${number}.wav
		if {![file exists $audio]} {
			set audio [string toupper $audio]
			if {![file exists $audio]} {
				unset audio
		    }		
		}  
	}
	if {[info exists audio]} {
		puts "Found audiofile $audio"
		clearExecBit $audio
		.info.sound configure -state normal -command [list exec play $audio]
	} else {
		.info.sound configure -state disabled
	}	
	if {$index == 0} {
		.preview.prev configure -state disabled -image {}
	} else {
		.preview.prev configure -state normal\
		-image [make_image [lindex $filelist [expr $index-1]] $buttonSize]
	}	
	if {$index == $filecount-1} {
		.preview.next configure -state disabled -image {}
	} else {
		.preview.next configure -state normal\
			-image [make_image [lindex $filelist [expr $index+1]] $buttonSize]
	}	
	.info.filename configure -text "$filename ([expr $index+1]/$filecount)"
	array set exifdata [::jpeg::formatExif [::jpeg::getExif $filename]]
	if {[info exists exifdata(DateTime)]} {
	 set tm $exifdata(DateTime)
	} else {
		set tm "unknown"
	}	
	.info.exif configure -state normal
	.info.exif delete 0.0 end
	foreach {key value} [array get exifdata] {
		if {$key == "MakerNote"} break
		if {$key == "UserComment"} {
			set value [string trim $value "\0"]
		}	
	 	.info.exif insert end $key key "\t: $value\n" {}
	}	
	.info.datetime configure -text "Date: $tm"
	foreach {width height} [jpeg::dimensions $filename] break
	show_image $filename $width $height
	.info.comment delete 0.0 end
	.info.comment insert 0.0 [encoding convertfrom [join [jpeg::getComments $filename] "\n"]] 
	.info.comment edit reset
	.info.comment edit modified n
}	

proc delete_file {index} {
	global filename filelist filecount
	if {[tk_messageBox -message "Really delete file $filename" -type yesno -title\
			Confirm -icon warning] != "yes"} {
		return
	}	
		
	file delete $filename
	.info.comment edit reset
	.info.comment edit modified n
	
	if {$index == $filecount-1} {
		global current
		set filelist [lrange $filelist 0 [expr $index-1]]
		incr current -1
		set index $current
    } else {
		set filelist [lreplace $filelist $index $index]
	}
	incr filecount -1
	set_file $index
}	
	
proc rotateGUI {filename angle} {
	global exifdata
	rotate $filename $angle
	show_image $filename
}	

proc show_image {filename {width 0} {height 0}} {
	global exifdata
	if {$width == 0} {
		foreach {width height} [jpeg::dimensions $filename] break
	}	
	set img [make_image $filename [option get . imageSize ImageSize] $width $height] 
	.preview.l configure -image $img
	foreach img [image names] {
		if { ![string match ::tk::* $img]&& ![image inuse $img]} {
			image delete $img
		}
	}
	.info.size configure -text "Size: ${width}x$height"
}

proc save_file_info {} {
	global filename
	if {![.info.comment edit modified]} {
		return 
	}
	set_comment $filename [string trim [.info.comment get 0.0 end] "\n"]
}	

#
# Interface construction
# 
option add *Text.Font -rfx-courier-medium-r-normal--12-120-75-75-m-70-iso10646-1  
option add *Font -rfx-times-bold-r-normal--12-120-75-75-p-67-iso10646-1 widgetDefault
image create bitmap speaker -data {
#define speaker_width 24
#define speaker_height 24
static unsigned char speaker_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00,
  0x00, 0x24, 0x00, 0x00, 0x49, 0x00, 0x80, 0x51, 0x00, 0xc0, 0x91, 0x00,
  0xe7, 0xa5, 0x00, 0xf7, 0xa5, 0x00, 0xff, 0x29, 0x01, 0xff, 0x49, 0x01,
  0xff, 0x49, 0x01, 0xff, 0x29, 0x01, 0xf7, 0xa9, 0x00, 0xe7, 0xa5, 0x00,
  0xc0, 0x95, 0x00, 0x80, 0x51, 0x00, 0x00, 0x49, 0x00, 0x00, 0x24, 0x00,
  0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  };
}
frame .info
label .info.filename -width 32 -anchor w
button .info.rotateleft -text "90\u00b0" -command {rotateGUI [getname $current] 90} 
button .info.rotateright -text "270\u00b0" -command {rotateGUI [getname $current] 270} 
button .info.upsidedown -text "180\u00b0" -command {rotateGUI [getname $current] 180}
button .info.delete -text "Delete" -command {delete_file $current}
label .info.size -text "Size:" -anchor w
label .info.datetime -text "Date:" -anchor w
text .info.exif -height 20 -width 40 -state disabled -yscrollcommand ".info.yexif set" -tabs {3c}
text .info.comment -width 40 -height 5 -undo y -wrap word -yscrollcommand ".info.ycomment set"
scrollbar .info.yexif -orient vert -command ".info.exif yview"
scrollbar .info.ycomment -orient vert -command ".info.comment yview"
button .info.sound -state disabled -image speaker
frame .preview -width 200 -height 200
button .preview.next -text ">>" -command {set_file [incr current]}
button .preview.prev -text "<<" -command {set_file [incr current -1]} -state disabled

label .preview.l


grid .info.filename - - - - - -sticky news
grid .info.rotateleft .info.upsidedown .info.rotateright .info.sound .info.delete -
grid .info.size - - -  -sticky news
grid .info.datetime - - - -sticky news
grid .info.exif    - - - - .info.yexif -sticky news
grid .info.comment - - - - .info.ycomment -sticky news
grid rowconfigure .info 5 -weight 1
grid columnconfigure .info 4 -weight 1

grid .preview.prev .preview.next -sticky news
grid .preview.l - -sticky news
grid rowconfigure .preview 1 -weight 1
grid columnconfigure .preview 0 -weight 1
grid columnconfigure .preview 1 -weight 1
grid .info .preview -sticky news

wm protocol . WM_DELETE_WINDOW {save_file_info;destroy .}
focus .info.comment
bind .info.comment <Next> {.preview.next invoke}
bind .info.comment <Prior> {.preview.prev invoke}
bind .info.comment <Control-q> {eval [wm protocol . WM_DELETE_WINDOW]}
option add [winfo class .].imageSize 400 widgetDefault

if {[llength $argv]>1} {
	puts stderr "Usage $argv0 [image-file]"
}
set filelist [lsort -dictionary [concat [glob -nocomplain *.jpg] [glob -nocomplain *.JPG]]]

set filecount [llength $filelist]
if {!$filecount} {
	puts stderr "No image files in the current directory!"
	exit 1
}
if {[llength $argv]} {
	set current [lsearch $filelist [lindex $argv 0]]
	if {$current == -1} {
		puts stderr "File $argv not found in the current dir\n"
		exit 1
	}
} else {
	set current 0
}
set_file $current
