#!/usr/bin/env bash

workspace=$(pwd -P)

export srcdir="${workspace}/src"
export pkgdir="${workspace}/pkg"

FAKE_PACKAGE=0
OVERRIDE_SOURCE=0
OVERRIDE_VERSION=""
ACTION=""


set -o functrace
set -o nounset
set -o errtrace
set -o errexit

function err_occur {
    err "Build aborted in: %s" "${ACTION}"
    err "Failed at $1: ${BASH_COMMAND}"
    err "Trace line number: %s" "$*"
}
trap 'err_occur "${LINENO}" "${BASH_LINENO[@]}"' ERR

# prefer terminal safe colored and bold text when tput is supported
if tput setaf 0 &>/dev/null; then
    ALL_OFF="$(tput sgr0)"
    BOLD="$(tput bold)"
    BLUE="${BOLD}$(tput setaf 4)"
    GREEN="${BOLD}$(tput setaf 2)"
    RED="${BOLD}$(tput setaf 1)"
    YELLOW="${BOLD}$(tput setaf 3)"
else
    ALL_OFF="\e[0m"
    BOLD="\e[1m"
    BLUE="${BOLD}\e[34m"
    GREEN="${BOLD}\e[32m"
    RED="${BOLD}\e[31m"
    YELLOW="${BOLD}\e[33m"
fi
readonly ALL_OFF BOLD BLUE GREEN RED YELLOW

function msg {
    local mesg=$1; shift
    printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@"
}
function msg2 {
    local mesg=$1; shift
    printf "${BLUE}  ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@"
}
function err {
    local mesg=$1; shift
    printf "${RED}  ->${ALL_OFF}${BOLD}${RED} ${mesg}${ALL_OFF}\n" "$@" >&2
}


function debian_control {
    local install_size="$(du --bytes --summarize ${pkgdir} | cut -d $'\t' -f 1)"

    cat << EOF | envsubst
Package: ${pkgname}
Version: ${pkgver}
Architecture: all
Maintainer: ${maintainer}
Installed-Size: ${install_size}
Description: ${pkgdesc}
EOF
}

function generate_deb {
    local tmpdir="$(mktemp --directory)"
    local data_tgz="${tmpdir}/data.tar.gz"
    local control_tgz="${tmpdir}/control.tar.gz"
    local debian_binary="${tmpdir}/debian-binary"

    find "${pkgdir}" -mindepth 1 -not -type d -not -path "${pkgdir}/DEBIAN*" -printf "%P\n" |
        tar -czf "${data_tgz}" -C "${pkgdir}" -T -

    find "${pkgdir}/DEBIAN" -mindepth 1 -not -type d -printf "%P\n" |
        tar -czf "${control_tgz}" -C "${pkgdir}/DEBIAN" -T -

    echo 2.0 > "${debian_binary}"

    ar r "${pkgname}${pkgver:+-}${pkgver}.deb" "${debian_binary}" "${control_tgz}" "${data_tgz}"

    rm -rf "${tmpdir}"
}

function url_type {
    if [[ "${url}" == git+* ]] ; then
        echo "git"
    elif [[ "${url}" == http://* ]] ; then
        echo "http"
    elif [[ "${url}" == https://* ]] ; then
        echo "https"
    elif [[ "${url}" == file//* ]]; then
        echo "file"
    elif [[ "${url}" != *://* ]] ; then
        echo "file"
    else
        err "Unkown url schema: %s" "${url}"
        err "Aborting..."
        exit 1
    fi
}

function create_soft_link {
    local src="$1"
    local tgt="$2"

    if [[ ! -e "${src}" ]] ; then
        err "soft_link src %s not exist" "${src}"
    fi

    if [[ -e "${tgt}" ]] ; then
        err "soft_link tgt %s already exist" "${tgt}"
    fi

    ln --symbolic "${src}" "${tgt}"
}

# $1 is relative path to file
# $2 is url/(relative path to materials)
function retrive_source_single {
    local file_name="$1"
    local url="$2"

    case "$(url_type "${url}")" in
        "git")
            if [[ -d "${file_name}" ]]; then
                [[ -d "${srcdir}/${file_name}" ]] || mkdir -p "${srcdir}/${file_name}"
                git --git-dir="${workspace}/${file_name}" --work-tree="${srcdir}/${file_name}" pull
            else
                git clone --bare "${url##git+}" "${workspace}/${file_name}"
            fi
            ;;
        "http"|"https")
            if (( ! OVERRIDE_SOURCE )) && [[ -f "${workspace}/${file_name}" ]] ; then
                msg2 "${file_name} already exists, skip download"
            else
                curl --location "${url}" --output "${workspace}/${file_name}"
            fi
            ;;
    esac
}

# $1 is relative path to file
# $2 is url/(relative path to materials)
function extract_source_single {
    local file_name="$1"
    local url="$2"
    local url_type="$(url_type "${url}")"

    case "${url_type}" in
        "git")
            local restore_source=HEAD
            git --git-dir "${workspace}/${file_name}" --work-tree="${srcdir}" restore --source="${restore_source}" .
            ;;
        "file") # for material files, just soft-link under src
            create_soft_link $(realpath "${workspace}/${url}") "${srcdir}/${file_name}"
            decompress_source_file "${srcdir}/${file_name}"
            ;;
        *)      # for downloaded files, just soft-link downloaded file under src
            create_soft_link $(realpath "${workspace}/${file_name}") "${srcdir}/${file_name}"
            decompress_source_file "${srcdir}/${file_name}"
            ;;
    esac
}

# $1 is real path to file
function decompress_source_file {
    local file="$1"
    case "${file}" in
        *.tar|*.tar.gz|*.tgz|*.tar.Z|*.tar.bz2|*.tbz2|*.tar.lz|*.tlz|*.tar.xz|*.txz|*.tar.zst)
            cmd="tar" ;;
        *.gz|*.z|*.Z)
            cmd="gzip" ;;
        *.bz2|*.bz)
            cmd="bzip2" ;;
        *.xz)
            cmd="xz" ;;
        *.zst|*.zstd)
            cmd="zstd" ;;
        *.zip)
            cmd="unzip" ;;
        *)
            msg2 "No need to decompress, skip %s" "${file}"
            return
    esac

    msg2 "Decompressing %s with %s" "${file}" "${cmd}"
    local res=0
    case "$cmd" in
        "tar")
            $cmd -xf "$file" --directory="${srcdir}" || res=$?
            ;;
        "unzip")
            $cmd "$file" -d "${srcdir}" || res=$?
            ;;
        *)
            rm -f -- "${file%.*}"
            $cmd -dcf -- "$file" > "${file%.*}" || res=$?
            ;;
    esac
}

function is_function {
    declare -F "$1" > /dev/null
}


##
## Here start the build logic
##

while (( "$#" >= 1 )); do
    case "$1" in
        -F)  FAKE_PACKAGE=1 ;;
        -OS) OVERRIDE_SOURCE=1 ;;
        -OV) OVERRIDE_VERSION="$2"; shift ;;
        *)   err "Unkown option $1"; break ;;
    esac
    shift
done

source "${workspace}/DEBBUILD"

# Run package and generate deb and exit
if (( FAKE_PACKAGE )); then
    # version passed directly from parent shell
    if [[ -n "${OVERRIDE_VERSION}" ]] ; then
        pkgver="${OVERRIDE_VERSION}"
    fi

    if is_function package; then
        ACTION="custom package"; msg "run function: %s..." "${ACTION}"
        package
    fi

    ACTION="generating deb package"; msg "${ACTION}..."
    generate_deb
    exit $?
fi

# check if all source is valid
for s in "${source[@]}"; do
    if ! grep "::" <<< "$s" > /dev/null; then
        err "source must contain \"::\" to specify file name"
        exit 1
    fi
done

ACTION="cleaning $$srcdir and $$pkgdir"; msg "${ACTION}..."
rm -rf "${srcdir}" "${pkgdir}"

mkdir -p "${srcdir}" "${pkgdir}"

ACTION="retrieving source"; msg "${ACTION}..."
for s in "${source[@]}"; do
    file_name="${s%%::*}"
    url="${s##*::}"

    retrive_source_single "${file_name}" "${url}"
done

ACTION="extracting source"; msg "${ACTION}..."
for s in "${source[@]}"; do
    file_name="${s%%::*}"
    url="${s##*::}"

    extract_source_single "${file_name}" "${url}"
done

if is_function build; then
    ACTION="custom build"; msg "run function: %s..." "${ACTION}"
    build
fi

ACTION="custom pkgver";
if [[ -n "${OVERRIDE_VERSION}" ]] ; then
    msg "Using override version: %s" "${OVERRIDE_VERSION}"
    pkgver="${OVERRIDE_VERSION}"
elif is_function pkgver; then
    msg "run function: %s..." "${ACTION}"
    pkgver="$(pkgver)"
fi

ACTION="generating control"; msg "${ACTION}..."
mkdir -p "${pkgdir}/DEBIAN"
echo 2 > "${pkgdir}/DEBIAN/compat"
debian_control > "${pkgdir}/DEBIAN/control"

function debian_hooks_warpper { is_function "$1" && "$1" > "$2" && chmod +x "$2" || true; }
debian_hooks_warpper debian_preinst  ${pkgdir}/DEBIAN/preinst
debian_hooks_warpper debian_postinst ${pkgdir}/DEBIAN/postinst
debian_hooks_warpper debian_prerm    ${pkgdir}/DEBIAN/prerm
debian_hooks_warpper debian_postrm   ${pkgdir}/DEBIAN/postrm

if is_function package; then
    # recursive call self to run package in fakeroot
    msg "Entering fakeroot environment..."
    fakeroot -- bash -$- "${BASH_SOURCE[0]}" -F -OV "${pkgver}"  "${ARGLIST[@]}" || exit $?
    msg "Leaving fakeroot environment..."
fi

msg "Builds has done"
