# $Id: ScriptCreator.xotcl,v 1.4 2006/02/18 22:17:33 neumann Exp $

package provide xotcl::scriptCreation::scriptCreator 1.0
package require XOTcl 1

namespace eval ::xotcl::scriptCreation::scriptCreator {
    namespace import ::xotcl::*

    Class ScriptCreator \
	-parameter {
	    {excludedObjs {Object Class Class::Parameter}}
	    {excludeNames ""}
	    {dependencyChecking 1}
	}


    #
    # queries the excludedObjs variable whether a given object
    # is already defined/predefined or not  
    # -> a way to exclude classes/objs from saving
    #
    ScriptCreator instproc isExcluded {n} {
	my instvar excludedObjs
	#puts stderr "Checking Excluded: $n in $excludedObjs"
	if {[lsearch $excludedObjs [string trimleft $n :]] == -1} {
	    return 0
	} else {
	    return 1
	}
    }

    ScriptCreator instproc appendExcluded {n} {
	my instvar excludedObjs
	lappend excludedObjs [string trimleft $n :]
    }

    #
    # compare command for lsort  
    #
    ScriptCreator instproc namespaceDepth {a b} {
	set aCount 0
	set bCount 0
	for {set i 0} {$i < [string length $a]} {incr i} {
	    if {[string index $a $i] eq ":"} {
		incr aCount
	    }
	}
	for {set i 0} {$i < [string length $b]} {incr i} {
	    if {[string index $b $i] eq ":"} {
		incr bCount
	    }
	}
	if {$aCount == $bCount} {
	    return 0
	} elseif {$aCount > $bCount} {
	    return 1
	}
	
	return -1
    } 

    #
    # produces a script containing the current state of 
    # the given obj
    #
    ScriptCreator instproc stateScript {obj} {
	set script ""
	foreach v [$obj info vars] {
	    if {[lsearch [my set excludeNames] $v] == -1} {
		if {[$obj array exists $v]} {
		    foreach name [$obj array names $v] {
			set arr ${v}($name)
			set value [$obj set $arr]
			append script "$obj set $arr \"$value\"\n"
		    }
		} else {
		    set value [$obj set $v]
		    append script "$obj set $v \"$value\"\n"
		}
	    }
	}
	return $script
    }

    #
    # produces a script containing the procs of the given obj
    #
    ScriptCreator instproc procScript {obj} {
	set script ""
	foreach p [$obj info procs] {
	    if {[lsearch [my set excludeNames] $p] == -1} {
		append script \
		    "$obj proc $p \{[$obj info args $p]\} \{[$obj info body $p]\}\n"
	    }
	}
	return $script
    }

    #
    # produces a script containing the instprocs of the given class
    #
    ScriptCreator instproc instprocScript {cl} {
	set script ""
	foreach p [$cl info instprocs] {
	    if {[lsearch [my set excludeNames] $p] == -1} {
		append script \
		    "$cl instproc $p \{[$cl info instargs $p]\} \{[$cl info instbody $p]\}\n"
	    }
	}
	return $script
    }



    #
    # saves a script to a file
    #
    ScriptCreator instproc saveScript {filename script} {
	set f [open $filename w]
	puts $f $script
	close $f
    }

    #
    # load a script from a file
    #
    ScriptCreator instproc loadScript {filename} {
	set f [open $filename r]
	set r [read $f]
	close $f
	return $r
    }

    #
    # check parent obj/classes/namespaces of an object completly
    #
    ScriptCreator instproc checkParents {name} {
	set p ""

	set n $name
	while {[set np [namespace parent ::$n]] != "::"} {
	    lappend p $np
	    set n $np
	}    
	set p [lsort -command {my namespaceDepth} $p]

	foreach n $p {
	    if {![my isExcluded $n] &&
		![my isAppended $n]} {
		error "ScriptCreator: $name needs parent $n, neither appended nor excluded yet."
	    }
	}
    }

    ScriptCreator instproc checkClass {obj class} {
	if {![my isExcluded $class] &&
	    ![my isAppended $class]} {
	    error "ScriptCreator: $obj depends on $class, neither appended nor excluded yet."
	}
    }

    ScriptCreator instproc isAppended name {
	set n [string trimleft $name :]
	if {[lsearch [my set appendedNames] $n]!=-1} {
	    return 1
	} else {
	    return 0
	}
    }

    ScriptCreator instproc appendName name {
	set n [string trimleft $name :]
	my lappend appendedNames $n
    }

    ScriptCreator instproc makeScript args {
	my instvar dependencyChecking
	my set appendedNames ""
	set script ""
	foreach name $args {
	    #puts stderr "Script Creator -- $name"
	    if {![my isExcluded $name] && 
		![my isAppended $name]} {
		
		if {$dependencyChecking} {
		    my checkParents $name
		}
		if {[Object isobject $name]} {
		    set class [$name info class]
		    if {$dependencyChecking} {
			my checkClass $name $class
		    }
		    if {[Object isclass $name]} {
			# append the class
			#puts stderr "Appending Class: $name"
			append script "[$name info class] $name"
			set sl [$name info superclass]
			if {$dependencyChecking} {
			    foreach c $sl {
				my checkClass $name $c
			    }
			}
			if {$sl ne ""} {
			    append script " -superclass \{$sl\}\n"
			} else {
			    append script "\n"
			}
			append script [my instprocScript $name]
		    } else {
			# append the obj
			#puts stderr "Appending Object: $name"
			append script "[$name info class] $name\n"
		    }
		    append script [my procScript $name]
		} else {
		    append script "namespace eval $name \{\}\n"
		    #puts stderr "Appending Namespace: $name"
		}
		my appendName $name
	    }
	}
	return $script
    }

    namespace export ScriptCreator
}

namespace import ::xotcl::scriptCreation::scriptCreator::*
