#!/bin/bash

# Downloads and installs a self-contained Lua and LuaRocks.
# Supports Linux, macOS and MSYS2.
# Copyright (c) Pierre Chapuis, MIT Licensed.
# Latest stable version available at: https://loadk.com/localua.sh
# Maintained at: https://github.com/oploadk/localua

DEFAULT_LUA_V="5.4.8"
DEFAULT_LR_V="3.12.2"

set -e

usage () {
    {
        echo -e "USAGE: $0 [options] output-dir\n"
        echo -e "Options:"
        echo -e "  --lua VERSION       Lua version (default: $DEFAULT_LUA_V)"
        echo -e "  --luarocks VERSION  LuaRocks version (default: $DEFAULT_LR_V)"
        echo -e "  --no-compat         Disable LUA_COMPAT"
        echo -e "  --no-luarocks       Skip LuaRocks installation"
        echo -e "  --target TARGET     Makefile target (linux, macosx, msys, posix)"
        echo -e "  --build-dir DIR     Custom build directory"
    } >&2
    exit 1
}

LUA_V=""
LR_V=""
POSITIONAL=()

while [[ $# -gt 0 ]]; do
    case "$1" in
        --lua)
            LUA_V="$2"
            shift 2
            ;;
        --luarocks)
            LR_V="$2"
            shift 2
            ;;
        --no-compat)
            LOCALUA_NO_COMPAT=1
            shift
            ;;
        --no-luarocks)
            LOCALUA_NO_LUAROCKS=1
            shift
            ;;
        --target)
            LOCALUA_TARGET="$2"
            shift 2
            ;;
        --build-dir)
            LOCALUA_BUILD_DIRECTORY="$2"
            shift 2
            ;;
        -h|--help)
            usage
            ;;
        -*)
            >&2 echo "Unknown option: $1"
            usage
            ;;
        *)
            POSITIONAL+=("$1")
            shift
            ;;
    esac
done

# Handle positional arguments for backward compatibility:
# output-dir [lua-version] [luarocks-version]
ODIR="${POSITIONAL[0]}"
[ -z "$ODIR" ] && usage

[ -z "$LUA_V" ] && LUA_V="${POSITIONAL[1]}"
[ -z "$LUA_V" ] && LUA_V="$DEFAULT_LUA_V"

[ -z "$LR_V" ] && LR_V="${POSITIONAL[2]}"
[ -z "$LR_V" ] && LR_V="$DEFAULT_LR_V"

LUA_SHORTV="$(echo "$LUA_V" | cut -c 1-3)"
LUA_SRCDIR="lua-${LUA_V}"
if [ "$LUA_V" = "pallene" ]; then
    LUA_SHORTV="5.4"
    LUA_SRCDIR="lua-internals"
elif [[ "$LUA_V" == *-rc* ]]; then
    LUA_SRCDIR="lua-$(echo "$LUA_V" | cut -d- -f1)"
fi

LUA_SHORTV2="$(echo "$LUA_SHORTV" | tr -d '.')"

PALLENE_ROCKSPEC="https://raw.githubusercontent.com/pallene-lang/pallene/master/pallene-dev-1.rockspec"

# Set build directory

BDIR="$LOCALUA_BUILD_DIRECTORY"
[ -z "$BDIR" ] && BDIR="$(mktemp -d /tmp/localua-XXXXXX)"

# Create output directory and get absolute path

mkdir -p "$ODIR"
>/dev/null pushd "$ODIR"
    ODIR="$(pwd)"
>/dev/null popd

# Download, unpack and build Lua and LuaRocks

if [ -z "$LOCALUA_TARGET" ]; then
    case "$(uname)" in
        Linux)
            LOCALUA_TARGET="linux";;
        Darwin)
            LOCALUA_TARGET="macosx";;
        MSYS*)
            LOCALUA_TARGET="msys";;
        *)
            LOCALUA_TARGET="posix";;
    esac
fi

download_lua () {
    if [ "$LUA_V" = "pallene" ]; then
        git clone --depth 1 "git@github.com:pallene-lang/lua-internals.git"
    else
        if [[ "$LUA_V" == *-rc* ]]; then
            curl "https://www.lua.org/work/lua-${LUA_V}.tar.gz" -O
        else
            curl "https://www.lua.org/ftp/lua-${LUA_V}.tar.gz" -O
        fi
        tar xf "lua-${LUA_V}.tar.gz"
    fi
}

cleanup_luarocks_isolation () {
    # See:
    # https://github.com/oploadk/localua/issues/3
    # https://github.com/oploadk/localua/issues/4

    echo "Cleaning up luarocks isolation..."

    >/dev/null pushd "$ODIR"
        # We must *not* be in the build directory, otherwise
        # it will take its config instead.

        rocks_dir="$("$ODIR/bin/luarocks" config rocks_dir)"
        lr_version="$("$ODIR/bin/luarocks" show luarocks --mversion)"
        lr_bin="$rocks_dir/luarocks/$lr_version/bin/luarocks"
        lr_config_file=$("$ODIR/bin/luarocks" config config_files.system.file)

        if [ ! -x "$lr_bin" ]; then
            >&2 echo -n "Could not cleanup luarocks isolation, "
            >&2 echo "executable $lr_bin not found."
            return 1
        fi

        if [ ! -f "$lr_config_file" ]; then
            >&2 echo -n "Could not cleanup luarocks isolation, "
            >&2 echo "config file $lr_config_file not found."
            return 1
        fi

        # Remove user tree from configuration file.
        sed -e '/name = "user"/d' "$lr_config_file" > "$BDIR/t"
        mv "$BDIR/t" "$lr_config_file"
    >/dev/null popd

    # Rebuild with the *local* Lua to avoid trash in wrapper scripts.
    "$ODIR/bin/lua" "$lr_bin" make --tree="$ODIR"
}

install_ptracer () {
    echo "Installing Pallene Tracer..."
    pushd "$BDIR"
        git clone https://www.github.com/pallene-lang/pallene-tracer --depth 1 --branch 0.5.0a
        pushd pallene-tracer
            make PREFIX="$ODIR" LUA_PREFIX="$ODIR" install
        popd
    popd
}

pushd "$BDIR"
    download_lua
    pushd "$LUA_SRCDIR"
        sed 's#"/usr/local/"#"'"$ODIR"'/"#' "src/luaconf.h" > "$BDIR/t"
        mv "$BDIR/t" "src/luaconf.h"
        if [ -n "$LOCALUA_NO_COMPAT" ]; then
            sed 's#-DLUA_COMPAT_5_.##' "src/Makefile" > "$BDIR/t"
            sed 's#-DLUA_COMPAT_ALL##' "$BDIR/t" > "src/Makefile"
        fi

        if [ "$LOCALUA_TARGET" = "msys" ]; then
            # shellcheck disable=SC2016,SC1003
            {
                echo
                echo 'msys:'
                echo -ne "\t"
                echo '$(MAKE) "LUA_A=lua'"$LUA_SHORTV2"'.dll" "LUA_T=lua.exe" \'
                echo -ne "\t"
                echo '"AR=$(CC) -shared -Wl,--out-implib,liblua.dll.a -o" "RANLIB=strip --strip-unneeded" \'
                echo -ne "\t"
                echo '"SYSCFLAGS=-DLUA_BUILD_AS_DLL -DLUA_USE_POSIX -DLUA_USE_DLOPEN" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe'
                echo -ne "\t"
                echo '$(MAKE) "LUAC_T=luac.exe" luac.exe'
            } >> "src/Makefile"

            make -C src "$LOCALUA_TARGET"
            make \
                TO_BIN="lua.exe luac.exe lua${LUA_SHORTV2}.dll" \
                TO_LIB="liblua.a liblua.dll.a" \
                INSTALL_TOP="$ODIR" install
        else
            make "$LOCALUA_TARGET"
            make INSTALL_TOP="$ODIR" install
        fi
    popd
    if [ -z "$LOCALUA_NO_LUAROCKS" ]; then
        curl -L "https://luarocks.org/releases/luarocks-${LR_V}.tar.gz" -O
        tar xf "luarocks-${LR_V}.tar.gz"
        pushd "luarocks-${LR_V}"
            ./configure --with-lua="$ODIR" --prefix="$ODIR" \
                        --lua-version="$LUA_SHORTV" \
                        --sysconfdir="$ODIR/etc" --force-config
            make bootstrap
            if [ -z "$LOCALUA_NO_LUAROCKS_ISOLATION_CLEANUP" ]; then
                cleanup_luarocks_isolation
            fi
        popd
        if [ "$LUA_V" = "pallene" ]; then
            install_ptracer
            "$ODIR/bin/luarocks" install "$PALLENE_ROCKSPEC" PTRACER_DIR="$ODIR"
        fi
    fi
popd

# Cleanup

rm -rf "$BDIR"
