#
# Library functions for the ic_xml library
#
# File: ic_xml/scriptinter/ic_xml_report.tcl
#
# $Revision$ on $Date$
# 
# Copyright (c) 1993-2004 ICEM CFD Engineering
#
# Author: Jason Aftosmis (jason@icemcfd.com)
#
# TODO:
#   o
#
#---------------------------------------------------------------------


namespace eval IcXmlReport {
    
    namespace import ::IcXml::*
    
    variable _rootElNameV    ;#  hash of strings, as in "MESH_REPORT"
    variable _rootObjV       ;# hash of objects ?? fixme --jka
    
    proc err {text}   { if 1 { return [ic_xml_err   "IcXmlReport: $text"] } }
    proc debug {text} { if 1 { return [ic_xml_debug "IcXmlReport: $text"] } }
}


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


##

proc IcXmlReport::getRootElName { domKey } {
    variable _rootElNameV
    if [info exists _rootElNameV($domKey)] {
	return $_rootElNameV($domKey)
    }
    return ""
}


##

proc IcXmlReport::getNullToken {} {
    return "__NONE__"
}


##

proc IcXmlReport::checkEmpty {} {
    variable _rootObjV
    variable _rootElNameV
    
    set stat SUCCESS
    
    if {[info exists _rootObjV] && ("" != [array names _rootObjV])} {
	err "the _rootObjV is not empty, rather has keys [array names _rootObjV]"
	set stat FAILURE
    }
    if {[info exists _rootElNameV] && ("" != [array names _rootElNameV])} {
	err "the _rootElNameV is not empty, rather has keys [array names _rootElNameV]"
	set stat FAILURE
    }
    
    return $stat
}


## Create a document with a root <$rootElName>. The rootElName given to
## this proc is stored and can be retrieved with getRootElname, should
## one need it. It also sets the _rootObjV var.

proc IcXmlReport::createDocument { rootElName {attributes ""} } {
    variable _rootObjV
    variable _rootElNameV
    
    #
    # --> CREATE THE DOM
    #
     
    set domKey [IcXmlInter::domCreate $rootElName]
    if {"" == $domKey} {
	err "could not create a DOM with \"$rootElName\""
	return -code error ""
    }
    
    #
    # --> ADD THE ROOT ELEMENT AND ITS ATTRIBUTES
    #
    
    set attribute_list [IcXmlReport::parseValidAttributes $rootElName $attributes]
    set attribute_list [enforceAttributes $attribute_list "date user"]
    
    set rootObj [IcXmlInter::domRoot $domKey $attribute_list]
    if {"" == $domKey} {
	err "could not create a get the DOM root for \"$domKey\""
	return -code error ""
    }
    
    debug "rootObj is $rootObj && domKey is $domKey"
    
    set _rootObjV($domKey)    $rootObj
    set _rootElNameV($domKey) $rootElName
    
    return $domKey
}


## 

proc IcXmlReport::closeDocument { domKey } {
    variable _rootObjV
    variable _rootElNameV
    
    debug "closing document $domKey..."

    set stat [IcXmlInter::domClose $domKey]
    
    debug "...closed it...now unset $_rootObjV($domKey) & $_rootElNameV($domKey)..."
    
    catch { unset _rootObjV($domKey) }
    catch { unset _rootElNameV($domKey) }
    
    return $stat
}


## 

proc IcXmlReport::openReport { xml_file {required_root_element_name ""} } {
    variable _rootObjV
    variable _rootElNameV
    
    set domKey [IcXmlInter::domOpen $xml_file]
    debug "opened up file $xml_file into domKey \"$domKey\""
    
    if {"" == $domKey} {
	err "could not open report $xml_file"
	return ""
    }

    set rootObj [IcXmlInter::domRoot $domKey]
    debug "from file $xml_file and got dom rootObj \"$rootObj\""
    
    set rootElName [IcXmlInter::getElementName $rootObj]
    debug "that rootObj $rootObj has name \"$rootElName\""
    
    if {("" != $required_root_element_name) && ($rootElName != $required_root_element_name)} {
	err "the root element of the given xml_file $xml_file is not the expected $required_root_element_name, rather $rootElName"
	IcXmlInter::domClose $domKey
	return -code error "The root element name is $rootElName rather than $required_root_element_name"
    }
    
    set _rootObjV($domKey)    $rootObj
    set _rootElNameV($domKey) $rootElName
    
    return $domKey
}


##

proc IcXmlReport::getReportObject { domKey } {
    variable _rootObjV
    
    if ![info exists _rootObjV($domKey)] {
	return ""
    }
    
    return $_rootObjV($domKey)
}


## Open up a domain file, and create an XML file with the specified
## xml-elements, or else create the default report.
## Example:

proc IcXmlReport::writeReport { domKey file format args } {
    
    #if ![info exists _rootObjV] {
#	err "no current document object to write..."
	#return ""
    #}
    
    set css_file ""    
    foreach {n v} $args {
	set [string range $n 1 end] $v
    }
    
    debug "getting $format-formatted DOM data from $domKey"
    
    set data [eval IcXmlInter::domFormat $domKey $format $args]
    
    set fd [open $file w]
    
    switch -- $format {
	HTML - html {
	    if {("" != $css_file) && [file exists $css_file]} {
		set cssFp [open $css_file r]
		set css_data [read $cssFp]
		puts $fd "$css_data\n"
		close $cssFp
	    }
	}
    }
    
    puts $fd $data
    close $fd
    
    debug "wrote ${format}-formatted DOM data from $domKey"

    return $file
}


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



## Create an element in MESH_REPORT if necessary. Return the pointer.
## If we end up calling this too much, we might store the val instead
## of calling IcXmlInter::getElement too much. FIXME

proc IcXmlReport::ensureReportElement { domKey elName {attributes ""} } {
    
    set reportObj [IcXmlReport::getReportObject $domKey]    

    set reportChild [IcXmlInter::getElement $reportObj $elName]
    
    #  --> Create if not
    
    if {"" == $reportChild} {
	set reportChild [IcXmlReport::addElement $domKey $reportObj $elName $attributes ""]
    }
    
    return $reportChild
}

## 

proc IcXmlReport::addElement { domKey addToObj elementName attributes children } {
    
    set attributes [IcXmlReport::parseValidAttributes $elementName $attributes]
    set newObj [IcXmlInter::elementCreate $domKey $elementName $addToObj $attributes]
    
    #
    # --> Create the children
    #
    
    set valid_children [IcXmlReport::parseValidChildren $elementName $children]
    foreach {n v} $valid_children {
        set childObj [IcXmlInter::elementCreate $domKey $n $newObj "" $v]
        # childObj is not used
    }
    
    #
    # newObj is not tracked right now  fixme --jka FIXME FIXME
    #
    
    return $newObj
}


## A DATA element is an abstract element which should be usable widely,
## e.g.:
##    <dataset name="Element percentages">
##       <datum name="QUAD_4" type="percentage">90</datum>
##       <datum name="TRI_3"  type="percentage">10</datum>
##    </dataset>
## <em>children</em> should be of the form:
##    set children {{name type val} {name type val} ...}
## where each sub-list describes a datum.

proc IcXmlReport::addDataSet { domKey addToObj attributes children } {
    
    set elementName "data"
    set newObj [IcXmlInter::elementCreate $domKey $elementName $addToObj $attributes]
    
    #
    # --> Create the children
    #
    
    foreach datum $children {
	set elname "datum"
	set name [lindex $datum 0]
	set type [lindex $datum 1]
	set val  [lindex $datum 2]
	set attributes "name $name"
	if {"" != $type} { append attributes " type $type" }
        set childObj [IcXmlInter::elementCreate $domKey $elname $newObj $attributes $val]
        # childObj is not used
    }
    
    #
    # newObj is not tracked right now  fixme --jka FIXME FIXME
    #
    
    return $newObj
}


## A Histogram element is an abstract element which should be usable widely,
## e.g.:
##    <histogram name="Quality" eltypes="MIXED" min="0" max="1" nintervals="10">
##       <interval min="0.0" max="0.1">0</interval>
##       <interval min="0.1" max="0.2">0</interval>
##       ...
##       <interval min="0.9" max="1.0">1</interval>
##    </histogram>
##
## Args:
## <em>attributes</em> should be of the form:
##    set attributes {{name val} {name val} ...}
## where each sub-list describes one attribute
## <em>data</em> should be of the form:
##    set data {{min max num} {min max num} ...}

proc IcXmlReport::addHistogram { domKey addToObj attributes data } {
    
    set elementName "histogram"
    set newObj [IcXmlInter::elementCreate $domKey $elementName $addToObj $attributes]
    
    #
    # --> Create the children
    #
    
    foreach datum $data {
	set elname "interval"
	set min [lindex $datum 0]
	set decInd [string first . $min]
	if {0 <= $decInd} {
	    set min [string range $min 0 [expr $decInd + 2]]
	}
	set max [lindex $datum 1]
	set decInd [string first . $max]
	if {0 <= $decInd} {
	    set max [string range $max 0 [expr $decInd + 2]]
	}
	set val  [lindex $datum 2]
	set attributes "min $min max $max"
        set childObj [IcXmlInter::elementCreate $domKey $elname $newObj $attributes $val]
        # childObj is not used
    }
    
    #
    # newObj is not tracked right now  fixme --jka FIXME FIXME
    #
    
    return $newObj
}


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

## 

proc IcXmlReport::addHeader { domKey children } {

    set addToObj [getReportObject $domKey]
    
    set elementName "header"
    set newObj [IcXmlInter::elementCreate $domKey $elementName $addToObj ""]
    
    if {"" == $children} {	
	set children [enforceAttributes "" "date user"]
    }
    
    foreach {name val} $children {
        set childObj [IcXmlInter::elementCreate $domKey $name $newObj "" $val]
        # childObj is not used
    }
    
    return $newObj
}


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


## Write out a DTD for the given ROOT-elementName

proc IcXmlReport::writeDTDFile { file rootElementName } {
    
     set fd [open $file w]
     
     puts "<!DOCTYPE $rootElementName ["
     
     # FIXME -- get all stuff and put it
     
     puts "]>"
     
     close $fd
}


##

proc IcXmlReport::getPossibleAttributes { elementName } {
    
    switch -- $elementName {
	testset -
	TESTSET {
	    return "NAME TITLE TYPE APPLICATION VERSION MACHINE DATE"
	}
	testcase -
	TESTCASE {
	    return "TITLE"
	}
	mesh_report -
	MESH_REPORT {
	    return "title path subset type mesher date user script"
	}
	mesh_summary -
	MESH_SUMMARY {
	    return "subset"
	}
	mesh_quality -
	MESH_QUALITY {
	    return "subset"
	}
	mesh_diagnostics -
	MESH_DIAGNOSTICS {
	    return "subset"
	}
	default {
	    #err "could not get any attributes for \"$elementName\""
	    return ""
	}
    }
    return ""
}


##

proc IcXmlReport::getPossibleChildren { elementName } {
    
    switch -- $elementName {
	mesh_report -
	MESH_REPORT {
	    return "mesh_quality"
	}
	testset -
	TESTSET {
	    return "TESTCASE" ;# fixme
	}
	testcase -
	TESTCASE {
	    return "TESTPATH START ELAPSED RESULT DUMP PLATFORM" ;# fixme
	}
	mesh_summary -
	MESH_SUMMARY {
	    return ""
	}
	mesh_quality -
	MESH_QUALITY {
	    return ""
	}
	mesh_diagnostics -
	MESH_DIAGNOSTICS {
	    return ""
	}
	default {
	    #err "could not get any children for \"$elementName\""
	    return ""
	}
    }
    return ""
}


##

proc IcXmlReport::parseValidChildren  { elementName children } {
    
    set possible_children [getPossibleChildren $elementName]
    if {"" == $possible_children} {
	# then allow all
	return $children
    }
    
    foreach att $possible_children {
        set $att [getNullToken]
    }
    foreach {n v} $children {
        set $n $v
    }
    
    set valid_children ""
    foreach kid $possible_children {
        if [info exists $kid] {
            lappend valid_children $kid [set $kid]
        }
    }
    
    return $valid_children
}


##

proc IcXmlReport::parseValidAttributes { elementName attributes } {
    
    set possible_attributes [getPossibleAttributes $elementName]
    if {"" == $possible_attributes} {
	# then allow all
	return $attributes
    }
    
    foreach att $possible_attributes {
	if {"date" == [string tolower $att]} {
	    set $att [clock format [clock seconds]]
	} else {
	    set $att [getNullToken]
	}
    }
    foreach {n v} $attributes {
        set $n $v
    }
    
    set validated_atts ""    
    foreach att $possible_attributes {
        if [info exists $att] {
            lappend validated_atts $att [set $att]
        }
    }
    
    return $validated_atts
}

proc IcXmlReport::enforceAttributes { attributes attributes_to_enforce } {
    
    foreach att $attributes_to_enforce {
	
	set att_ind [lsearch $attributes $att]
	
	if {0 > $att_ind} {
	    
	    set att_val ""
	    switch -- $att {
		date {
		    set att_val [clock format [clock seconds]]
		}
		user {
		    if {"" != [info commands cmd_whoami]} {
			set user [cmd_whoami]
		    } else {
			global env
			if {[info exists env(USER)] && ($env(USER) != "")} {
			    set user $env(USER)
			} elseif {[info exists env(LOGNAME)] && ($env(LOGNAME) != "")} {
			    set user $env(LOGNAME)
			} else {
			    if ![catch {exec [get_path whoami]} res] {
				set env(USER) $res
				set user $res
			    } elseif {[set home [glob -nocomplain ~]] != ""} {
				set user [file tail $home]
			    } else {
				set user "IcXmlUser"
			    }
			}
		    }
		    set att_val $user
		}
	    }
	    if {"" != $att_val} {
		append attributes " $att \"$att_val\""
	    }
	}
    }
    
    return $attributes
}

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


##

proc IcXmlReport::compareFiles {standard proposed {ignoreCallBack ""}} {
    #
    # create two xml-structures and compare them
    # use the ignoreCallBack to ignore certain aspects of
    #    the files
    #
    # FIXME
    #
}

