# maphtml.tcl --
#
# This file contains utilities to convert maps into a set of html files.
#
# Copyright (c) 1997      Gaertner Datensysteme.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

package require Tnm
package provide TnmMapHtml $tnm(version)

proc Tnm_MapToHtml {map root {interval ""}} {
    if [string length $interval] {
	set ms [expr $interval * 1000]
	job create -command [list TnmMapToHtml $map $root] -interval $ms
    } else {
	TnmMapToHtml $map $root
    }
}

proc TnmMapToHtml {map root} {
    global tnm
    set tmp $tnm(tmp)/map[pid]
    set f [open $tmp w]
    puts $f "<HTML>\n<HEAD>"
    puts $f "<TITLE>[$map cget -name]</TITLE>"
    puts $f "<META HTTP-EQUIV=\"refresh\" CONTENT=\"120\">"
    puts $f "</HEAD>\n"
    puts $f "<BODY>"
    puts $f [TnmMapHtmlMap $map]
    puts $f "</BODY>\n</HTML>"
    close $f
    file rename -force $tmp $root/index.html

    foreach item [$map items] {
	set f [open $tmp w]
	puts $f "<HTML>\n<HEAD>"
	puts $f "<TITLE>[$item cget -name]</TITLE>"
	puts $f "<META HTTP-EQUIV=\"refresh\" CONTENT=\"120\">"
	puts $f "</HEAD>\n"
	puts $f "<BODY>"
	puts $f [TnmMapHtmlItem $item]
	puts $f "</BODY>\n</HTML>"
	close $f
	file rename -force $tmp $root/$item.html
    }
}

proc TnmMapHtmlMap {map} {
    global tnm
    set Attributes [TnmMapHtmlAttributes $map]
    foreach type [map types] {
	set table($type) [TnmMapHtmlItemSummary $map $type]
    }

    append html "<H1><A HREF=\"$tnm(url)\">"
    append html "<IMG SRC=\"$tnm(url)/tkined.gif\" ALIGN=RIGHT ALT=\"\"></A>"
    append html "Network Map Status Page ([$map cget -name])</H1>\n"
    append html "<TABLE>\n"
    append html "<TR><TD><B>Now:</B>"
    append html "<TD><EM>[clock format [clock seconds]]</EM><BR>\n"
    append html "<TR><TD><B>Start:</B>"
    append html "<TD><EM>[clock format $tnm(start)]</EM>\n"
    append html "</TABLE>\n"
    append html "<HR><B>"
    append html "\[<A HREF=\"#Description\">Description</A> "
    foreach part {Attributes} {
	if {[info exists $part] && [string length [set $part]]} {
	    append html " | <A HREF=\"#$part\">$part</A>"
	}
    }
    foreach type [array names table] {
	if [string length $table($type)] {
	    set part [TnmHtmlCapitalize $type]s
	    append html " | <A HREF=\"#$part\">$part</A>"
	}
    }
    append html "\]</B><HR><P>\n"

    append html "<H2><A NAME=\"Description\">Description:</A></H2>\n"
    append html "This network status page is generated by the\n"
    append html "<A HREF=\"$tnm(url)\">scotty network management\n"
    append html "package</A>.\n"
    append html "It is based on the upcoming 2.2.0 version, which\n"
    append html "includes commands to manage generic network maps.\n"
    append html "A network map allows to generate events. Generic\n"
    append html "monitoring jobs are provided which act as event sources.\n"
    append html "Tcl scripts can be bound to map events in order to filter\n"
    append html "and analyze events.\n<P>\n"
    append html "Network maps maintain health attributes for every map\n"
    append html "object. The health is defined as a value between 0 (bad)\n"
    append html "and 100 (good). Tcl scripts bound to events usually\n"
    append html "modify the health of affected devices, which makes it\n"
    append html "easy to aggregate information.\n<P>\n"
    append html "The package includes a library package which allows to\n"
    append html "convert a map into a set of HTML pages. These HTML pages\n"
    append html "can be updated at regular intervals so that you can\n"
    append html "check the status of your network from remote locations.\n"
    append html "Unhealthy map elements are show in a red color while\n"
    append html "healthy elements are shown in green color.\n"
    append html "A history of events is automatically maintained for each\n"
    append html "map object which allows to pinpoint problems easily.\n"
    foreach part {Attributes} {
	append html [set $part]
    }
    foreach type [array names table] {
	append html [set table($type)]
    }
    return $html
}

proc TnmMapHtmlItem {item} {
    global tnm
    set Attributes [TnmMapHtmlAttributes $item]
    set Links [TnmMapHtmlLinks $item]
    set Messages [TnmMapHtmlMessages $item]

    set name [$item cget -name]
    if {![string length $name]} {
	set name [$item cget -address]
	if {![string length $name]} {
	    set name $item
	}
    }

    append html "<H1><A HREF=\"$tnm(url)\">"
    append html "<IMG SRC=\"$tnm(url)/tkined.gif\" ALIGN=RIGHT ALT=\"\"></A>"
    append html "[TnmHtmlCapitalize [$item type]] Status Page ($name)</H1>\n"
    append html "<TABLE>\n"
    append html "<TR><TD><B>Now:</B>"
    append html "<TD><EM>[clock format [clock seconds]]</EM><BR>\n"
    append html "<TR><TD><B>Start:</B>"
    append html "<TD><EM>[clock format $tnm(start)]</EM>\n"
    append html "</TABLE>\n"
    append html "<HR><B>"
    append html "\[<A HREF=\"#Description\">Description</A> "
    foreach part {Attributes Links Messages} {
	if {[info exists $part] && [string length [set $part]]} {
	    append html " | <A HREF=\"#$part\">$part</A>"
	}
    }
    append html "\]</B><HR><P>\n"

    append html [TnmMapHtmlDescription $item]
    foreach part {Attributes Links Messages} {
	append html [set $part]
    }
    return $html
}

proc TnmMapHtmlItemSummary {map type} {
    foreach item [$map items -type $type -sort health -order increasing] {
	set health [lindex [$item health] 0]
	append table "<TR BGCOLOR=\"[TnmMapHtmlColor $health]\">\n"
	catch {
	    append table \
	    "<TD> <A HREF=\"$item.html\">[TnmHtmlQuote [$item cget -name]]</A>\n"
	}
	catch {
	    append table \
	    "<TD> <A HREF=\"$item.html\">[TnmHtmlQuote [$item cget -address]]</A>\n"
	}
	catch {
	    append table "<TD ALIGN=RIGHT> [TnmHtmlQuote $health]\n"
	}
    }
    if {![info exists table]} return

    set name [TnmHtmlCapitalize $type]s
    append html "<H2><A NAME=\"$name\">$name:</A></H2>\n"
    append html "<TABLE BORDER>\n"
    append html "<TR><TH>Name<TH>Address<TH>Health\n"
    append html $table
    append html "</TABLE>\n"
    return $html
}

proc TnmMapHtmlDescription {item} {
    catch {
	set health [lindex [$item health] 0]
	set color [TnmMapHtmlColor $health]
    }
    append html "<H2><A NAME=\"Description\">Description:</A></H2>\n"
    if [info exists color] {
	append html "<TABLE BORDER BGCOLOR=\"$color\">\n"
    } else {
	append html "<TABLE BORDER>\n"
    }
    catch {
	append html "<TR><TD><B>Id:</B>"
	append html "<TD>[TnmHtmlQuote $item]\n"
    }
    catch {
	append html "<TR><TD><B>Name:</B>"
	append html "<TD>[TnmHtmlQuote [$item cget -name]]\n"
    }
    catch {
	append html "<TR><TD><B>Address:</B>"
        append html "<TD>[TnmHtmlQuote [$item cget -address]]\n"
    }
    catch {
	append html "<TR><TD><B>Source:</B><TD>[$item cget -src]\n"
    }
    catch {
	append html "<TR><TD><B>Destination:</B><TD>[$item cget -dst]\n"
    }
    catch {
	append html "<TR><TD NOWRAP><B>Health:</B><TD>[$item health]\n"
    }
    catch {
	append html "<TR><TD><B>Priority:</B><TD>[$item cget -priority]\n"
    }
    catch {
	append html "<TR><TD><B>Expire:</B><TD>[$item cget -expire] seconds\n"
    }
    catch {
	append html "<TR><TD><B>Created:</B>"
        append html "<TD>[clock format [$item cget -ctime]]\n"
    }
    catch {
	append html "<TR><TD><B>Modified:</B>"
        append html "<TD>[clock format [$item cget -mtime]]\n"
    }
    append html "</TABLE>\n"
    return $html
}

proc TnmMapHtmlLinks {item} {
    if [catch {$item info links} links] return
    foreach link $links {
	set dst [$link cget -src]
	if {[string compare $dst $item] == 0} {
	    set dst [$link cget -dst]
	}
	append table "<TR>"
	append table "<TD><A HREF=\"$link.html\">$link</A>"
	append table \
		"<TD><A HREF=\"$dst.html\">[TnmHtmlQuote [$dst cget -name]]</A>"
	append table \
		"<TD><A HREF=\"$dst.html\">[TnmHtmlQuote [$dst cget -address]]</A>\n"
    }
    if {![info exists table]} return

    append html "<H2><A NAME=\"Links\">Links:</A></H2>\n"
    append html "<TABLE BORDER>\n"
    append html "<TR><TH>Id<TH>Destination Name<TH>Destination Address\n"
    append html $table
    append html "</TABLE>\n"
    return $html
}

proc TnmMapHtmlAttributes {item} {
    foreach name [lsort [$item attribute]] {
	if {! [regexp {(^:)|(^Tnm:)} $name]} continue
	append table "<TR>"
	append table "<TD NOWRAP><B>[TnmHtmlQuote [string trim $name :]]</B>"
	append table "<TD NOWRAP>[TnmHtmlQuote [$item attribute $name]]\n"
    }
    if {![info exists table]} return

    append html "<H2><A NAME=\"Attributes\">Attributes:</A></H2>\n"
    append html "<TABLE BORDER>\n"
    append html "<TR><TH>Attribute<TH>Value\n"
    append html $table
    append html "</TABLE>\n"
    return $html
}

proc TnmMapHtmlEvents {item} {
 
    foreach event [$item info events] {
	set secs [$event time]
	append table "<TR>"
	append table "<TD NOWRAP> [clock format $secs -format {%d %B %Y}]\n"
	append table "<TD NOWRAP> [clock format $secs -format {%T}]\n"
	append table "<TD NOWRAP> [TnmHtmlQuote [$event tag]]\n"
	append table "<TD NOWRAP> [TnmHtmlQuote [$event args]]\n"
    }
    if {![info exists table]} return

    append html "<H2><A NAME=\"Events\">Events:</A></H2>\n"
    append html "<TABLE BORDER>\n"
    append html "<TR><TH>Date<TH>Time<TH>Event<TH>Arguments\n"
    append html $table
    append html "</TABLE>\n"
    return $html
}

proc TnmMapHtmlMessages {item} {
 
    foreach msg [$item info messages] {
	set secs [$msg time]
	set color [TnmMapHtmlColor [expr [$msg health] + 80]]
	append table "<TR BGCOLOR=\"$color\">"
	append table "<TD NOWRAP> [clock format $secs -format {%d %B %Y}]\n"
	append table "<TD NOWRAP> [clock format $secs -format {%T}]\n"
	append table "<TD NOWRAP> [TnmHtmlQuote [$msg tag]]\n"
	append table "<TD ALIGN=RIGHT> [$msg health]\n"
	append table "<TD NOWRAP> [TnmHtmlQuote [$msg text]]\n"
    }
    if {![info exists table]} return

    append html "<H2><A NAME=\"Messages\">Messages:</A></H2>\n"
    append html "<TABLE BORDER>\n"
    append html "<TR><TH>Date<TH>Time<TH>Tag<TH>Score<TH>Message\n"
    append html $table
    append html "</TABLE>\n"
    return $html
}

proc TnmMapHtmlColor {health} {
    format #%2X%2X00 [expr 250 - $health] [expr 50 + ($health * 2)]
}

proc TnmHtmlQuote {string} {
    regsub -all {&} $string {&amp;} string
    regsub -all {<} $string {\&lt;} string
    regsub -all {>} $string {\&gt;} string
    return "&nbsp;$string&nbsp;"
}

proc TnmHtmlCapitalize {word} {
    return "[string toupper [string index $word 0]][string range $word 1 end]"
}
