#
# itclWidget.tcl
# ----------------------------------------------------------------------
# Invoked automatically upon startup to customize the interpreter
# for [incr Tcl] when one of ::itcl::widget or ::itcl::widgetadaptor is called.
# ----------------------------------------------------------------------
#   AUTHOR:  Arnulf P. Wiedemann
#
# ----------------------------------------------------------------------
#            Copyright (c) 2008  Arnulf P. Wiedemann
# ======================================================================
# See the file "license.terms" for information on usage and
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.

package require Tk 8.6
# package require itclwidget [set ::itcl::version]

namespace eval ::itcl {

proc widget {name args} {
    set result [uplevel 1 ::itcl::internal::commands::genericclass widget $name $args]
    # we handle create by owerselfs !! allow classunknown to handle that
    oo::objdefine $result unexport create
    return $result
}

proc widgetadaptor {name args} {
    set result [uplevel 1 ::itcl::internal::commands::genericclass widgetadaptor $name $args]
    # we handle create by owerselfs !! allow classunknown to handle that
    oo::objdefine $result unexport create
    return $result
}

} ; # end ::itcl


namespace eval ::itcl::internal::commands {

proc initWidgetOptions {varNsName widgetName className} {
    set myDict [set ::itcl::internal::dicts::classOptions]
    if {$myDict eq ""} {
        return
    }
    if {![dict exists $myDict $className]} {
        return
    }
    set myDict [dict get $myDict $className]
    foreach option [dict keys $myDict] {
        set infos [dict get $myDict $option]
	set resource [dict get $infos -resource]
	set class [dict get $infos -class]
	set value [::option get $widgetName $resource $class]
	if {$value eq ""} {
	    if {[dict exists $infos -default]} {
	        set defaultValue [dict get $infos -default]
	        uplevel 1 set ${varNsName}::itcl_options($option) $defaultValue
	    }
	} else {
	    uplevel 1 set ${varNsName}::itcl_options($option) $value
	}
    }
}

proc initWidgetDelegatedOptions {varNsName widgetName className args} {
    set myDict [set ::itcl::internal::dicts::classDelegatedOptions]
    if {$myDict eq ""} {
        return
    }
    if {![dict exists $myDict $className]} {
        return
    }
    set myDict [dict get $myDict $className]
    foreach option [dict keys $myDict] {
        set infos [dict get $myDict $option]
	if {![dict exists $infos -resource]} {
	    # this is the case when delegating "*"
	    continue
	}
	if {![dict exists $infos -component]} {
	    # nothing to do
	    continue
	}
	# check if not in the command line options
	# these have higher priority
	set myOption $option
	if {[dict exists $infos -as]} {
	   set myOption [dict get $infos -as]
	}
	set noOptionSet 0
	foreach {optName optVal} $args {
	    if {$optName eq $myOption} {
	        set noOptionSet 1
		break
	    }
	}
	if {$noOptionSet} {
	    continue
	}
	set resource [dict get $infos -resource]
	set class [dict get $infos -class]
	set component [dict get $infos -component]
	set value [::option get $widgetName $resource $class]
	if {$component ne ""} {
	    if {$value ne ""} {
		set compVar [namespace eval ${varNsName}${className} "set $component"]
		if {$compVar ne ""} {
	            uplevel 1 $compVar configure $myOption $value
	        }
	    }
	}
    }
}

proc widgetinitobjectoptions {varNsName widgetName className} {
#puts stderr "initWidgetObjectOptions!$varNsName!$widgetName!$className!"
}

proc deletehull {newName oldName what} {
    if {$what eq "delete"} {
        set name [namespace tail $newName]
        regsub {hull[0-9]+} $name {} name
        rename $name {}
    }
    if {$what eq "rename"} {
        set name [namespace tail $newName]
        regsub {hull[0-9]+} $name {} name
        rename $name {}
    }
}

proc hullandoptionsinstall {objectName className widgetClass hulltype args} {
    if {$hulltype eq ""} {
        set hulltype frame
    }
    set idx 0
    set found 0
    foreach {optName optValue} $args {
	if {$optName eq "-class"} {
	    set found 1
	    set widgetClass $optValue
	    break
	}
        incr idx
    }
    if {$found} {
        set args [lreplace $args $idx [expr {$idx + 1}]]
    }
    if {$widgetClass eq ""} {
        set widgetClass $className
	set widgetClass [string totitle $widgetClass]
    }
    set cmd "set win $objectName; ::itcl::builtin::installhull using $hulltype -class $widgetClass $args"
    uplevel 2 $cmd
}

} ; # end ::itcl::internal::commands

namespace eval ::itcl::builtin {

proc installhull {args} {
    set cmdPath ::itcl::internal::commands
    set className [uplevel 1 info class]

    set replace 0
    switch -- [llength $args] {
	0	{
		return -code error\
		"wrong # args: should be \"[lindex [info level 0] 0]\
		name|using <widgetType> ?arg ...?\""
	}
	1	{
		set widgetName [lindex $args 0]
		set varNsName $::itcl::internal::varNsName($widgetName)
	}
	default	{
		upvar win win
		set widgetName $win

		set varNsName $::itcl::internal::varNsName($widgetName)
	        set widgetType [lindex $args 1]
		incr replace
		if {[llength $args] > 3 && [lindex $args 2] eq "-class"} {
		    set classNam [lindex $args 3]
		    incr replace 2
		} else {
		    set classNam [string totitle $widgetType]
		}
		uplevel 1 [lreplace $args 0 $replace $widgetType $widgetName -class $classNam]
		uplevel 1 [list ${cmdPath}::initWidgetOptions $varNsName $widgetName $className]
	}
    }

    # initialize the itcl_hull variable
    set i 0
    set nam ::itcl::internal::widgets::hull
    while {1} {
         incr i
	 set hullNam ${nam}${i}$widgetName
	 if {[::info command $hullNam] eq ""} {
	     break
	}
    }
    uplevel 1 [list ${cmdPath}::sethullwindowname $widgetName]
    uplevel 1 [list ::rename $widgetName $hullNam]
    uplevel 1 [list ::trace add command $hullNam {delete rename} ::itcl::internal::commands::deletehull]
    catch {${cmdPath}::checksetitclhull [list] 0}
    namespace eval ${varNsName}${className} "set itcl_hull $hullNam"
    catch {${cmdPath}::checksetitclhull [list] 2}
    uplevel 1 [lreplace $args 0 $replace ${cmdPath}::initWidgetDelegatedOptions $varNsName $widgetName $className]
}

proc installcomponent {args} {
    upvar win win

    set className [uplevel 1 info class]
    set myType [${className}::info types [namespace tail $className]]
    set isType 0
    if {$myType ne ""} {
        set isType 1
    }
    set numArgs [llength $args]
    set usage "usage: installcomponent <componentName> using <widgetType> <widgetPath> ?-option value ...?"
    if {$numArgs < 4} {
        error $usage
    }
    foreach {componentName using widgetType widgetPath} $args break
    set opts [lrange $args 4 end]
    if {$using ne "using"} {
        error $usage
    }
    if {!$isType} {
        set hullExists [uplevel 1 ::info exists itcl_hull]
        if {!$hullExists} {
            error "cannot install \"$componentName\" before \"itcl_hull\" exists"
        }
        set hullVal [uplevel 1 set itcl_hull]
        if {$hullVal eq ""} {
            error "cannot install \"$componentName\" before \"itcl_hull\" exists"
        }
    }
    # check for delegated option and ask the option database for the values
    # first check for number of delegated options
    set numOpts 0
    set starOption 0
    set myDict [set ::itcl::internal::dicts::classDelegatedOptions]
    if {[dict exists $myDict $className]} {
        set myDict [dict get $myDict $className]
	foreach option [dict keys $myDict] {
	    if {$option eq "*"} {
	        set starOption 1
	    }
	    incr numOpts
	}
    }
    set myOptionDict [set ::itcl::internal::dicts::classOptions]
    if {[dict exists $myOptionDict $className]} {
        set myOptionDict [dict get $myOptionDict $className]
    }
    set cmd [list $widgetPath configure]
    set cmd1 "set $componentName \[$widgetType $widgetPath\]"
    uplevel 1 $cmd1
    if {$starOption} {
	upvar $componentName compName
	set cmd1 [list $compName configure]
        set configInfos [uplevel 1 $cmd1]
	foreach entry $configInfos {
	    if {[llength $entry] > 2} {
	        foreach {optName resource class defaultValue} $entry break
		set val ""
		catch {
		    set val [::option get $win $resource $class]
		}
		if {$val ne ""} {
		    set addOpt 1
		    if {[dict exists $myDict $$optName]} {
		        set addOpt 0
		    } else {
		        set starDict [dict get $myDict "*"]
			if {[dict exists $starDict -except]} {
			    set exceptions [dict get $starDict -except]
			    if {[lsearch $exceptions $optName] >= 0} {
			        set addOpt 0
			    }

			}
			if {[dict exists $myOptionDict $optName]} {
			    set addOpt 0
			}
                    }
		    if {$addOpt} {
		        lappend cmd $optName $val
		    }

		}

	    }
        }
    } else {
        foreach optName [dict keys $myDict] {
	    set optInfos [dict get $myDict $optName]
	    set resource [dict get $optInfos -resource]
	    set class [namespace tail $className]
	    set class [string totitle $class]
	    set val ""
	    catch {
	        set val [::option get $win $resource $class]
            }
	    if {$val ne ""} {
		if {[dict exists $optInfos -as] } {
	            set optName [dict get $optInfos -as]
		}
		lappend cmd $optName $val
	    }
	}
    }
    lappend cmd {*}$opts
    uplevel 1 $cmd
}

} ; # end ::itcl::builtin

set ::itcl::internal::dicts::hullTypes [list \
       frame \
       toplevel \
       labelframe \
       ttk:frame \
       ttk:toplevel \
       ttk:labelframe \
    ]

namespace eval ::itcl::builtin::Info {

proc hulltypes {args} {
    namespace upvar ::itcl::internal::dicts hullTypes hullTypes

    set numArgs [llength $args]
    if {$numArgs > 1} {
        error "wrong # args should be: info hulltypes ?<pattern>?"
    }
    set pattern ""
    if {$numArgs > 0} {
        set pattern [lindex $args 0]
    }
    if {$pattern ne ""} {
        return [lsearch -all -inline -glob $hullTypes $pattern]
    }
    return $hullTypes

}

proc widgetclasses {args} {
    set numArgs [llength $args]
    if {$numArgs > 1} {
        error "wrong # args should be: info widgetclasses ?<pattern>?"
    }
    set pattern ""
    if {$numArgs > 0} {
        set pattern [lindex $args 0]
    }
    set myDict [set ::itcl::internal::dicts::classes]
    if {![dict exists $myDict widget]} {
        return [list]
    }
    set myDict [dict get $myDict widget]
    set result [list]
    if {$pattern ne ""} {
        foreach key [dict keys $myDict] {
	    set myInfo [dict get $myDict $key]
	    set value [dict get $myInfo -widget]
	    if {[string match $pattern $value]} {
	        lappend result $value
            }
        }
    } else {
        foreach key [dict keys $myDict] {
	    set myInfo [dict get $myDict $key]
	    lappend result [dict get $myInfo -widget]
	}
    }
    return $result
}

proc widgets {args} {
    set numArgs [llength $args]
    if {$numArgs > 1} {
        error "wrong # args should be: info widgets ?<pattern>?"
    }
    set pattern ""
    if {$numArgs > 0} {
        set pattern [lindex $args 0]
    }
    set myDict [set ::itcl::internal::dicts::classes]
    if {![dict exists $myDict widget]} {
        return [list]
    }
    set myDict [dict get $myDict widget]
    set result [list]
    if {$pattern ne ""} {
        foreach key [dict keys $myDict] {
	    set myInfo [dict get $myDict $key]
	    set value [dict get $myInfo -name]
	    if {[string match $pattern $value]} {
	        lappend result $value
            }
        }
    } else {
        foreach key [dict keys $myDict] {
	    set myInfo [dict get $myDict $key]
	    lappend result [dict get $myInfo -name]
	}
    }
    return $result
}

proc widgetadaptors {args} {
    set numArgs [llength $args]
    if {$numArgs > 1} {
        error "wrong # args should be: info widgetadaptors ?<pattern>?"
    }
    set pattern ""
    if {$numArgs > 0} {
        set pattern [lindex $args 0]
    }
    set myDict [set ::itcl::internal::dicts::classes]
    if {![dict exists $myDict widgetadaptor]} {
        return [list]
    }
    set myDict [dict get $myDict widgetadaptor]
    set result [list]
    if {$pattern ne ""} {
        foreach key [dict keys $myDict] {
	    set myInfo [dict get $myDict $key]
	    set value [dict get $myInfo -name]
	    if {[string match $pattern $value]} {
	        lappend result $value
            }
        }
    } else {
        foreach key [dict keys $myDict] {
	    set myInfo [dict get $myDict $key]
	    lappend result [dict get $myInfo -name]
	}
    }
    return $result
}

} ; # end ::itcl::builtin::Info
