#
# Copyright (c) 1993-1995 Regents of the University of California.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#	This product includes software developed by the Network Research
#	Group at Lawrence Berkeley National Laboratory.
# 4. Neither the name of the University nor of the Laboratory may be used
#    to endorse or promote products derived from this software without
#    specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# @(#) $Header: /work/projects/tove/cvs/src/testing/vat/ui-main.tcl,v 1.1 1997/12/08 17:22:54 parnanen Exp $ (LBL)
#
#

proc net args {
	global V
	eval $V(data-net) $args
}

proc audio args {
	global audio
	eval $audio $args
}

proc controller args {
	global V
	eval $V(controller) $args
}

proc session args {
	global V
	eval $V(session) $args
}

set title "LBL Visual Audio Tool v[version]"

proc mk.meter { w c } {
	frame $w.$c -borderwidth 1 -relief flat -background black
	meter $w.$c.meter
	pack $w.$c.meter -expand 1 -fill y
}

proc mk.tests { w } {
	label $w.label -text "Audio Tests" -font [ctitlefont]
	frame $w.frame -borderwidth 2 -relief sunken
	frame $w.frame.p1
	frame $w.frame.p2

	set f [ctrlfont]
	set p $w.frame.p1
	radiobutton $p.none -text none -relief flat -command "audio test 0" \
		-anchor w -variable audioTest -font $f -value none
	$p.none select
	pack $p.none -fill x
	if ![audio halfduplex] {
		radiobutton $p.loop -text "loopback" -relief flat \
			-command "audio test 1" -value loopback \
			-anchor w -variable audioTest -font $f
		pack $p.loop -fill x
	}
	set p $w.frame.p2
	radiobutton $p.t6 -text "-6dBm tone" -relief flat -value t6 \
		-command "audio test 2" -anchor w -variable audioTest -font $f
	radiobutton $p.t0 -text "0dBm tone" -relief flat -value t0 \
		-command "audio test 3" -anchor w -variable audioTest -font $f
	radiobutton $p.tmax -text "max tone" -relief flat -value tmax \
		-command "audio test 4" -anchor w -variable audioTest -font $f
	pack $p.t6 $p.t0 $p.tmax -expand 1 -fill x

	pack $w.frame.p1 -side left -anchor center
	pack $w.frame.p2 -side left -expand 1 -fill both

	pack $w.label -fill x
	pack $w.frame -fill both -expand 1

	global audioTest
	set audioTest none
}

proc pri_accept { w pri } {
	global audioPri V
	if { $audioPri == 0 } {
		set V(priority) $pri
	}
	return 0
}

proc mk.pri { w } {
	label $w.label -text "Priority" -font [ctitlefont]
	frame $w.frame -borderwidth 2 -relief sunken
	set f [ctrlfont]
	set p $w.frame.inset
	frame $p -borderwidth 0
	pack $p -anchor c
	radiobutton $p.high -text "high (200)" -relief flat -value 200 \
		-variable audioPri -command "set V(priority) 200" -font $f
	radiobutton $p.med -text "med (100)" -relief flat -value 100 \
		-variable audioPri -command "set V(priority) 100" -font $f
	radiobutton $p.low -text "low (10)" -relief flat -value 10 \
		-variable audioPri -command "set V(priority) 10" -font $f
	frame $p.f
	radiobutton $p.f.rb -text "" -relief flat -value 0 \
		-command "set V(priority) \[$p.f.entry get\]" \
		-variable audioPri -font $f
	entry $p.f.entry -relief raised -borderwidth 1 -font $f -width 8 \
		-exportselection 0

	global entryTab
	set entryTab($p.f.entry:action) pri_accept

	pack $p.f.rb $p.f.entry -side left

	global V
	set pri [resource defaultPriority]
	set V(priority) $pri
	if { $pri == 10 } {
		$p.low select
	} elseif { $pri == 100 } {
		$p.med select
	} elseif { $pri == 200 } {
		$p.high select
	} else {
		$p.f.rb select
	}
	$p.f.entry insert 0 $pri
	set entryTab($p.f.entry:value) $pri

	pack $p.high $p.med $p.low $p.f -expand 1 -fill x
	pack $w.label $w.frame -expand 1 -fill x
}

proc mk.oradio { w } {
	set f [ctrlfont]
	set labels [audio output names]
	set i 0
	set n [llength $labels]
	while { $i < $n } {
		set p $w.p$i
		frame $p
		set port [lindex $labels $i]
		set label $port
		global omode$i
		if { $label == "speaker" } { set label "spkr" }
		label $p.label -text $label -font $f
		radiobutton $p.mmn -text "" -relief flat -value MikeMutesNet \
			-command "audio mode $port mikemutesnet" \
			-variable omode$i -font $f
		radiobutton $p.nmm -text "" -relief flat -value NetMutesMike \
			-command "audio mode $port netmutesmike" \
			-variable omode$i -font $f
		radiobutton $p.fd -text "" -relief flat -value FullDuplex \
			-command "audio mode $port fullduplex" \
			-variable omode$i -font $f
		pack $p.label $p.mmn $p.nmm $p.fd
		if { [yesno externalEchoCancel] } {
			radiobutton $p.ec -text "" -relief flat \
			-value EchoCancel \
			-command "audio mode $port echocancel" \
			-variable omode$i -font $f
			pack $p.ec
		}
		pack $p -side left
		set omode$i [option get . $port\Mode Vat]
		eval "audio mode $port \$omode$i"

		incr i
	}
	frame $w.label
	label $w.label.blank -text "" -font $f
	label $w.label.mmn -text "Mike mutes net" -font $f
	label $w.label.nmm -text "Net mutes mike" -font $f
	label $w.label.fd -text "Full duplex" -font $f
	pack $w.label.blank $w.label.mmn $w.label.nmm $w.label.fd -anchor w
	if { [yesno externalEchoCancel] } {
		label $w.label.ec -text "Ext. Echo Cancel" -font $f
		pack $w.label.ec -anchor w
	}
	pack $w.label -side left
}

#
# Initialize some global tcl variables.  Called
# after command line options have been parsed.
#
proc init_globals { } {
	foreach r { autoRaise keepSites sortSites muteNewSites mikeAGC \
		speakerAGC meterDisable lectureMode recvOnly } {
		global $r
		set $r [yesno $r]
	}
	foreach r { audioFormat iconPrefix silenceThresh } {
		global $r
		set $r [resource $r]
	}
}

proc install_meters {} { 
	global meterDisable spkr_meter mike_meter
	if $meterDisable {
		controller output-meter ""
		controller input-meter ""
		$spkr_meter set 0.
		$mike_meter set 0.
	} else {
		controller output-meter $spkr_meter
		controller input-meter $mike_meter
	}
}

proc set_silence_thresh {} {
	global silenceSuppressor silenceThresh
	if $silenceSuppressor {
		controller silence-thresh $silenceThresh
	} else {
		controller silence-thresh 0
	}
}

proc mk.obuttons { w } {

	set f [ctrlfont]
	frame $w.p0 -borderwidth 0
	frame $w.p1 -borderwidth 0
	pack $w.p0 $w.p1 -side left -fill x -anchor n

	set p $w.p0
	checkbutton $p.ar -text "Autoraise" -relief flat -font $f \
		-variable autoRaise

	global meterDisable
	checkbutton $p.dm -text "Disable Meters" -relief flat -font $f \
		-command install_meters -variable meterDisable

	checkbutton $p.nss -text "Suppress Silence" -relief flat -font $f \
		-command set_silence_thresh -variable silenceSuppressor
	global silenceSuppressor silenceSuppressorButton
	set silenceSuppressor 1
	set silenceSuppressorButton $p.nss

	pack $p.ar $p.dm $p.nss -expand 1 -fill x

	set p $w.p1
	global sitebox keepSites sortSites
	checkbutton $p.mns -text "Mute New Sites" -relief flat -font $f \
		-variable muteNewSites

	global keepSites
	checkbutton $p.kas -text "Keep All Sites" -relief flat -font $f \
		-command {srctab keep-sites $keepSites} -variable keepSites
	srctab keep-sites $keepSites
	srctab site-drop-time [resource siteDropTime]

	checkbutton $p.kss -text "Keep Sites Sorted" -relief flat -font $f \
		-command "$sitebox keep-sorted \$sortSites" -variable sortSites
	$sitebox keep-sorted $sortSites

	pack $p.mns $p.kas $p.kss -expand 1 -fill x
}

proc setAGC { w which level } {
	$w.label configure -text "$level dB"
	controller agc-$which $level
}

proc enableAGC { w which } {
	global doAGC
	if $doAGC($which) {
		controller agc-$which [$w.scale get]
		controller agc-$which-enable 1
		$w.scale configure -state normal
	} else {
		controller agc-$which-enable 0
		$w.scale configure -state disabled
	}
}

proc oneagc { w which label } {
	set f [ctrlfont]
	checkbutton $w.button -text $label -relief flat -font $f \
		-command "enableAGC $w $which" -variable doAGC($which)
	scale $w.scale -orient horizontal \
			-showvalue 0 \
			-from -10 -to 10 \
			-command "setAGC $w $which" \
			-relief groove -borderwidth 2 -width 10 \
			-state disabled 

	label  $w.label -text "0 dB" -width 5 -font $f
	pack $w.button $w.scale $w.label -side left
	pack $w.scale -expand 1 -fill x -pady 3

	global AGCbutton
	set AGCbutton($which) $w.button
}

proc mk.agc { w } {
	label $w.label -text "Automatic Gain Control" -font [ctitlefont]
	frame $w.frame -borderwidth 2 -relief sunken

	frame $w.frame.spkr -borderwidth 0
	frame $w.frame.mike -borderwidth 0

	oneagc $w.frame.spkr output Spkr
	$w.frame.spkr.scale set [option get . speakerAGCLevel Vat]
	oneagc $w.frame.mike input Mike
	$w.frame.mike.scale set [option get . mikeAGCLevel Vat]

	pack $w.frame.spkr $w.frame.mike -fill x
	pack $w.label $w.frame -expand 1 -fill x
	pack $w.frame -padx 6
}

proc set_ssthresh { w level } {
	global silenceThresh silenceSuppressor silenceSuppressorButton
	$w.label configure -text $level
	set silenceThresh $level
	set_silence_thresh
	if !$silenceSuppressor {
		$silenceSuppressorButton invoke
	}
}

proc mk.ssthresh w {
	set f [ctrlfont]

	global silenceThresh
	label $w.button -text "Silence Thresh: " -relief flat -font $f
	scale $w.scale -orient horizontal \
			-showvalue 0 \
			-from 10 -to 60 \
			-command "set_ssthresh $w" \
			-relief groove -borderwidth 2 -width 10
	$w.scale set $silenceThresh
	label  $w.label -text $silenceThresh -width 3 -font $f
	pack $w.button $w.scale $w.label -side left
	pack $w.scale -expand 1 -fill x -pady 3
}

proc mk.omode { w } {
	label $w.label -text "Output Mode" -font [ctitlefont]

	frame $w.frame -borderwidth 2 -relief sunken
	frame $w.frame.radios -borderwidth 0
	frame $w.frame.buttons -borderwidth 0
	if [audio halfduplex] {
		foreach i [audio output names] {
			audio mode $i mikemutesnet
		}
	} else {
		mk.oradio $w.frame.radios
	}
	mk.obuttons $w.frame.buttons
	frame $w.frame.ssthresh
	# mk.ssthresh $w.frame.ssthresh
	#pack $w.frame.radios $w.frame.buttons $w.frame.ssthresh \
	#	-anchor c -pady 4
	pack $w.frame.radios $w.frame.buttons \
		-anchor c -pady 4
	pack $w.label $w.frame -expand 1 -fill x
}

proc select_format { fmt blksPerPkt } {
	global V
	if [info exists V(encoder)] {
		delete $V(encoder)
	}
	set V(encoder) [new encoder $fmt]
	if { "$V(encoder)" == "" } {
		$V(controller) encoder ""
		return -1
	}
	$V(controller) encoder $V(encoder)
	$V(encoder) transmitter $V(session)
	$V(controller) blocks-per-packet $blksPerPkt
	$V(controller) encoder $V(encoder)
	return 0
}

proc set_lecture_mode v {
	foreach s [session active] {
		[$s handler] lecture-mode $v
	}
}

proc set_recvonly_mode v {
	global inputMutebutton
	if $v {
		global unmuted
		if $unmuted($inputMutebutton) {
			$inputMutebutton invoke
		}
		$inputMutebutton configure -state disabled
		bind all <ButtonPress-3> ""
		bind all <ButtonRelease-3> ""
	} else {
		$inputMutebutton configure -state normal
		bind all <ButtonPress-3> "ptt_press $inputMutebutton"
		bind all <ButtonRelease-3> "$inputMutebutton invoke"
	}
}

proc mk.me { w } {
	set f [ctrlfont]
	frame $w.mode -borderwidth 2 -relief sunken
	frame $w.mode.inset -borderwidth 0
	set p $w.mode.inset

	checkbutton $p.lec -text "Lecture" \
		-command { set_lecture_mode $lectureMode } \
		-variable lectureMode -font $f

	checkbutton $p.ro -text "RecvOnly" \
		-command { set_recvonly_mode $recvOnly } \
		-variable recvOnly -font $f

	pack $p.lec $p.ro -fill x
	pack $p -anchor c
	pack $p -side left -expand 1 -fill x

	frame $w.fmt -borderwidth 2 -relief sunken
	frame $w.fmt.p1
	set p $w.fmt.p1
	radiobutton $p.pcm -text PCM -font $f -value pcm \
		-command { select_format pcm 1 } -variable audioFormat
	radiobutton $p.pcm2 -text PCM2 -font $f -value pcm2 \
		-command { select_format pcm 2 } -variable audioFormat
	radiobutton $p.pcm4 -text PCM4 -font $f -value pcm4 \
		-command { select_format pcm 4 } -variable audioFormat
	pack $p.pcm $p.pcm2 $p.pcm4 -expand 1 -fill x

	frame $w.fmt.p2
	set p $w.fmt.p2
	radiobutton $p.dvi -text DVI -font $f -value dvi \
		-command { select_format dvi 1 } -variable audioFormat 
	radiobutton $p.dvi2 -text DVI2 -font $f -value dvi2 \
		-command { select_format dvi 2 } -variable audioFormat
	radiobutton $p.dvi4 -text DVI4 -font $f -value dvi4 \
		-command { select_format dvi 4 } -variable audioFormat
	pack $p.dvi $p.dvi2 $p.dvi4 -expand 1 -fill x

	frame $w.fmt.p3
	set p $w.fmt.p3
	radiobutton $p.gsm -text GSM -font $f -value gsm \
		-command { select_format gsm 4 } -variable audioFormat
	radiobutton $p.lpc4 -text LPC4 -font $f -value lpc4 \
		-command { select_format lpc 4 } -variable audioFormat
	pack $p.gsm $p.lpc4 -expand 1 -fill x
	pack $w.fmt.p1 $w.fmt.p2 $w.fmt.p3 -side left
	pack $w.mode -side left -expand 1 -fill both
	pack $w.fmt -side left
	global audioFormat
	set ttl [resource defaultTTL]
	if {$ttl > 160} {
		$w.fmt.p1.pcm configure -state disabled
		if {$audioFormat  == "pcm"} {
			set audioFormat pcm2
		}
		if {$ttl > 192} {
			$w.fmt.p1.pcm2 configure -state disabled
			$w.fmt.p1.pcm4 configure -state disabled
			if {[regexp -nocase pcm $audioFormat]} {
				set audioFormat dvi2
			}
			if {$ttl > 200} {
				$w.fmt.p2.dvi configure -state disabled
				$w.fmt.p2.dvi2 configure -state disabled
				$w.fmt.p2.dvi4 configure -state disabled
				if {[regexp -nocase dvi $audioFormat]} {
					set audioFormat gsm
				}
			}
		}
	}
	if { [select_format [string range $audioFormat 0 2] \
			[string range $audioFormat 3 4]] < 0 } {
		puts stderr "vat: unknown audio format: $audioFormat"
		exit 1
	}
}

proc mk.info { w } {
	global V
	set net $V(data-net)
	label $w.label -font [ctrlfont] -text \
	 "Dest: [$net addr]  Port: [$net port]  TTL: [$net ttl]"
	pack $w.label -expand 1 -fill x
}

# XXX probably belongs in ui-util.tcl
proc update_note { w s } {
	set s [string trim $s]
	[srctab local] sdes note $s
	return 0
}

proc mk.entries { w } {
	global sessionKey confName
	set sessionKey [option get . sessionKey Vat]

	frame $w.name
	label $w.name.label -text "Name: " -font [ctrlfont] -anchor e -width 6
	mk.entry $w.name updateName [resource rtpName]
	pack $w.name.label -side left
	pack $w.name.entry -side left -expand 1 -fill x -pady 2

	frame $w.msg
	label $w.msg.label -text "Note: " -font [ctrlfont] -anchor e -width 6
	mk.entry $w.msg update_note ""
	pack $w.msg.label -side left
	pack $w.msg.entry -side left -expand 1 -fill x -pady 2

	mk.key $w

	pack $w.name $w.msg $w.key -expand 1 -fill x

	frame $w.b
        button $w.b.stats -text "Global Stats" -borderwidth 2 \
                -anchor c -font [ctrlfont] -command create_global_window
	pack $w.b.stats -side left -padx 4 -pady 2 -anchor c

	pack $w.b -pady 2 -anchor c
}

proc mk.net { w } {
	label $w.label -text "Network" -font [ctitlefont]
	frame $w.frame -borderwidth 0
	frame $w.frame.me -borderwidth 0
	frame $w.frame.ie -borderwidth 2 -relief sunken
	frame $w.frame.ie.info -borderwidth 0
	frame $w.frame.ie.entries -borderwidth 0

	mk.me $w.frame.me
	mk.info $w.frame.ie.info
	mk.entries $w.frame.ie.entries

	pack $w.label $w.frame -expand 1 -fill x
	pack $w.frame -padx 6

	pack $w.frame.ie.info $w.frame.ie.entries -expand 1 -fill x
	pack $w.frame.me $w.frame.ie -expand 1 -fill x
}

proc build.menu { } {
	set w .menu
	toplevel $w
	bind $w <Enter> "focus $w"
	wm withdraw $w

	frame $w.tp
	frame $w.tp.tests
	frame $w.tp.pri
	frame $w.omode
	#XXXframe $w.agc
	frame $w.net

	mk.tests $w.tp.tests
	mk.pri $w.tp.pri
	mk.omode $w.omode
	#XXX NOT YET mk.agc $w.agc
	mk.net $w.net
	button $w.ok -text " Dismiss " -borderwidth 2 -relief raised \
		-command "toggle_window $w" -font [ctitlefont]
	frame $w.pad -borderwidth 0 -height 6

	pack $w.tp.tests -side left -expand 1 -fill both -padx 2
	pack $w.tp.pri -side left -expand 1 -fill x -padx 2
	#XXX pack $w.tp $w.omode $w.agc $w.net -expand 1 -fill x
	pack $w.tp $w.omode $w.net -expand 1 -fill x
	pack $w.ok -pady 6 -anchor c
	pack $w.tp -padx 4
	pack $w.omode -padx 6
}

proc mute_invoke { w which } {
	global unmuted
	if $unmuted($w) {
		audio $which unmute
		if ![audio have] {
			audio_demand
		}
	} else {
		audio $which mute
	}
}

proc lookup_bitmap { name } {
	switch -glob $name {
		mike { return mike }
		speaker { return speaker }
		jack { return headphone }
		lineout2 { return lineout2 }
		lineout3 { return lineout3 }
		lineout* { return lineout }
		linein2 { return linein2 }
		linein3 { return linein3 }
		default { return linein }
	}
}

proc setPort { which button scale port } {
	audio $which set $port
	$button configure -bitmap [lookup_bitmap $port]
	global gaintab porttab
	set porttab($which) $port
	$scale set $gaintab($port)
	# XXX the audio class remembers the gain levels but
	# that doesn't work the first time we select a port
	audio $which gain $gaintab($port)
}

proc changePort { which button scale } {
	set ports [audio $which names]
	set n [audio $which current]
	incr n
	if { $n >= [llength $ports] } {
		set n 0
	}
	setPort $which $button $scale [lindex $ports $n]
}

proc setgain { which level } {
	audio $which gain $level
	global gaintab porttab
	set gaintab($porttab($which)) $level
}

proc mk.pane { w which bitmap label } {
	set f [option get . audioFont Vat]
	frame $w.mute -borderwidth 1 -relief raised
	checkbutton $w.mute.b -text $label -font $f -relief ridge \
		-anchor c \
		-command "mute_invoke $w.mute.b $which" \
		-variable unmuted($w.mute.b) \
		-borderwidth 2 \
		-highlightthickness 0
	global unmuted
	set unmuted($w.mute.b) 1

	global $which\Mutebutton
	set $which\Mutebutton $w.mute.b

	pack $w.mute.b -expand 1 -fill x

	frame $w.select -borderwidth 2 -relief raised
	button $w.select.b -bitmap $bitmap -relief flat -borderwidth 2 \
		-command "changePort $which $w.select.b $w.frame.scale" \
		-height 24 -highlightthickness 1
	pack $w.select.b -expand 1 -fill x
	#
	# Disable button if there aren't any choices
	#
	if { [llength [audio $which names]] <= 1 } {
		$w.select.b configure -state disabled
	}

	frame $w.frame -borderwidth 2 -relief raised
	meter $w.frame.meter
	scale $w.frame.scale -orient vertical \
			-showvalue 0 \
			-from 256 -to 0 \
			-command "setgain $which" \
			-relief groove -borderwidth 2 -length 200 \
			-highlightthickness 0
	global $which\Scale $which\PortButton
	set $which\Scale $w.frame.scale
	set $which\PortButton $w.select.b

	pack $w.frame.meter $w.frame.scale -side left -expand 1 -fill y

	pack $w.mute $w.select -fill x
	pack $w.frame -expand 1 -fill y
}

proc invoke_keep_audio {} {
	global audioHeld
	if { $audioHeld && ![audio have] } {
		audio_demand
	}	
}

proc mk.ab { top } {
	set w $top.audio
	frame $w
	pack $w -expand 1 -fill y

	frame $w.spkr -borderwidth 0
	mk.pane $w.spkr output speaker listen
	frame $w.mike -borderwidth 0
	mk.pane $w.mike input mike talk
	pack $w.spkr $w.mike -side left -expand 1 -fill y
	global spkr_meter mike_meter
	set spkr_meter $w.spkr.frame.meter
	set mike_meter $w.mike.frame.meter

	set f [ctrlfont]
	checkbutton $top.button -text "Keep Audio" -font $f \
		-command invoke_keep_audio \
		-variable audioHeld -anchor c \
		-relief ridge -borderwidth 2 -highlightthickness 0
	global keepAudioButton
	set keepAudioButton $top.button

	pack $top.button -fill x -side top -anchor c
}

proc ptt_press { button } {
	global unmuted
	if !$unmuted($button) {
		$button invoke
	}
}

proc toggle_mute src {
	set v [expr ![$src mute]]
	$src mute $v
	global src_name
	$src_name($src) mute $v
}

#XXX update this comment
# a sitebox responds to the following commands:
#  sites			- returns current no. of sites
#  purge			- purges inactive sites
#  sort				- sorts sitebox
#  list				- lists sites on stdout
#  name localname		- sets local site name to 'localname'
#  keepsites 0|1		- set/reset 'keep all sites' option
#  mutesites 0|1		- set/reset 'mute new sites' option
#  keepsorted 0|1		- set/reset 'keep sites sorted' option
#  keepcompact 0|1		- set/reset 'keep sitebox comapacted' option
#  withID id			- return index of site with id <id>
#  IDtoName id			- return name of site with id <id>
#  which x y			- return number of site at location (x,y)
#				  (-1 if none)
#  where x y			- return where in site is (x,y) (0 = name,
#				  1 = mute checkbox).
#  site n info			- return site n name, id & packet src id
#  site n name			- return site name
#  site n activity		- return time of last activity
#  site n lastactive		- return number of milliseconds since
#				  site n was last active (-1 if never
#				  active).
#  site n muted			- return 1 if site n muted, 0 otherwise
#  site n toggle		- toggle site n mute state
#  site n mute 0|1		- set/reset site n mute state

proc sb_leftclick {sitebox x y m} {
	set s [$sitebox which $x $y]
	if {"$s" != ""} {
		if [$sitebox over-button $x $y] {
			#
			# toggle the mute button
			# update both the UI and the RTP object
			#
			toggle_mute $s
		} else {
			create_info_menu $s [winfo pointerx $sitebox] \
				[winfo pointery $sitebox]
		}
	}
}

proc is_mixer src {
	return [expr [$src srcid] != [$src ssrc]]
}

proc delete_source { w x y } {
	set src [$w which $x $y]
	if { "$src" != "" && $src != [srctab local] } {
		srctab delete $src
	}
}

proc sb_midclick {w x y m} {
	set src [$w which $x $y]
	if {"$src" != ""} {
		if [is_mixer $src] {
			set s [getid $src]
			open_dialog \
			  "can't do side conversation with $s thru mixer"
		} else {
			set fmt [rtp_format $src]
			if { $fmt == "" } {
				set fmt pcm2
			}
			global V
			set csig [$src addr]/[net port]
			if {$V(sessionType) == "vat"} {
				set st -n
				set csig $csig/[resource confid]
			} else {
				set st -r
			}
			exec vat $st -C [getid $src] $csig/$fmt &
		}
	}
}

proc audio_psetup {} {
	set s [audio input gain]
	puts "Vat.[lindex $s 0]Gain:	[lindex $s 1]"
	set s [audio output gain]
	puts "Vat.[lindex $s 0]Gain:	[lindex $s 1]"
	set s [controller agc-input]
	if [lindex $s 0] {
		puts "Vat.mikeAGCLevel:	[lindex $s 1]"
	}
	set s [controller agc-output]
	if [lindex $s 0] {
		puts "Vat.speakerAGCLevel:	[lindex $s 1]"
	}
}

proc build.bar w {
	global title
	label $w.title -text $title -font [ctrlfont] \
		-relief flat -justify left -width [string length $title]
	button $w.quit -text Quit -relief raised \
		-font [ctrlfont] -command adios \
		-highlightthickness 1
	button $w.menu -text Menu -relief raised \
		-font [ctrlfont] -highlightthickness 1 \
		-command "toggle_window .menu"
	button $w.help -text Help -relief raised \
		-font [ctrlfont] -highlightthickness 1 \
		-command "toggle_window .help"

	pack $w.title -side left -fill both -expand 1
	pack $w.menu $w.help $w.quit -side left -pady 1 -padx 1

	global title_bar
	set title_bar $w.title
}

#
# Build the user-interface.
#
proc init_gui { } {
	global audio

	set dev [resource defaultDevice]
	if { "$dev" != "" } {
		set audio [new audio $dev]
		if { $audio == "" } {
			puts stderr "vat: no such audio device: $dev"
			exit 1
		}
	} else {
		set devices "sun sgi voxware hp pc ibm af"
		if [yesno useAF] {
			set devices "af $devices"
		}
		foreach d $devices {
			set audio [new audio $d]
			if { "$audio" != "" } {
				break
			}
		}
	}
	# try to open the device
	$audio obtain
	after 5000 audio_timeout

	init_globals

	#
	# emulate implicit keyboard focus
	#
	bind . <Enter> { focus %W }

	#wm focusmodel . active
	bind . q { adios }
	bind . <Control-c> { adios }
	bind . <Control-d> { adios }

	bind . p audio_psetup
	bind . P audio_psetup

	frame .m
	frame .m.left
	frame .m.right
	frame .m.left.sites -relief raised -borderwidth 2

	global sitebox
	set sitebox .m.left.sites.sb
	new sitebox $sitebox

	pack .m.left.sites -expand 1 -fill both
	pack .m.left.sites.sb -expand 1 -fill both

	set a .m.right
	frame $a.ab
	mk.ab $a.ab

	bind . c purge_sources
	bind . C purge_sources
	bind . l list_sources
	bind . L list_sources

	#
	# Setup sitebox bindings
	#	
	bind . o "$sitebox sort"
	bind . O "$sitebox sort"
	bind $sitebox <1> "sb_leftclick %W %x %y 0"
	bind $sitebox <Shift-1> "sb_leftclick %W %x %y 1"
	bind $sitebox <2> "sb_midclick %W %x %y 0"
	bind $sitebox d "delete_source %W %x %y"

	global recvOnly
	if !$recvOnly {
		global inputMutebutton
		bind all <ButtonPress-3> "ptt_press $inputMutebutton"
		bind all <ButtonRelease-3> "$inputMutebutton invoke"
	}

	pack $a.ab -expand 1 -fill both

	pack .m.left -side left -expand 1 -fill both
	pack .m.right -side left -fill y

	pack .m -expand 1 -fill both

	frame .bar -relief ridge -borderwidth 2
	build.bar .bar
	pack .bar -fill x

	set v [resource geometry]
	if { $v != "" } { 
		if { [ catch "wm geometry . $v" ] } {
			puts "vat: bad geometry $v"
			adios
		}
	}
	wm withdraw .
	update idletasks
	global minwidth minheight iconPrefix
	set minwidth [winfo reqwidth .]
	set minheight [winfo reqheight .]
	wm minsize . $minwidth $minheight

	#XXX 
	global mike_meter spkr_meter V
	if [audio halfduplex] {
		set duplex half-duplex
	} else {
		set duplex full-duplex
	}

	set V(controller) [new controller $duplex]
	install_meters
	$V(controller) audio $audio

	update idletasks
	wm deiconify .
	#
	# postpone until all the sub-windows are mapped
	#
	bind . <Map> { mark_icon "" }

	rank_init
}

#
# Do any initialization that needs to wait for Controller
# object to be built.  Happens after mktk called, just
# before Tk_MainLoop is called.
#
proc init_tk { } {
	build.menu

	audio_indicator_update
	global keepAudioButton
	if [yesno keepAudio] {
		$keepAudioButton invoke
	}

	global outputMutebutton inputMutebutton \
		inputAGCbutton outputAGCbutton recvOnly
	if $recvOnly {
		# mute and then disable
		$inputMutebutton invoke
		$inputMutebutton configure -state disabled
	} elseif [yesno mikeMute] {
		$inputMutebutton invoke
	}
	if [yesno speakerMute] {
		$outputMutebutton invoke
	}
	if [yesno mikeAGC] {
		#XXX NOT YET $AGCbutton(input) invoke
	}
	if [yesno speakerAGC] {
		#XXX NOT YET $AGCbutton(output) invoke
	}
	global gaintab porttab inputScale outputScale 
	global inputPortButton outputPortButton
	set names [audio input names]
	foreach port $names {
		set attr [string tolower $port]
		set gaintab($port) [option get . $attr\Gain Vat]
	}
	if { [llength $names] <= 1 } {
		$inputPortButton configure -state disabled \
			-disabledforeground [option get . foreground Button]
	}
	set pname [audio input name [option get . inputPort Vat]]
	if {[info exists gaintab($pname)] == 0} {
		set pname [lindex $names 0]
	}
	setPort input $inputPortButton $inputScale $pname

	set names [audio output names]
	foreach port $names {
		set attr [string tolower $port]
		set gaintab($port) [option get . $attr\Gain Vat]
		audio mode $port [option get . $attr\Mode Vat]
	}
	if { [llength $names] <= 1 } {
		$outputPortButton configure -state disabled \
			-disabledforeground [option get . foreground Button]
	}
	set pname [audio output name [option get . outputPort Vat]]
	if {[info exists gaintab($pname)] == 0} {
		set pname [lindex $names 0]
	}
	setPort output $outputPortButton $outputScale $pname
}

proc ctrlfont { } {
	return [option get . ctrlFont Vat]
}

proc ctitlefont { } {
	return [option get . ctrlTitleFont Vat]
}

proc build.help { } {
	create_help_window .help {

"Before transmitting audio, adjust the mike \
level so that the output meter peaks around 80% of full scale.  Below this\
you are hard to hear and above this your signal is distorted."

"To talk, temporarily unmute the mike by depressing\
the right mouse button anywhere in the vat window.  The mike is\
live only while the button is depressed.  For hands-free operation,\
you can leave the mike active by selecting the ``talk'' button\
above the mike icon. \
If the ``talk'' button is grayed-out, the ``recvOnly'' option is\
probably selected on the ``Menu'' panel."

"Mute individual sites by clicking on checkbox next to name."

"If your computer supports multiple audio input or output ports,
you can select which you want by clicking on mike or speaker icon."

"Prevent other vats from taking the audio device\
by clicking on the ``Keep Audio'' button.  Different vats will\
cooperate so that only one instance ever has ``Keep Audio'' selected. \
The vat label (at the bottom of the window) is italicized when\
this vat does not have control of the audio."

"Get info about a site by\
clicking (and holding) left mouse button over the site name. \
A popup menu lets you select a site description window, RTP and\
decoder statistics windows (various reception statistics for data\
coming from the site), and the `mtrace' (multicast traceroute)\
diagnostic run from the site to you or from you to the site."

"In a statistics window (the window you get by selecting either RTP\
or Decoder stats in the site popup menu), clicking the left button\
on a stat name will bring up a stripchart plotting that stat. \
The stat value is plotted every second. \
The horizontal axis has a tickmark (a vertical white\
line plotted *under* the data) every 30 seconds. \
A legend at the bottom of the window gives the vertical axis scale."

"If the user interface looks peculiar, you might\
have X resources that conflict with tk.  A common problem is\
defining ``*background'' and/or ``*foreground''."

"Bugs and suggestions to vat@ee.lbl.gov.  Thanks."
	}
}

proc register src {
	#
	# if we display everyone (i.e., non-mixers and mixers),
	# then just create the name in the sitebox.  otherwise,
	# it gets deferred until we receive an SDES packet
	# (which never happens for a mixer)
	#
	if [yesno displayMixers] {
		create_src_name $src
	}
}

proc create_src_name src {
	global sitebox src_name muteNewSites
	set src_name($src) [$sitebox create]
	$src_name($src) text [getid $src]
	$src_name($src) tag $src
	if $muteNewSites {
		toggle_mute $src
	}
}

proc unregister src {
	destroy_rtp_stats $src
	global sitebox src_name
	if [info exists src_name($src)] {
		rank_clear $src
		$sitebox remove $src_name($src)
		unset src_name($src)
	}
}

proc deactivate src {
	destroy_decoder_stats $src
	delete [$src handler]
	$src handler ""
}

proc activate src {
	set decoder [new decoder [rtp_format $src]]
	if { $decoder == "" } {
		# don't support this format
		set decoder [new decoder null]
	}
	global V lectureMode
	$decoder lecture-mode $lectureMode
	$src handler $decoder
	$decoder controller $V(controller)
}

proc dummy args ""

proc rank_clear src {
	global rank
	if { $rank(2) == "$src" } {
		set rank(2) dummy
	}
	if { $rank(1) == "$src" } {
		set rank(1) $rank(2)
		set rank(2) dummy
	}
	if { $rank(0) == "$src" } {
		set rank(0) $rank(1)
		set rank(1) $rank(2)
		set rank(2) dummy
	}
}

proc rank_touch src {
	global src_name rank
	set r [$src_name($src) rank]
	if { $r == 1 } {
		set rank(1) $rank(0)
		set rank(0) $src
		$src_name($rank(1)) rank 1
	} elseif { $r != 0 } {
		$src_name($rank(2)) rank 3
		set rank(2) $rank(1)
		set rank(1) $rank(0)
		set rank(0) $src
		$src_name($rank(2)) rank 2
		$src_name($rank(1)) rank 1
	}
	$src_name($rank(0)) rank 0
}

proc rank_init {} {
	global rank src_name
	set rank(0) dummy
	set rank(1) dummy
	set rank(2) dummy
	set src_name(dummy) dummy
}

proc set_busy src {
	global src_name
	if ![info exists src_name($src)] {
		create_src_name $src
	}
	$src_name($src) highlight 1
	rank_touch $src

	if { ![$src mute] && ![winfo ismapped .] } {
		mark_icon [resource iconMark]
	}
	after 500 "busy_check $src"

	global V unmuted outputMutebutton
	if [info exists V(cb)] {
		# XXX this preliminary protocol will change
		set cname [$src sdes cname]
		if { "$cname" != "" } {
			$V(cb) send "focus $cname"
		}
	}
	#
	# if we don't have the audio, request.
	# (but don't demand it since this happens on
	#  network input not user intervention)
	#
	if { ![audio have] && $unmuted($outputMutebutton) } {
		audio_request
	}
}

proc busy_check src {
	global src_name
	if [info exists src_name($src)] {
		set delta [expr [controller ntp-time] - [$src last-data]]
		#XXX delta is in ntp time units.  (65536 Hz)
		if { $delta > 20000 } {
			$src_name($src) highlight 0
			$src clear-busy
		} else {
			global unmuted outputMutebutton
			if { ![audio have] && $unmuted($outputMutebutton) } {
				audio_request
			}
			after 500 "busy_check $src"
		}
	}
}

proc update_source_info src {
	global src_info src_nickname src_name
	if ![info exists src_name($src)] {
		create_src_name $src
	}
	#
	# Figure out best presentation from available information.
	#
	set name [$src sdes name]
	set cname [$src sdes cname]
	set addr [$src addr]
	if { $name == "" } {
		if { $cname == "" } {
			set src_nickname($src) $addr
			set info $addr/[rtp_format $src]

		} else {
			set src_nickname($src) $cname
			set info "$addr/[rtp_format $src]"
		}
	} elseif [cname_redundant $name $cname] {
		set src_nickname($src) $name
		set info $addr/[rtp_format $src]
	} else {
		set src_nickname($src) $name
		set info $cname/[rtp_format $src]
	}
		set src_info($src) $cname/[rtp_format $src]

	set msg [$src sdes text]
	if { $msg != "" } {
		set info $msg
	}
	set src_info($src) $info
	
	#
	# only call change_name when name really changes
	# all this does is change the name in the on-screen site display
	#
	if { [$src_name($src) text] != $src_nickname($src) } {
		$src_name($src) text $src_nickname($src)
	}
}

proc grayout src {
	global src_name
	if [info exists src_name($src)] {
		$src_name($src) disable 1
	}
}

proc embolden src {
	global src_name
	if [info exists src_name($src)] {
		$src_name($src) disable 0
	}
}

proc change_format src {
	deactivate $src
	activate $src
}

#
# what we want printed in the info window for our format etc.
# (i.e., info window code is app independent but this info isn't,
#  so we have this hackish callback)
#
proc info_text src {
	set d [$src handler]
	set fmt [rtp_format $src]
	if { "$d" != "" } {
		set n [expr [$d block-size] / 160]
		if { $n > 1 } {
			set fmt $fmt/$n
		}
	}
	if { $fmt == "" } {
		set fmt none
	}
	return "format: $fmt"
}

proc purge_sources {} {
	srctab gen-init
	while { 1 } {
		set src [srctab gen-next]
		if { $src == "" } {
			return
		}
		if [$src lost] {
			srctab delete $src
		}
	}
}

proc list_sources {} {
	srctab gen-init
	while { 1 } {
		set src [srctab gen-next]
		if { $src == "" } {
			return
		}
		if [$src lost] {
			set lost "*"		    
		} else {
			set lost ""
		}
		set fmt "[getid $src] \[[$src addr]/[$src srcid]"
		if [is_mixer $src] {
			set fmt "$fmt via [$src ssrc]"
		}
		set fmt "$fmt\]"
		puts $lost$fmt
	}
}
