# javaDetect.tcl --
#
# Utility to find libjvm prior to loading tclJBlend

namespace eval java {}

# java::jvmFindInt --
#
#	Internal function to traverse given directories and accumulate
#	matching file names which are filtered against directory patterns.
#
# Results:
#	Path names are appended to the result list variable.

proc java::jvmFindInt {result dirs name nc filters} {
    upvar $result ret
    set subdirs {}
    foreach dir $dirs {
	foreach fn [glob -nocomplain -dir $dir -types {f l} $name] {
	    if {$nc} {
		foreach pat $filters {
		    if {[string match -nocase $pat $fn]} {
			lappend ret $fn
			break
		    }
		}
	    } else {
		foreach pat $filters {
		    if {[string match $pat $fn]} {
			lappend ret $fn
			break
		    }
		}
	    }
	}
	lappend subdirs {*}[glob -nocomplain -dir $dir -types {d l} *]
    }
    if {[llength $subdirs]} {
	tailcall java::jvmFindInt ret $subdirs $name $nc $filters
    }
}

# java::jvmFindReg --
#
#	Internal function to traverse the Windows registry for
#	path names to the JVM runtime library.
#
# Results:
#	Path names are appended to the result list variable.

proc java::jvmFindReg {result} {
    upvar $result ret
    set top HKEY_LOCAL_MACHINE\\Software\\JavaSoft
    foreach dir {"JDK" "Java Development Kit" "Java Runtime Environment"} {
	set dir [join [list $top $dir] \\]
        if {[catch {registry keys $dir} subdirs]} {
	    continue
	}
	foreach sub $subdirs {
	    set sub [join [list $dir $sub] \\]
	    if {![catch {registry get $sub RuntimeLib} val]} {
		if {[file exists $val]} {
		    lappend ret $val
		}
	    }
	}
    }
}

# java::jvmFind --
#
#	Try to find libjvm.so or jvm.dll depending on platform.
#
# Results:
#	On success, the LOAD_JVM environment variable is set
#	to the shared library or dll to be loaded.

proc java::jvmFind {} {
    if {[info exists ::env(LOAD_JVM)] && $::env(LOAD_JVM) ne ""} {
	# already set, do nothing
	return
    }
    set lib {}
    switch -- [info sharedlibextension] {
	.dll {
	    # Windows
	    package require registry
	    set ret {}
	    java::jvmFindReg ret 
	    set lib [lindex [lsort -decreasing -dictionary $ret] 0]
	}
	.dylib {
	    # MacOSX
	    set libs /Library/Java/JavaVirtualMachines
	    set ret {}
	    java::jvmFindInt ret $libs libjvm[info sharedlibextension] 0 *
	    set lib [lindex [lsort -decreasing -dictionary $ret] 0]
	}
	default {
	    # all else POSIX guesswork
	    set arch $::tcl_platform(machine)
	    set libs /usr/local/lib/jvm
	    switch -glob -- $arch {
		x86_64 - amd64 {
		    if {$::tcl_platform(pointerSize) < 8} {
			set arch [list {*i[3-6]86*} *x86/*]
			lappend libs /usr/lib/jvm
		    } else {
			set arch [list *x86_64* *amd64*]
			lappend libs /usr/lib64/jvm /usr/lib/jvm
		    }
		}
		aarch64 {
		    if {$::tcl_platform(pointerSize) < 8} {
			set arch *arm*
			lappend libs /usr/lib/jvm
		    } else {
			set arch [list *aarch64* *arm64*]
			lappend libs /usr/lib64/jvm /usr/lib/jvm
		    }
		}
		i86pc {
		    lappend libs /usr/jdk/latest/jre /usr/jdk/instances
		    if {$::tcl_platform(pointerSize) < 8} {
			set arch [list {*i[3-6]86*} *86/*]
		    } else {
			set arch [list *amd64* *x86_64*]
		    }
		}
		default {
		    set arch *
		    lappend libs /usr/lib/jvm
		}
	    }
	    set ret {}
	    java::jvmFindInt ret $libs libjvm[info sharedlibextension] 0 $arch
	    set i [lsearch -glob $ret */default-java/*]
	    if {$i >= 0} {
		set lib [lindex $ret $i]
	    } else {
		set ret [lsort -decreasing -dictionary $ret]
		# prefer JDK (OpenJDK)
		set i [lsearch -glob $ret *java*openjdk*]
		if {$i < 0} {
		    # fallback to JDK (any)
		    set i [lsearch -glob $ret */java*]
		}
		if {$i < 0} {
		    # fallback to JRE (OpenJDK)
		    set i [lsearch -glob $ret *openjdk*]
		}
		if {$i < 0} {
		    # last resort: the very first hit
		    set i 0
		}
		set lib [lindex $ret $i]
	    }
	}
    }
    if {$lib ne ""} {
	set ::env(LOAD_JVM) $lib
    }
    return
}

java::jvmFind
rename java::jvmFind {}
rename java::jvmFindReg {}
rename java::jvmFindInt {}
