#!/bin/sh
# configure:
#    Makefile configuration script for ScidCommunity.
#
# This program tries to determine system-specific details needed
# for compiling ScidCommunity (such as the version of Tcl/Tk you have
# installed, and where to find the Tcl/Tk header and library 
# files), and creates the file "Makefile".

# Execute this script using tclsh
# The backslash at the end of this line is needed: \
exec tclsh "$0" ${1+"$@"}

# Default values for Makefile settings:
#

# Version detection
set var(SCID_VERSION) "5.1.1"
if {[info exists ::env(SCIDCOMMUNITY_VERSION)] && $::env(SCIDCOMMUNITY_VERSION) ne ""} {
    set var(SCID_VERSION) $::env(SCIDCOMMUNITY_VERSION)
} else {
    set git_ver ""
    # Try git archive expansion
    if {[file exists "GIT_ARCHIVE_VERSION"]} {
        set fp [open "GIT_ARCHIVE_VERSION" r]
        set line [read $fp]
        close $fp
        # Expanded format is usually "tag: v5.1.3.32" or "tag: v5.1.3.32, master"
        if {[regexp {tag: v?([0-9][^, )]*)} $line -> match_ver]} {
            set git_ver $match_ver
        }
    }
    # Fallback to git describe
    if {$git_ver eq "" && [catch {exec git describe --tags --always --dirty} describe_ver] == 0} {
        set git_ver $describe_ver
        if {[string index $git_ver 0] eq "v"} {
            set git_ver [string range $git_ver 1 end]
        }
    }
    if {$git_ver ne ""} {
        set var(SCID_VERSION) $git_ver
    }
}
set var(SCID_DATE) [clock format [clock seconds] -format "%b %d %Y"]

set var(BINDIR) /usr/local/bin
set var(COMPILE) g++
set var(DEBUG) {-DNDEBUG}
set var(OBJS) {$(SCIDOBJS)}
set var(THREADS) {-pthread}
set var(OPTIMIZE) {-std=c++20 -O3 -fno-rtti -fno-exceptions}
set var(PROFILE) {}
set var(LIBS) {}
set var(SCIDFLAGS) {}
set var(SHAREDIR) /usr/local/share/scidCommunity
set var(TCL_VERSION) 8.6 ;# fallback if auto-detection finds nothing
set var(WARNINGS) "-Wall -Wno-deprecated-declarations"
set var(MAKEFILE) Makefile.conf
set var(INSTALL) {install_scid install_engines}

set defaultVar(TCL_INCLUDE) {/usr/include}

# findDir:
#    Returns the first directory in the list "path" that contains a
#    readable file matching the wildcard pattern "f".
#    If exp is provided, the directory of the first such file that also
#    has a line containing the regular expression "exp" is returned. If
#    none of the found files contains the expression, the first file
#    found is returned.
#
proc findDir {f path {exp ""}} {
    set subpath {}
    foreach d $path {
        lappend subpath "$d"
        foreach subdir [glob -nocomplain -type {d  r} -directory "$d" *] {
            lappend subpath "$subdir"
        }
    }

    set best ""
    foreach dir $subpath {
        set p [file join $dir $f]
        if {![catch {glob $p}]} {
          if {$best == ""} { set best $p }
            if {$exp != ""} {
              if {[catch {exec grep -c $exp $p}] == 0} { return $dir } else {
                # puts "$p skipped, not right version"
              }
            } else {
                return $dir 
            }
        }
    }
    return $best
}

# findTclTkPathsDarwin:
#    Set-up paths using frameworks. Since frameworks are quite
#    different, the UNIX search logic is not sharable:
proc findTclTkPathsAqua {} {
    global var
    set frameworkPaths "\
        [ file nativename ~/Library/Frameworks ] \
        /Library/Frameworks \
        /System/Library/Frameworks \
    "
    set fw_path ""
    # Find the path to the Tcl and Tk frameworks:
    foreach F $frameworkPaths {
        set both 1
        foreach fw {"Tcl" "Tk"} {
            # Both Tcl.framework and Tk.framework must be present:
            if {! [file exists "$F/$fw.framework/Versions/$var(TCL_VERSION)"]} {
                set both 0
                break
            }
        }
        if {$both == 1} {
            # $F has both frameworks, continue:
            set fw_path $F
            break
        }
    }
    if {$both == 0} {
        # Couldn't find Tcl and Tk frameworks, keep searching the "unix" way:
        return 0
    }
    if {! [info exists var(TCL_INCLUDE)]} {
        set var(TCL_INCLUDE) "-F$fw_path/Tcl.framework -F$fw_path/Tk.framework"
    }
    if {! [info exists var(TCL_LIBRARY)]} {
        set var(TCL_LIBRARY) {-framework Tcl}
    }
    puts "    Location of Tcl/Tk Aqua frameworks: $fw_path"
    return 1
}

# findTclTkPaths:
#    Finds all details of the Tcl/Tk installation.
#    Returns 1 on success, 0 on failure.
#
proc findTclTkPaths {} {
    global tclv tclv_nodot var
    set success 1
    array set opt {}
    if {"$::tcl_platform(os)" == "Darwin" && [findTclTkPathsAqua]} {
        # Frameworks found, stop looking for paths:
        return 1
    }

    # headerPath: List of possible locations for tcl.h
    set headerPath {
        /usr/local/include
        /usr/include
        /usr/local/tcl/include
        /usr/X11/include
        /usr/X11R6/include
        /usr/local/X11/include
        /opt/tcltk/include
        /usr/X11R/include
    }

    # libraryPath: List of possible locations of Tcl/Tk library.
    set libraryPath {
        /usr/lib
        /usr/lib64
        /usr/local/tcl/lib
        /usr/local/lib
        /usr/X11R6/lib
        /opt/tcltk/lib
    }

    # Try to add tcl_library and auto_path values to libraryPath,
    # in case the user has a non-standard Tcl/Tk library location:
    if {[info exists ::tcl_library]} {
        lappend headerPath \
            [file join [file dirname [file dirname $::tcl_library]] include]
        lappend libraryPath [file dirname $::tcl_library]
        lappend libraryPath $::tcl_library
    }
    if {[info exists ::auto_path]} {
        foreach name $::auto_path {
            lappend libraryPath $name
        }
    }

    if {! [info exists var(TCL_INCLUDE)]} {
        puts -nonewline {    Location of "tcl.h": }
        set opt(tcl_h) [findDir "tcl.h" $headerPath "TCL_VERSION.*$tclv"]
        if {$opt(tcl_h) == ""} {
            puts "not found"
            set success 0
            set opt(tcl_h) "$::defaultVar(TCL_INCLUDE)"
        } else {
            puts $opt(tcl_h)
        }
    }

    set opt(tcl_lib) ""

    if {! [info exists var(TCL_LIBRARY)]} {
        puts -nonewline "    Location of Tcl $tclv library: "
        set opt(tcl_lib) [findDir "libtcl${tclv}.*" $libraryPath]
        if {$opt(tcl_lib) == ""} {
            set opt(tcl_lib) [findDir "libtcl${tclv_nodot}.*" $libraryPath]
            if {$opt(tcl_lib) == ""} {
                set opt(tcl_lib) [findDir "tcl${tclv_nodot}.*" $libraryPath]
            }
            if {$opt(tcl_lib) == ""} {
                puts "not found"
                set success 0
                set opt(tcl_lib_file) "tcl"
            } else {
                set opt(tcl_lib_file) "tcl${tclv_nodot}"
                puts $opt(tcl_lib)
            }
        } else {
            set opt(tcl_lib_file) "tcl\$(TCL_VERSION)"
            puts $opt(tcl_lib)
        }
    }

    # Search for Tk library in the same location as Tcl
    set opt(tk_lib) ""
    set opt(tk_lib_file) ""
    
    if {! [info exists var(TCL_LIBRARY)]} {
        puts -nonewline "    Location of Tk $tclv library: "
        # Tcl 9.0+ uses combined libtcl9tk9.0.so
        set tclv_major [lindex [split $tclv .] 0]
        if {$tclv_major >= 9} {
            # Only check for combined library in Tcl 9.0+
            set opt(tk_lib) [findDir "libtcl${tclv_major}tk${tclv}.*" $libraryPath]
            if {$opt(tk_lib) != ""} {
                set opt(tk_lib_file) "tcl${tclv_major}tk${tclv}"
                puts "$opt(tk_lib) (combined Tcl/Tk library)"
            }
        }
        
        if {$opt(tk_lib) == ""} {
            # Separate Tk library (Tcl 8.6 and older, or fallback)
            set opt(tk_lib) [findDir "libtk${tclv}.*" $libraryPath]
            if {$opt(tk_lib) == ""} {
                set opt(tk_lib) [findDir "libtk${tclv_nodot}.*" $libraryPath]
                if {$opt(tk_lib) == ""} {
                    set opt(tk_lib) [findDir "tk${tclv_nodot}.*" $libraryPath]
                }
                if {$opt(tk_lib) == ""} {
                    puts "not found (will try -ltk\$(TCL_VERSION))"
                    set opt(tk_lib_file) "tk\$(TCL_VERSION)"
                } else {
                    set opt(tk_lib_file) "tk${tclv_nodot}"
                    puts $opt(tk_lib)
                }
            } else {
                set opt(tk_lib_file) "tk\$(TCL_VERSION)"
                puts $opt(tk_lib)
            }
        }
    }

    if {! [info exists var(TCL_INCLUDE)]} {
        set var(TCL_INCLUDE) "-I$opt(tcl_h)"
    }
    if {! [info exists var(TCL_LIBRARY)]} {
        set var(TCL_LIBRARY) "-L$opt(tcl_lib) -l$opt(tcl_lib_file) -l$opt(tk_lib_file)"
    }
    return $success
}

# detectTclHeaderVersions:
#    Scan header directories for tcl.h and extract version numbers.
#
proc detectTclHeaderVersions {} {
    set headerPath {
        /usr/local/include
        /usr/include
        /usr/local/tcl/include
        /usr/X11/include
        /usr/X11R6/include
        /usr/local/X11/include
        /opt/tcltk/include
        /usr/X11R/include
    }
    set versions {}
    foreach dir $headerPath {
        foreach f [glob -nocomplain [file join $dir tcl.h]] {
            if {[catch {set fh [open $f r]}]} continue
            while {[gets $fh line] >= 0} {
                if {[regexp {TCL_VERSION\s+("?)(\d+\.\d+)\1} $line -> _ ver]} {
                    lappend versions $ver
                }
            }
            close $fh
        }
    }
    return $versions
}

# detectTclLibVersions:
#    Scan library directories for Tcl shared libraries and extract version numbers.
#
proc detectTclLibVersions {} {
    set searchPath {
        /usr/local/include
        /usr/include
        /usr/local/tcl/include
        /usr/X11/include
        /usr/X11R6/include
        /usr/local/X11/include
        /opt/tcltk/include
        /usr/X11R/include
        /usr/lib
        /usr/lib64
        /usr/local/tcl/lib
        /usr/local/lib
        /usr/X11R6/lib
        /opt/tcltk/lib
    }
    if {[info exists ::tcl_library]} {
        lappend searchPath [file dirname $::tcl_library]
    }
    set versions {}
    foreach dir $searchPath {
        foreach f [glob -nocomplain [file join $dir libtcl*]] {
            if {[regexp {libtcl(\d+\.\d+)} [file tail $f] -> ver]} {
                lappend versions $ver
            } elseif {[regexp {libtcl(\d+)} [file tail $f] -> ver]} {
                lappend versions $ver
            }
        }
    }
    return $versions
}

# detectLatestTclVersion:
#    Find the highest Tcl/Tk version available on the system.
#
proc detectLatestTclVersion {} {
    set versions {}
    foreach v [detectTclHeaderVersions] { dict set versions $v 1 }
    foreach v [detectTclLibVersions]    { dict set versions $v 1 }
    set vlist [dict keys $versions]
    if {[llength $vlist] == 0} { return "" }
    set vlist [lsort -decreasing $vlist]
    return [lindex $vlist 0]
}

# writeMakefile:
#    Creates the Makefile using Makefile.conf and the configured
#    settings.
#    Also creates tcl/config.tcl
#
proc writeMakefile {{type ""}} {
    global var
    set success [findTclTkPaths]

    if {[catch {set from [open $var(MAKEFILE) r]}]} {
       puts "Error opening file for reading: $var(MAKEFILE)"
       exit 1
    }
    puts "    Using $var(MAKEFILE)."

    if {[catch {set to [open "Makefile" w]}]} {
       puts "Error opening file for writing: Makefile"
       exit 1
    }

    set line [gets $from]
    while {1} {
        set line [gets $from]
        if {[eof $from]} { break }
        foreach sub [array names var] {
            set first [string first "@$sub@" $line]
            if {$first >= 0} {
                set last [expr $first + [string length $sub] + 1]
                set pre [string range $line 0 [expr $first - 1]]
                set post [string range $line [expr $last + 1] end]
                set line $pre
                append line $var($sub)
                append line $post
            }
        }
        if {[string compare "!" [string index $line 0]]} {
            puts $to $line
        }
    }

    close $from
    close $to

    # Summary info
    if {$success} {
        puts {The Makefile configured for your system was written.}
        puts {Now just type "make" to compile ScidCommunity.}
    } else {
        puts {Not all settings could be determined!}
        puts {You may need to edit it before you can compile ScidCommunity.}
    }
}


# usage:
#     Explains the usage of this script, then exits
#
proc usage {} {
    puts {Valid options are:}
    puts {  BINDIR       The location for ScidCommunity executables for "make install".}
    puts {  COMPILE      Your C++ compiler. Default: "g++".}
    puts {  DEBUG        Debugging flags. Defaut: "-DNDEBUG"}
    puts {  LANGUAGES    Multi-language support. Use LANGUAGES="" for English only.}
    puts {  LINK         Your C++ linker. Default: "g++".}
    puts {  OPTIMIZE     C++ optimizations. Default: "-O3 -fno-rtti -fno-exceptions".}
    puts {  PROFILE      Used for profiling the source code. Default: none.}
    puts {  SCIDFLAGS    Scid customization flags. Default: none.}
    puts {  SHAREDIR     Location of ScidCommunity data (ECO, spelling) files for "make install".}
    puts {  TCL_INCLUDE  Compiler directives for including Tcl.}
    puts {  TCL_LIBRARY  Compiler directives for linking Tcl.}
    puts {  TCL_VERSION  Your Tcl/Tk version. Example: TCL_VERSION="8.3".}
    puts {  THREADS      Flags for c++11 threads. Default: "-pthread".}
    puts {  WARNINGS     C++ compiler warnings. Default: "-Wall".}
    puts {  MAKEFILE     Makefile configuration file. Default: "Makefile.conf".}
    puts {}
    puts {  This configure program should find the proper values for the options}
    puts {  starting with TCL_ and TK_ automatically, so you should only use those}
    puts {  options if it fails to find your Tcl/Tk installation.}
    puts {}
    puts {Example usage:}
    puts {  ./configure LANGUAGES="tcl/francais.tcl" BINDIR="/usr/local/bin"}
    puts {}
    exit 1
}

########################################

puts "configure: Makefile configuration program for ScidCommunity"

# Parse command-line arguments:
set default 0
set user_set_tcl_version 0

foreach arg $argv {
    if {![string compare "default" $arg]} {
        set default 1
    } else {
        set temp_idx [string first "=" $arg]
        if {$temp_idx > 0} {
            set temp_var [string range $arg 0 [expr $temp_idx - 1]]
            set temp_value [string range $arg [expr $temp_idx + 1] end]
            set var($temp_var) $temp_value
            if {$temp_var eq "TCL_VERSION"} { set user_set_tcl_version 1 }
        } else {
            puts "Invalid argument: $arg"
            usage
        }
    }
}

if {![info exists var(LINK)]} { set var(LINK) "$var(COMPILE)" }

# Auto-detect Tcl/Tk version unless user explicitly set TCL_VERSION:
if {!$user_set_tcl_version} {
    set detected [detectLatestTclVersion]
    if {$detected ne ""} {
        set var(TCL_VERSION) $detected
        puts "    Auto-detected Tcl/Tk version: $detected"
    } else {
        puts "    No Tcl/Tk installation found, using default: $var(TCL_VERSION)"
    }
}

if {$default} {
    writeMakefile default
    exit 0
}

if {[file readable "Makefile"]} {
    puts {    Renaming "Makefile" to "Makefile.bak"}
    catch {file rename -force "Makefile" "Makefile.bak"}
}

set tclv $var(TCL_VERSION)
set tclv_nodot [expr round($tclv * 10)]

puts "    Tcl/Tk version: $tclv"
puts "    Your operating system is: $tcl_platform(os) $tcl_platform(osVersion)"

writeMakefile

exit 0

### End of configure script ###
