abinfo "Setting up build environment ..."
OWTARGET="loongarch64-aosc-linux-gnuow"
LOLPREFIX="/opt/lol"

abinfo "Appending -O2 to CPPFLAGS to fix build ..."
export CPPFLAGS="${CPPFLAGS} -O2"

apply_source_patch_series() {
    local patch_path=$1
    local src_path=$2
    local package=$3
    local patch

    abinfo "Applying $package patches ..."
    while IFS= read -r patch; do
        abinfo "  Applying $patch ..."
        patch -p 1 --ignore-whitespace -d "$src_path" \
            < "$patch_path/$patch"
    done <"$patch_path/series"
}

glibc_source() {
    abinfo "Extracting Glibc sources ..."

    abinfo "Setting up Glibc source tree ..."
    abinfo "Unpacking Glibc source tar ..."
    mkdir -pv "$BLDDIR/src/glibc"
    tar \
        -C "$BLDDIR/src/glibc" \
        -xvJ \
        --strip-components=1 \
        -f "$SRCDIR/glibc.tar.xz"
    apply_source_patch_series \
        "$SRCDIR/autobuild/patches/glibc" \
        "$BLDDIR/src/glibc" \
        "Glibc"
}

libxcrypt_source() {
    abinfo "Extracting Libxcrypt sources ..."

    abinfo "Setting up Libxcrypt source tree ..."
    abinfo "Unpacking Libxcrypt source tar ..."
    mkdir -pv "$BLDDIR/src/libxcrypt"
    tar \
        -C "$BLDDIR/src/libxcrypt" \
        -xvJ \
        --strip-components=1 \
        -f "$SRCDIR/libxcrypt.tar.xz"
}

cflags_common="-O2 -g -ffile-prefix-map=$BLDDIR=."
ldflags_common="-Wl,-O1,--sort-common,--as-needed -Wl,-build-id=sha1"
libc_map="GLIBC_2.36=GLIBC_2.27,GLIBC_2.28"

gen_abi_list() {
    local lib=$1
    LC_ALL=C objdump --dynamic-syms "$lib" | \
        LC_ALL=C awk -f "$BLDDIR/src/glibc/scripts/abilist.awk"
}

glibc_build() {
    mkdir -pv "$BLDDIR/build/glibc"

    pushd "$BLDDIR/build/glibc"

    local debver=$PKGVER
    if [ "$PKGREL" != 0 ]; then
		debver+="-$PKGREL"
	fi

    cat > configparms <<EOF
install_root = \$(DESTDIR)
exec_prefix = \${prefix}
datarootdir = /usr/share
libdir = \${exec_prefix}/lib
slibdir = \${exec_prefix}/lib
extra_libdir = \${exec_prefix}/lib
rtlddir = /lib64
libexecdir = \${exec_prefix}/lib
rootsbindir = \${exec_prefix}/sbin
complocaledir = /usr/lib/locale
localtime-file = /etc/localtime
sysconfdir = $LOLPREFIX/etc
user-defined-trusted-dirs-pre = $LOLPREFIX/local/preload-lib $LOLPREFIX/support/lib/$OWTARGET
user-defined-trusted-dirs = $LOLPREFIX/local/lib $LOLPREFIX/lib/$OWTARGET $LOLPREFIX/lib

CFLAGS = -pipe $cflags_common
ASFLAGS = -pipe $cflags_common
BUILD_CFLAGS = $cflags_common
LDFLAGS = $ldflags_common
EOF

    env -i "TERM=$TERM" "PATH=$PATH" \
        "$BLDDIR/src/glibc/configure" \
            --target="$OWTARGET" \
            --host="$OWTARGET" \
            --disable-werror \
            --prefix="/usr" \
            --enable-obsolete-rpc \
            --with-pkgversion="LibLoL $debver" \
		    --with-bugurl="https://github.com/AOSC-Dev/liblol/issues"

    local pthread_map="GLIBC_2.27=GLIBC_2.0,GLIBC_2.2,GLIBC_2.2.1,GLIBC_2.2.2,GLIBC_2.2.3,GLIBC_2.2.4,GLIBC_2.2.6,GLIBC_2.3,GLIBC_2.3.2,GLIBC_2.3.3,GLIBC_2.3.4,GLIBC_2.4,GLIBC_2.5,GLIBC_2.6,GLIBC_2.7,GLIBC_2.8,GLIBC_2.9,GLIBC_2.10,GLIBC_2.11,GLIBC_2.12,GLIBC_2.13,GLIBC_2.14,GLIBC_2.15,GLIBC_2.16,GLIBC_2.17,GLIBC_2.18,GLIBC_2.19,GLIBC_2.20,GLIBC_2.21,GLIBC_2.22,GLIBC_2.23,GLIBC_2.24,GLIBC_2.25,GLIBC_2.26"

    for i in "libc.so.6" "linkobj/libc.so"; do
        env -i "MAKEFLAGS=$MAKEFLAGS" "TERM=$TERM" "PATH=$PATH" \
        make "$PWD/$i"

        "$BLDDIR/build/patchelf/src/patchelf" \
            --page-size "$(( 64 * 1024 ))" \
            --remap-symvers "$pthread_map" \
            --also-remap-verneed \
            "$i"

        "$BLDDIR/build/patchelf/src/patchelf" \
            --page-size "$(( 64 * 1024 ))" \
            --remap-symvers "$libc_map" \
            --also-remap-verneed \
            "$i"
    done

    local libs=( \
        "elf/ld.so.1" \
        "math/libm.so.6" \
        "resolv/libresolv.so.2" \
        "malloc/libc_malloc_debug.so.0" \
        "nptl_db/libthread_db.so.1" \
        "locale/libBrokenLocale.so.1" \
    )
    local merged_libs=( \
        "resolv/libanl.so.1" \
        "dlfcn/libdl.so.2" \
        "rt/librt.so.1" \
        "login/libutil.so.1" \
        "nis/libnsl.so.1" \
    )
    local dir
    local lib

    for i in "nptl/libpthread.so.0" "${libs[@]}" "${merged_libs[@]}"; do
        dir=$(dirname -- "$i")
        lib=$(basename -- "$i")
        env -i "MAKEFLAGS=$MAKEFLAGS" "TERM=$TERM" "PATH=$PATH" \
            make -C "$BLDDIR/src/glibc/$dir" \
                "$PWD/$i" \
                subdir="$dir" \
                ..=../ \
                objdir="$PWD"
    done

    abinfo "Patching Glibc libraries ..."

    "$BLDDIR/build/patchelf/src/patchelf" \
        --page-size "$(( 64 * 1024 ))" \
        --remap-symvers "$pthread_map" \
        --also-remap-verneed \
        "nptl/libpthread.so.0"

    for i in "${libs[@]}"; do
        "$BLDDIR/build/patchelf/src/patchelf" \
            --page-size "$(( 64 * 1024 ))" \
            --remap-symvers "$libc_map" \
            --also-remap-verneed \
            "$i"
    done

    abinfo "Generating a dummy ld-linux-loongarch-lp64d.so.1 ..."
    gcc -shared \
        -x c /dev/null \
        -o ld-linux-loongarch-lp64d.so.1 \
        -Wl,--version-script -Wl,ld.map \
        -nostdlib \
        $cflags_common \
        $ldflags_common

    abinfo "Installing libLoL runtime ..."
    mkdir -pv "$PKGDIR$LOLPREFIX/lib/$OWTARGET/"

    for i in "libc.so.6" "nptl/libpthread.so.0" \
             "ld-linux-loongarch-lp64d.so.1" \
             "${libs[@]}" "${merged_libs[@]}"; do
        install -Dvm644 "$i" \
            -t "$PKGDIR$LOLPREFIX/support/lib/$OWTARGET/"
    done

    abinfo "Generating abi info ..."
    mkdir -pv "$BLDDIR/build/abiversions/actual"

    for i in "libc.so.6" "nptl/libpthread.so.0" \
            "${libs[@]}" "${merged_libs[@]}"; do
        lib=$(basename -- "$i" | cut -d. -f1)
        gen_abi_list "$i" \
            > "$BLDDIR/build/abiversions/actual/$lib.abilist"
    done

    chmod -v +x \
        "$PKGDIR$LOLPREFIX/support/lib/$OWTARGET/ld.so.1" \
        "$PKGDIR$LOLPREFIX/support/lib/$OWTARGET/libc.so.6"

    mkdir -pv "$PKGDIR/usr/lib" #/lib64 is /usr/lib on AOSC
    ln -sfvr "$PKGDIR$LOLPREFIX/support/lib/$OWTARGET/ld.so.1" \
        "$PKGDIR/usr/lib/ld.so.1"
    popd
}

libxcrypt_build_core() {
    abinfo "Configuring Libxcrypt ..."
    env -i "TERM=$TERM" "PATH=$PATH" \
        "$BLDDIR/src/libxcrypt/configure" \
            --prefix="/usr" \
            --host="$OWTARGET" \
            --disable-werror \
            --enable-shared \
            --disable-xcrypt-compat-files \
            --enable-obsolete-api=glibc \
            CFLAGS="$cflags_common" \
            LDFLAGS="$ldflags_common"

    abinfo "Building Libxcrypt ..."
    rm -f crypt-symbol-vers.h crypt-symbol-vers.h.stamp
    env -i "MAKEFLAGS=$MAKEFLAGS" "TERM=$TERM" "PATH=$PATH" \
        make "libcrypt.la" "$@"

    if [ ! -f .libs/libcrypt.so.1 ]; then
        abdie "libcrypt.so.1 not generated"
    fi
}


libxcrypt_build() {
    mkdir -pv "$BLDDIR/build/libxcrypt"
    pushd "$BLDDIR/build/libxcrypt"


    autoreconf -fvs --install "$BLDDIR/src/libxcrypt"

    libxcrypt_build_core "SYMVER_FLOOR=GLIBC_2.27"

    abinfo "Patching Libxcrypt ..."

    "$BLDDIR/build/patchelf/src/patchelf" \
        --page-size "$(( 64 * 1024 ))" \
        --remap-symvers "$libc_map" \
        ".libs/libcrypt.so.1"

    "$BLDDIR/build/patchelf/src/patchelf" \
        --page-size "$(( 64 * 1024 ))" \
        --replace-needed "ld-linux-loongarch-lp64d.so.1" "ld.so.1" \
        ".libs/libcrypt.so.1"

    abinfo "Installing Libxcrypt ..."
    install -Dvm644 ".libs/libcrypt.so.1" \
        -t "$PKGDIR$LOLPREFIX/support/lib/$OWTARGET/"

    abinfo "Generating abi info ..."
    mkdir -pv "$BLDDIR/build/abiversions/actual"

    gen_abi_list ".libs/libcrypt.so.1" \
        > "$BLDDIR/build/abiversions/actual/libcrypt.abilist"
    popd

    abinfo "Rebuilding Libxcrypt with proper ABI ..."
    mkdir -pv "$BLDDIR/build/libxcrypt-normal"
    pushd "$BLDDIR/build/libxcrypt-normal"

    libxcrypt_build_core

    abinfo "Generating expected abi info ..."
    gen_abi_list ".libs/libcrypt.so.1" \
        > "$BLDDIR/build/libxcrypt-normal/libcrypt.abilist"
    popd

}

patchelf_build() {
    abinfo "Unpacking patchelf ..."
    mkdir -pv "$BLDDIR/src/patchelf"
    tar \
        -C "$BLDDIR/src/patchelf" \
        -xvz \
        --strip-components=1 \
        -f "$SRCDIR/patchelf.tar.gz"

    apply_source_patch_series \
        "$SRCDIR/autobuild/patches/patchelf" \
        "$BLDDIR/src/patchelf" \
        "patchelf"

    abinfo "Building patchelf ..."
    mkdir -pv "$BLDDIR/build/patchelf"
    pushd "$BLDDIR/build/patchelf"
    "$BLDDIR/src/patchelf/configure"
    make
    popd
}

install_hooks() {
    for i in postinst; do
        sed 's#@prefix@#'"$LOLPREFIX"'#g; s#@target@#'"$OWTARGET"'#g' \
            "$SRCDIR/autobuild/additional-files/hooks/$i.in" \
            > "$SRCDIR/autobuild/$i"
        chmod +x "$SRCDIR/autobuild/$i"
    done
}

check_abiversion() {
    abinfo "Generating expected ABI version ..."
    mkdir -pv "$BLDDIR/build/abiversions/ow"
    mkdir -pv "$BLDDIR/build/abiversions/exp"
    local merged_libs=(libanl libdl libpthread librt libutil)
    for i in "${merged_libs[@]}"; do
        cut -d" " -f 1 "$SRCDIR/autobuild/abiversions/$i.abilist" | uniq | sed 's/$/ __'$i'_version_placeholder F/' >"$BLDDIR/build/abiversions/ow/$i.abilist"
        cat "$SRCDIR/autobuild/abiversions/$i.abilist" >> "$BLDDIR/build/abiversions/ow/libc.abilist"
    done
    cat "$SRCDIR/autobuild/abiversions/libc.abilist" >> "$BLDDIR/build/abiversions/ow/libc.abilist"
    for i in "$SRCDIR/autobuild/abiversions/"*".abilist"; do
        local name=$(basename "$i")
        local filename="$BLDDIR/build/abiversions/ow/$name"
        if [ ! -f "$filename" ]; then
            cp "$i" "$filename"
        fi
    done
    for i in \
        "$BLDDIR/src/glibc/sysdeps/unix/sysv/linux/loongarch/lp64/"*".abilist" \
        "$BLDDIR/build/libxcrypt-normal/libcrypt.abilist" \
    ; do
        local name=$(basename "$i")
        local filename="$BLDDIR/build/abiversions/ow/$name"
        if [ ! -f "$filename" ]; then
            filename="/dev/null"
        fi
        cat "$i" "$filename" | sort | uniq >"$BLDDIR/build/abiversions/exp/$name"
    done
    for i in "$BLDDIR/build/abiversions/ow/"*".abilist"; do
        local name=$(basename "$i")
        local filename="$BLDDIR/build/abiversions/exp/$name"
        if [ ! -f "$filename" ]; then
            cp "$i" "$filename"
        fi
    done
    abinfo "Comparing ABI versions ..."
    local abi_error=""
    local abi_warn=""
    for i in "$BLDDIR/build/abiversions/exp/"*".abilist"; do
        local name=$(basename "$i")
        local lib=$(echo "$name" | cut -d. -f1)
        if [ ! -f "$BLDDIR/build/abiversions/actual/$name" ]; then
            abi_error+="Missing expected library: $lib"$'\n'
            continue
        fi
        local missing=$(comm -23 "$i" <(cat "$BLDDIR/build/abiversions/actual/$name" "$BLDDIR/build/abiversions/actual/libc.abilist" | sort | uniq))
        local extra=$(comm -13 "$i" "$BLDDIR/build/abiversions/actual/$name")
        if [ "" != "$missing" ]; then
            abi_error+="Missing symbols in $lib:"$'\n'
            abi_error+="$missing"$'\n'
        fi
        if [ "" != "$extra" ]; then
            abi_warn+="Extra symbols in $lib:"$'\n'
            abi_warn+="$extra"$'\n'
        fi
    done
    if [ -n "$abi_error" ]; then
        abdie "ABI check failed"$'\n'"$abi_error"
    else
        abinfo "ABI check passed"
        if [ -n "$abi_warn" ]; then
            abinfo "Warnings:"$'\n'"$abi_warn"
        fi
    fi
}

abinfo "Building patchelf..."
patchelf_build

abinfo "Building Glibc ..."
glibc_source
glibc_build

abinfo "Building Libxcrypt ..."
libxcrypt_source
libxcrypt_build

check_abiversion

abinfo "Making symlinks for system provided libs ..."
system_libs_prio=("/usr/lib/libstdc++.so.6")
for i in "${system_libs_prio[@]}"; do
    ln -sfv "$i" "$PKGDIR$LOLPREFIX/support/lib/$OWTARGET/"
done

abinfo "Installing package hooks ..."
install_hooks

abinfo "Adding glibc dependency ..."
cur_glibc_version=$(dpkg-query -W --showformat='${Version}' glibc)
if [ -z "$cur_glibc_version" ]; then
    abdie "Cannot find glibc version"
fi
abinfo "Current glibc version: $cur_glibc_version"
cur_glibc_epoch=""
if [[ "$cur_glibc_version" == *":"* ]]; then
    cur_glibc_epoch="$(echo "$cur_glibc_version" | cut -d: -f1)"
    abinfo "Current glibc epoch: $cur_glibc_epoch"
    cur_glibc_epoch+=":"
fi
PKGDEP+=" glibc>=${cur_glibc_epoch}2.36 glibc<<${cur_glibc_epoch}${__GLIBC_VER}.1~"

abinfo "Adding libxcrypt dependency ..."
cur_libxcrypt_version=$(dpkg-query -W --showformat='${Version}' libxcrypt)
if [ -z "$cur_libxcrypt_version" ]; then
    abdie "Cannot find libxcrypt version"
fi
abinfo "Current libxcrypt version: $cur_libxcrypt_version"
cur_libxcrypt_epoch=""
if [[ "$cur_libxcrypt_version" == *":"* ]]; then
    cur_libxcrypt_epoch="$(echo "$cur_libxcrypt_version" | cut -d: -f1)"
    abinfo "Current libxcrypt epoch: $cur_libxcrypt_epoch"
    cur_libxcrypt_epoch+=":"
fi
PKGBREAK+=" libxcrypt>=${cur_libxcrypt_epoch}${__LIBXCRYPT_VER}.1~"
