#!/bin/bash

# Created by jiaqiang.ye@lnxall.com
# Neccessary Function definitions
# 2024/04/14

# Note that `lnxbf stands for `LNXALL Build Function
# Function to create commonly unused directories under `target/install:
lnxbf_create_install_dirs() {
	# create packages directory
	mkdir -v -p "${FINSTALL_DIR}/packages"
	[ $? -ne 0 ] && return 1

	mkdir -v -p "${LNXDESTPFX}/bin" \
		"${LNXDESTPFX}/include" \
		"${LNXDESTPFX}/lib/lua" \
		"${LNXDESTPFX}/share/lua" \
		"${LNXDESTPFX}/lib/dyiot/bin" \
		"${LNXDESTPFX}/lib/dyiot/init.d" \
		"${LNXDESTPFX}/platspec" \
		"${LNXDESTPFX}/etc/config" \
		"${IPKG_LISTDIR}" \
		"${IPKG_INFODIR}"
	return $?
}

# global environment variables for `ipk generation
declare -r IPKG_VERFILE=version-for-ipkg
declare -r IPKG_LISTDIR="${FINSTALL_DIR}/ipkg-dirs"
declare -r IPKG_INFODIR="${FINSTALL_DIR}/ipkg-info"
declare -r LNX_APPCFG="${FTOPDIR}/tagman/select_app/appconfig.sh"

# call previously defined function, only once:
if [ -z "${LNXBF_DIRS_CREATED}" ] ; then
	lnxbf_create_install_dirs
	declare -x LNXBF_DIRS_CREATED=true
fi

# remove existing package directory
lnxbf_remove_pkgdir() {
	local pkgname="$1"
	[ -z "${pkgname}" ] && return 1

	local OUTDIRS=$(pkgdir-name "${IPKG_LISTDIR}" "${pkgname}")
	[ -z "${OUTDIRS}" ] && return 2

	local PREDIR="${PWD}"
	if cd "${IPKG_LISTDIR}" ; then
		echo "INFO: cd ${IPKG_LISTDIR} && rm -rf ${OUTDIRS}"
		rm -rf ${OUTDIRS}
	fi
	cd "${PREDIR}"
	return 0
}

# remove ipkg generation directory
lnxbf_package_clean() {
	local PKGNAME=""
	[ ! -d ipkg-dir ] && echo "Warning, \`ipkg-dir not found in $PWD"
	# get the name of package:
	if [ -f "ipkg-dir/CONTROL/control" ] ; then
		PKGNAME=$(grep -e "^Package: " ipkg-dir/CONTROL/control | awk '{print $2}')
	fi

	# remove package information
	if [ -n "${PKGNAME}" ] ; then
		lnxbf_remove_pkgdir "${PKGNAME}"
		# remove package-information related files:
		rm -f -v "${IPKG_INFODIR}/info/${PKGNAME}".* \
			"${IPKG_INFODIR}/${PKGNAME}.status"
	fi

	local tmpval=''
	for tmpval in Makefile makefile GNUmakefile ; do
		[ -f "${tmpval}" ] && make -f "${tmpval}" CC=${LNX_CC} clean
	done
	rm -rf ipkg-dir ./build_dir CMakeFiles CMakeCache.txt
	return 0
}

# should be called by ELF package generation function
lnxbf_ipkg_finalize() {
	local OLDDIR="$PWD"
	# remove existing staged files
	cd "${FSTAGING_DIR}"
	if [ $? -ne 0 ] ; then
		echo "Error, directory not found: \`${FSTAGING_DIR}."
		return 1
	fi
	rm -rf * # remove all files in \`${FSTAGING_DIR}
	echo "All files removed in directory: ${FSTAGING_DIR}"

	# go to `${IPKG_LISTDIR} directory
	cd "${IPKG_LISTDIR}"
	if [ $? -ne 0 ] ; then
		cd "${OLDDIR}"
		return 1
	fi

	local tmpdir=''
	for tmpdir in * ; do
		if [ -d "${tmpdir}" ] ; then
			echo "Assembling files from ${tmpdir} ..."
			cp -r -f -P --preserve=mode "${tmpdir}"/* "${FSTAGING_DIR}/"
		fi
	done

	mkdir -v -p "${FSTAGING_DIR}${LNX_PREFIX}/opkg"/{info,lists}
	if cd "${IPKG_INFODIR}" ; then
		local STFILE="${FSTAGING_DIR}${LNX_PREFIX}/opkg/status"
		echo "Creating status file: ${STFILE}"
		cat *.status > "${STFILE}"

		echo "Copy package info files..."
		cp -r -f ./info/* "${FSTAGING_DIR}${LNX_PREFIX}/opkg/info/"
	fi

	cd "${OLDDIR}"
	return 0
}

lnxbf_elf_generate() {
	local FWFILE="$1"
	if [ -z "${FWFILE}" ] ; then
		echo "Error, output Firmware ELF name not specified."
		return 1
	fi

	local OLDDIR="${PWD}"
	cd "${FSTAGING_DIR}${LNX_PREFIX}"
	[ $? -ne 0 ] && return 2

	# generate /lib/dyiot/issue for non-openwrt systems
	local major="$(($(date +%Y) - 2018))"
	local minor="$(date +%V%u)"
	local revision="$(date +%H%M)"
	echo "v2-${major}.${minor}.${revision}" > './lib/dyiot/issue'
	unset major minor revision # remove unused local variable definition

	# remove unwanted files
	rm -rf ./share/man ./share/doc ./share/licenses ./man

	cd "${FSTAGING_DIR}/opt" || return 3
	rm -rf -v *.tar *.tar.* # remove existing tarballs
	# backup binaries with debugging symbols:
	echo "INFO: Generating lnxall_app-symbols.tar.xz ..."
	tar --numeric-owner --owner=0 --group=0 \
		-cf lnxall_app-symbols.tar lnxall_app && xz -z -v -T 6 -4 lnxall_app-symbols.tar

	# then strip ELF binaries files
	if [ "${LNXOPT_NOSTRIP}" != "true" ] ; then
		strip-elf .
	else
		echo "INFO: skip stripping of ELF files in '${PWD}'"
	fi

	# next create tarball
	tar --numeric-owner --owner=0 --group=0 \
		-cf lnxall_app.tar lnxall_app && xz -z -v -T 6 -9 lnxall_app.tar
	if [ $? -ne 0 ] ; then
		rm -rf *.tar *.tar.*
		echo "Error, failed to create firmware tarball" 1>&2
		cd "${OLDDIR}"
		return 4
	fi

	# goto firmware upgrade package generation directory
	local FWUP_DIR="${FTOPDIR}/lnxall_core/fwupgrade"
	cd "${FWUP_DIR}" || {
		echo "Error, cannot goto directory \`${FWUP_DIR}"
		cd "${OLDDIR}"
		return 5
	}

	rm -rf *.bin *.sh *.elf *.out fwupgrade # remove existing files
	ln -sv "${FSTAGING_DIR}/opt/lnxall_app.tar.xz" firmware.bin && \
		ln -sv "${OLDDIR}/fwscript.sh" fwscript.sh
	if [ $? -ne 0 ] ; then
		echo "Error, failed to symlink for firmware and upgrade script" 1>&2
		cd "${OLDDIR}"
		return 6
	fi

	# invoke make to compile `fwupgrade package
	make TARGET_CC="${LNX_CC}" TARGET_CFLAGS="${LNX_CFLAGS}" TARGET_STRIP="${LNX_TCSTRIP}"
	if [ $? -ne 0 ] ; then
		rm -rf target *.bin *.sh *.out
		cd "${OLDDIR}"
		return 7
	fi

	mv -f -v fwupgrade "${FWFILE}" && ls -lh "${FWFILE}"
	# append signed certificate to the END of ELF file:
	${FTOPDIR}/scripts/append-verify-data.sh "${FWFILE}"
	cd "${OLDDIR}"
	return 0
}

# copy `ipkg-skeleton to `ipkg-dir and then modify CONTROL/control
# The first argument is the package name, such as libmodbus
# The second argument is the package version, such as 2.1.2
lnxbf_ipkg_init() {
	# check package name string first
	local PKGNAME="$1"
	if [ -z "${PKGNAME}" ] ; then
		echo "Error, NAME_STRING not specified." 1>&2
		return 1
	fi

	# second, check version string
	local VERSION="$2"
	if [ -z "${VERSION}" ] ; then
		echo "Error, VERSION_STRING not specified." 1>&2
		return 2
	fi

	# check for `ipkg-skeleton directory
	if [ ! -d ipkg-skeleton ] ; then
		echo "Error, directory not found: \`ipkg-skeleton" 1>&2
		return 3
	fi

	# remove existing directory:
	rm -rf ipkg-dir
	lnxbf_remove_pkgdir "${PKGNAME}"

	# copy `ipkg-skeleton as `ipkg-dir
	cp -r -P --preserve=mode ipkg-skeleton ipkg-dir
	[ $? -ne 0 ] && return 4

	# control script must exist:
	if [ ! -f "ipkg-dir/CONTROL/control" ] ; then
		echo "Error, control not found." 1>&2
		return 5
	fi

	# modify `IPK_VERSION_STR and `IPK_ARCH_STR in CONTROL/control
	sed -i "ipkg-dir/CONTROL/control" -e "s/IPK_NAME_STR/${PKGNAME}/g" \
		-e "s/IPK_VERSION_STR/${VERSION}/g" \
		-e "s/IPK_ARCH_STR/${LNXPLAT_IPKG_ARCH}/g"
	return 0
}

# list files from a directory, and add
lnxbf_import_conf_files() {
	local condir="$1"
	local prefix="$2"

	if [ ! -d ipkg-dir/CONTROL ] ; then
		echo "Error, \`ipkg-dir directory not found."
		return 1
	fi
	if [ ! -d "${condir}" ] ; then
		echo "Error, conffiles directory not specified."
		return 2
	fi
	if [ -z "${prefix}" ] ; then
		echo "Error, conffiles prefix not specified."
		return 3
	fi

	echo "Importing conffiles from directory: ${condir} ..."
	find "${condir}" -print | xargs -n 1 -I '{WTH}' bash -c \
		"if [ ! -d \"{WTH}\" ] ; then \\
	echo \"${prefix}/\$(basename {WTH})\" | tee -a ipkg-dir/CONTROL/conffiles ; \\
fi"
	return 0
}

# add existing files to conffile list
lnxbf_add_conf_files() {
	local conpath="$1"
	if [ -z "${conpath}" ] ; then
		echo "Error, absolute path not specified for conffiles"
		return 1
	fi

	local OLDDIR="${PWD}"
	cd "ipkg-dir"
	if [ $? -ne 0 ] ; then
		echo "Error, cannot add conffiles: ${conpath}"
		cd "${OLDDIR}"
		return 2
	fi
	if [ ! -d CONTROL ] ; then
		echo "Error, CONTROL directory not found."
		cd "${OLDDIR}"
		return 3
	fi

	if [ ! -e ".${conpath}" ] ; then
		echo "Error, file not found: ${conpath}"
		cd "${OLDDIR}"
		return 4
	fi

	echo "INFO: adding new conffile: ${conpath} ..."
	if [ -f ".${conpath}" ] ; then
		echo "${conpath}" >> CONTROL/conffiles
	elif [ -d ".${conpath}" ] ; then
		find ".${conpath}" -print | xargs -n 1 -I '{WTH}' bash -c \
			"if [ ! -d \"{WTH}\" ] ; then \\
	echo '{WTH}' | cut -c2- | tee -a CONTROL/conffiles ; \\
fi"
	else
		echo "Error, file not found: ${conpath}"
		cd "${OLDDIR}"
		return 5
	fi

	cd "${OLDDIR}"
	return 0
}

# generate an IPK file
# The first argument is the name of package
lnxbf_ipkg_generate() {
	local VERSION="$2"
	local PKGNAME="$1"
	if [ -z "${PKGNAME}" -o -z "${VERSION}" ] ; then
		echo "Error, IPK package name or version not specified." 1>&2
		return 1
	fi

	local OLDDIR="$PWD"
	# check for package index string
	if [ -z "${FTPIDX}" ] ; then
		echo "Error, \`FTPIDX not defined, cannot generate IPK for ${PKGNAME}"
		return 2
	fi

	# check for directory for ipk generation, which must exist
	if [ ! -d ipkg-dir ] ; then
		echo "Error, \`ipkg-dir not found in : ${PWD}" 1>&2
		return 3
	fi
	dedup-line ipkg-dir/CONTROL/conffiles

	lnxbf_remove_pkgdir "${PKGNAME}"
	local pkgdir=$(printf "%s/%03d-%s" "${IPKG_LISTDIR}" "${FTPIDX}" "${PKGNAME}")
	echo "INFO: copying files with debugging symbols to ${pkgdir}"
	cp -r -f -P --preserve=mode ipkg-dir "${pkgdir}"

	# strip binaries with `${LNX_TCSTRIP}, errors ignored
	if [ "${LNXOPT_NOSTRIP}" != "true" ] ; then
		strip-elf "ipkg-dir"
	else
		echo "ELF binaries stripping skipped: ${PKGNAME}"
	fi

	ipkg-build ipkg-dir "${FINSTALL_DIR}/packages"
	if [ $? -ne 0 ] ; then
		echo "Error, failed to generate ipk in ${OLDDIR}" 1>&2
		return 4
	fi

	local tmpn=''
	# generate installed `opkg information
	mkdir -v -p "${IPKG_INFODIR}/info"
	for tmpn in ipkg-dir/CONTROL/* ; do
		if [ -e "${tmpn}" ] ; then
			local bfile=$(basename "${tmpn}")
			cp -r -v -f "${tmpn}" "${IPKG_INFODIR}/info/${PKGNAME}.${bfile}"
		fi
	done

	local pkglist="${IPKG_INFODIR}/info/${PKGNAME}.list"
	rm -v -f "${pkglist}" # remove existing list file
	cd ipkg-dir
	if [ $? -ne 0 ] ; then
		echo "Error, cannot goto directory \`ipkg-dir"
		return 5
	fi

	# generate a list of files installed
	echo "Generating files list for ${PKGNAME} ..."
	find ./ -print | grep -a -v -e '^\./CONTROL\>' | xargs -n 1 -I '{WTH}' bash -c \
		"if [ ! -d \"{WTH}\" ] ; then \\
	echo \"{WTH}\" | sed -e 's#^\\.##g' | tee -a \"${pkglist}\" ; \\
fi"

	# generate standalone package information
	echo "Package: ${PKGNAME}
Version: ${VERSION}
Depends:
Status: install user installed
Architecture: ${LNXPLAT_IPKG_ARCH}
Installed-Time: $(date +%s)
Auto-Installed: yes
" > "${IPKG_INFODIR}/${PKGNAME}.status"

	local retv=$?
	cd "${OLDDIR}"
	return ${retv}
}

function lnxbf_pymod_generate() {
	CC="${LNX_CC}" CFLAGS="${LNX_CFLAGS}" \
		LDSHARED="${LNX_CC} -shared -L${FINSTALL_DIR}${LNX_PREFIX} -Wl,-rpath=${LNX_PREFIX}/lib" \
		OPT='-DNDEBUG -g -fwrapv -O2 -Wall -fno-omit-frame-pointer' \
		_PYTHON_PROJECT_BASE=${FSOURCE_DIR}/python3/build_dir \
		_PYTHON_HOST_PLATFORM=linux-${LNXPLAT_ARCH} \
		PYTHONPATH=${FSOURCE_DIR}/python3/build_dir/build/lib.linux-arm-3.9:${PYTHONPATH}:../Lib \
		_PYTHON_SYSCONFIGDATA_NAME=_sysconfigdata \
		python3.9 setup.py build_ext --inplace
	local retv=$?
	rm -rf build
	return $retv
}
