#! /usr/bin/env /usr/bin/wish9.0
#
# Copyright (c) 2006,2011 CNRS-LAAS
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
#----------------------------------------------------------------------
##
## Log collector and viewer for alog system
##
package require BWidget

##
## Global variables
##
set version "0.5"
set port 3412
set logId 0
set textMode 0
set TS0 [clock milliseconds]

#----------------------------------------------------------------------
##
## Interface setup
##
proc mainWindow { {myport 3412} } {
    global mf version port

    set port $myport
    #
    # Main menu
    #
    set mainMenu {
	"&File" {} {} 0 {
	    {command "&Save as..." {} "Save logs" {Ctrl s} -command doSave}
	    {command "&Reset" {} "Reset logs" {Ctrl r} -command resetLog}
	    {command "E&xit" {} "Exit alogview" {Ctrl q} -command exit}
	}
	"&View" {} viewMenu 1 {
	    {checkbutton "&Timestamps" {} "Show time stamps" {}
		-variable showTime -command viewTimeStamps}
	    {separator {} {} 0 }
	}

	"&Help" {} {} 0 {
	    {command "&About" {} "About alogview" {} -command aboutBox}
	}
    }

    MainFrame .main -menu $mainMenu -textvariable status -progressvar progress

    set mf [.main getframe]

    .main addindicator -text "© CNRS-LAAS 2006  "

    frame $mf.search -relief raised
    button $mf.search.button -text "search" -command doSearch
    pack $mf.search.button -side right
    entry $mf.search.text -width 20
    pack $mf.search.text -side right
    pack $mf.search -fill x -expand no

    text $mf.text -width 80 -height 35  \
	-font fixed -background white \
	-takefocus false -insertofftime 0 \
	-yscrollcommand "$mf.scroll set"

    # By default hide time stamps
    set showTime false
    $mf.text tag configure TS -elide on
    $mf.text tag configure TS -background yellow

    $mf.text tag configure SR -background lightgreen

    scrollbar $mf.scroll -command "$mf.text yview"
    pack $mf.scroll -side right -fill y
    pack $mf.text -fill both  -expand yes

    $mf.text insert 0.0 "alogview version $version\n-- \n"
    $mf.text configure -state disabled


    pack .main -fill both -expand yes

    socket -server connectClient $port
}

#----------------------------------------------------------------------
##
## Text mode interface
##
proc textWindow {{myport 3412}} {
    global port version

    set port $myport
    wm withdraw .
    socket -server connectClient $port
    puts "alogview version $version\n-- "
}

#----------------------------------------------------------------------
##
## Handler for socket connections
##
proc connectClient { chan address port } {
    global textMode mf channel logId name visible

    # Read the channel name
    set n [gets $chan]
    set channel($logId) $chan
    # Register a read handler
    fconfigure $chan -blocking false
    fileevent $chan readable "logData $logId"
    if { ! $textMode } {
	$mf.text configure -state normal
	$mf.text insert end "$n connected from $address\n" $n
	$mf.text configure -state disabled

	# Handle visibility
	if [expr ! [info exists visible($n)]] {
	    set menu [.main getmenu viewMenu]
	    set visible($n) 1
	    $menu add checkbutton -label $n -variable visible($n) \
		-command "setVisibility $n"
	}
    }
    # Register channel
    set name($logId) $n
    incr logId
}

#----------------------------------------------------------------------
##
## Handler for data arriving
##
proc logData { logId } {
    global textMode mf name channel TS0

    set chan $channel($logId)

    set msg [gets $chan]

    if { !$textMode } {
	# Enable text widget modifications
	$mf.text configure -state normal
    }
    # Check for client disconnection
    if  [eof $chan] {
	close $chan
	if {!$textMode} {
	    $mf.text insert end "$name($logId) disconnected\n" $name($logId)
	    $mf.text configure -state disabled
	} else {
	    puts "$name($logId) disconnected"
	}
	unset channel($logId)
	return
    }
    if { !$textMode } {
	# Add time stamp with "TS" and name tags
	$mf.text insert end [clock format [clock seconds] -format "%T "] \
	    "TS $name($logId)"
	# Add name and text itself
	$mf.text insert end "<$name($logId)> $msg\n" $name($logId)
	# scroll to bottom if needed
	$mf.text yview -pickplace end
	# disable text widget edition again
	$mf.text configure -state disabled
    } else {
	set TS [clock milliseconds]
	regsub {([0-9]{5})([0-9]{3})} [format %08lld [expr $TS - $TS0]] {\1.\2} ts
	set TS0 $TS
	puts "<$name($logId) $ts> $msg"
    }
}

#----------------------------------------------------------------------
##
## Hide/show given log
##
proc setVisibility { n } {
    global mf visible showTime

    # puts "setVisibility $n $visible($n)"
    $mf.text tag configure $n -elide [expr ! $visible($n)]
}

#----------------------------------------------------------------------
##
## Toggle time stamps viewing
##
proc viewTimeStamps { } {
    global mf showTime

    # puts "viewTimeStamps $showTime"
    $mf.text tag configure TS -elide [expr ! $showTime]
}
#----------------------------------------------------------------------
##
## Reset the log view (clear all text)
##
proc resetLog {} {
    global mf version

    # Allow text modifications
    $mf.text configure -state normal
    # Delete all
    $mf.text delete 1.0 end
    # Insert initial marker
    $mf.text insert 1.0 "alogview version $version\n-- \n"
    # And disable edition
    $mf.text configure -state disabled
}

#----------------------------------------------------------------------
##
## Save the current log
##
proc doSave {} {
    global mf

    # Log file types
    set types {
	{{Log files} {.log}}
	{{All files} *}
    }
    # file chooser
    set file [tk_getSaveFile -filetypes $types \
		  -defaultextension ".log" -title "save logs as..."]
    if {$file != ""} {
	# If a name was choosen, save to it
	set chan [open $file "w"]
	puts $chan [$mf.text get 1.0 end]
	close $chan
    }
}
#----------------------------------------------------------------------
##
## Search text in the log
##
## Hihlight it using the 'SR' tag
##
proc doSearch {} {
    global mf status

    # Get text to search
    set text [$mf.search.text get]
    if { $text == "" } return

    # Remove current results marks
    $mf.text tag remove SR 1.0 end
    # Start at the beginning of text
    set cur 1.0
    set n 0
    while 1 {
	# search text once
	set new [$mf.text search -nocase -count c $text $cur end]
	if {$new == ""} {
	    # not found, done.
	    break
	}
	set cur $new
	# Mark occurence
	$mf.text tag add SR $cur "$cur + $c char"
	set cur [$mf.text index "$cur + $c char"]
	incr n
    }
    if {$cur != "1.0"} {
	# if at least one occurence was found, position the scroll bar
	# to show the last one.
	$mf.text yview -pickplace $cur
	set status "$n string(s) found"
    } else {
	set status "No string found"
    }
}

#----------------------------------------------------------------------
##
## About
##
proc aboutBox {} {
    global version

    # Nothing fancy here.
    tk_messageBox -type ok -title "About alogview" \
	-message "alogview version $version\nCopyright (c) 2006-2007 CNRS-LAAS"

}


#----------------------------------------------------------------------
##
## Start with options
##
proc alogviewOptions { argv } {
    global port textMode

    set nextisport 0
    set myport $port

    foreach option $argv {

#	puts "option $option"
	if { $nextisport == 1 } {
	    set myport $option
	    puts "alog on port $myport"
	    set nextisport 0
	    continue
	}

	switch -regexp -- "$option" {

	    # textmode
	    "--textmode"
	    -

	    "-textmode"
	    -

	    "-t"
	    {set textMode 1}

	    # port
	    "-p"
	    -

	    "-port"
	    -

	    "--port"
	    {set nextisport 1}

	    # help and default
	    "--help"
	    -

	    default
	    {puts {
alogview [-t|--textmode]    # text mode only output
	 [-p|--port port]   # choose the port to listen to
}
		exit
	    }
	}
    }

    if { $textMode } {
	textWindow $myport
    } else {
	mainWindow $myport
    }
}



#----------------------------------------------------------------------

###
### Go
###

alogviewOptions $argv
