#!/bin/bash

# Useful functions
UMASK=""
set_umask () {
	[ "$UMASK" == "" ] && UMASK="$(umask)"
	export UMASK
	umask 002
}

restore_umask () {
	umask $UMASK >/dev/null
}

# set up general environment
WORKDIR=$(mktemp -d /tmp/$(basename $0).XXXXXXXXXX)
LOCKS=()

# check if messages are to be printed using color
unset ALL_OFF BOLD BLUE GREEN RED YELLOW
if [[ -t 2 ]]; 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)"
fi
readonly ALL_OFF BOLD BLUE GREEN RED YELLOW

plain() {
	local mesg=$1; shift
	printf "${BOLD}    ${mesg}${ALL_OFF}\n" "$@"
}

msg() {
	local mesg=$1; shift
	printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@"
}

msg2() {
	local mesg=$1; shift
	printf "${BLUE}  ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@"
}

warning() {
	local mesg=$1; shift
	printf "${YELLOW}==> WARNING:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2
}

error() {
	local mesg=$1; shift
	printf "${RED}==> ERROR${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2
}

##
#  usage : in_array( $needle, $haystack )
# return : 0 - found
#          1 - not found
##
in_array() {
	local needle=$1; shift
	[[ -z $1 ]] && return 1 # Not Found
	local item
	for item in "$@"; do
		[[ $item = $needle ]] && return 0 # Found
	done
	return 1 # Not Found
}

script_lock() {
	local LOCKDIR="$TMPDIR/.scriptlock.$(basename $0)"
	if ! mkdir "$LOCKDIR" >/dev/null 2>&1 ; then
		local _owner="$(/usr/bin/stat -c %U $LOCKDIR)"
		error "Script $(basename $0) is already locked by $_owner."
		exit 1
	else
		set_umask
		return 0
	fi
}

script_unlock() {
	local LOCKDIR="$TMPDIR/.scriptlock.$(basename $0)"
	if [ ! -d "$LOCKDIR" ]; then
		warning "Script $(basename $0) was not locked!"
		restore_umask
		return 1
	else
		rmdir "$LOCKDIR"
		restore_umask
		return 0
	fi
}

cleanup() {
	local l
	local repo
	local arch

	trap - EXIT INT QUIT TERM
	for l in ${LOCKS[@]}; do
		repo=${l%.*}
		arch=${l#*.}
		if [ -d "$TMPDIR/.repolock.$repo.$arch" ]; then
			msg "Removing left over lock from [${repo}] (${arch})"
			repo_unlock $repo $arch
		fi
	done
	if [ -d "$TMPDIR/.scriptlock.$(basename $0)" ]; then
		msg "Removing left over lock from $(basename $0)"
		script_unlock
	fi
	rm -rf "$WORKDIR"
	[ "$1" ] && exit $1
}

abort() {
	msg 'Aborting...'
	cleanup 0
}

die() {
	error "$*"
	cleanup 1
}

trap abort INT QUIT TERM HUP
trap cleanup EXIT


#repo_lock <repo-name> <arch> [timeout]
repo_lock () {
	local LOCKDIR="$TMPDIR/.repolock.$1.$2"
	local _count
	local _trial
	local _timeout
	local _lockblock
	local _owner

	if [ $# -eq 2 ]; then
		_lockblock=true
		_trial=0
	elif [ $# -eq 3 ]; then
		_lockblock=false
		_timeout=$3
		let _trial=$_timeout/$LOCK_DELAY
	fi

	_count=0
	while [ $_count -le $_trial ] || $_lockblock ; do
		if ! mkdir "$LOCKDIR" >/dev/null 2>&1 ; then
			_owner="$(/usr/bin/stat -c %U $LOCKDIR)"
			warning "Repo [${1}] (${2}) is already locked by $_owner. "
			msg2 "Retrying in $LOCK_DELAY seconds..."
		else
			LOCKS[${#LOCKS[*]}]="$1.$2"
			set_umask
			return 0
		fi
		sleep $LOCK_DELAY
		let _count=$_count+1
	done

	error "Repo [${1}] (${2}) is already locked by $_owner. Giving up!"
	return 1
}

repo_unlock () { #repo_unlock <repo-name> <arch>
	local LOCKDIR="$TMPDIR/.repolock.$1.$2"
	if [ ! -d "$LOCKDIR" ]; then
		warning "Repo lock [${1}] (${2}) was not locked!"
		restore_umask
		return 1
	else
		rmdir "$LOCKDIR"
		restore_umask
		return 0
	fi
}

# usage: _grep_pkginfo pkgfile pattern
_grep_pkginfo() {
	local _ret

	_ret="$(/usr/bin/bsdtar -xOqf "$1" .PKGINFO | /bin/grep -m 1 -E "$2" | /bin/sed 's|\w*\s*=\s*\(.*\)|\1|')"
	echo "$_ret"
}


# Get the package base or name as fallback
getpkgbase() {
	local _base

	_base="$(_grep_pkginfo "$1" "^pkgbase")"
	if [ -z "$_base" ]; then
		getpkgname "$1"
	fi

	echo "$_base"
}

# Get the package name
getpkgname() {
	local _name

	_name="$(_grep_pkginfo "$1" "^pkgname")"
	if [ -z "$_name" ]; then
		error "Package '$1' has no pkgname in the PKGINFO. Fail!"
		exit 1
	fi

	echo "$_name"
}

# Get the pkgver-pkgrel of this package
getpkgver() {
	local _ver

	_ver="$(_grep_pkginfo "$1" "^pkgver")"
	if [ -z "$_ver" ]; then
		error "Package '$1' has no pkgver in the PKGINFO. Fail!"
		exit 1
	fi

	echo "$_ver"
}

getpkgarch() {
	local _ver

	_ver="$(_grep_pkginfo "$1" "^arch")"
	if [ -z "$_ver" ]; then
		error "Package '$1' has no arch in the PKGINFO. Fail!"
		exit 1
	fi

	echo "$_ver"
}

getpkgfile() {
	if [[ ${#} -ne 1 ]]; then
		error 'No canonical package found!'
		exit 1
	elif [ ! -f "${1}" ]; then
		error "Package ${1} not found!"
		exit 1
	fi

	echo ${1}
}

getpkgfiles() {
	local f
	if [ ! -z "$(echo ${@%\.*} | sed "s/ /\n/g" | sort | uniq -D)" ]; then
		error 'Duplicate packages found!'
		exit 1
	fi

	for f in ${@}; do
		if  [ ! -f "${f}" ]; then
			error "Package ${f} not found!"
			exit 1
		fi
	done

	echo ${@}
}

check_pkgfile() {
	local pkgfile=$1

	local pkgname="$(getpkgname ${pkgfile})"
	[ $? -ge 1 ] && return 1
	local pkgver="$(getpkgver ${pkgfile})"
	[ $? -ge 1 ] && return 1
	local pkgarch="$(getpkgarch ${pkgfile})"
	[ $? -ge 1 ] && return 1

	in_array "${pkgarch}" ${ARCHES[@]} 'any' || return 1

	if echo "$(basename ${pkgfile})" | grep -q "${pkgname}-${pkgver}-${pkgarch}"; then
		return 0
	else
		return 1
	fi
}

check_pkgsvn() {
	local pkgfile="${1}"
	local pkgbase="$(getpkgbase $pkg)"
	[ $? -ge 1 ] && return 1
	local pkgarch="$(getpkgarch ${pkgfile})"
	[ $? -ge 1 ] && return 1
	local repo="${2}"

	in_array "${repo}" $(get_repos_for_host) || return 1

	if [ ! -f "${WORKDIR}/pkgbuilds/${pkgbase}" ]; then
		mkdir -p "${WORKDIR}/pkgbuilds"
		svn export -q "${SVNREPO}/${pkgbase}/repos/${repo}-${pkgarch}/PKGBUILD" \
			"${WORKDIR}/pkgbuilds/${pkgbase}" >/dev/null
		[ $? -ge 1 ] && return 1
	fi

	local pkgver="$(. "${WORKDIR}/pkgbuilds/${pkgbase}"; echo "${pkgver}-${pkgrel}")"
	[ -z "${pkgver}" ] && return 1
	if echo "$(basename ${pkgfile})" | grep -q "${pkgver}-${pkgarch}"; then
		return 0
	else
		return 1
	fi
}

get_repos_for_host() {
	if [ "$(hostname)" = "sigurd" ]; then
		echo "community community-testing"
	else
		echo "core extra testing staging"
	fi
}

get_pkgpool_for_host() {
	if [ "$(hostname)" = "sigurd" ]; then
		echo "pool/community"
	else
		echo "pool/packages"
	fi
}

#usage: chk_license ${license[@]}"
chk_license() {
	local l
	local allowed
	for l in "$@"; do
		l="$(echo $l | tr '[:upper:]' '[:lower:]')"
		for allowed in ${ALLOWED_LICENSES[@]}; do
			allowed="$(echo $allowed | tr '[:upper:]' '[:lower:]')"
			if [ "$l" = "$allowed" ]; then
				return 0
			fi
		done
	done

	return 1
}

pkgname_from_src() {
	local tmp
	local a
	tmp=${1##*/}
	tmp=${tmp%$SRCEXT}
	for a in ${ARCHES[@]}; do
		tmp=${tmp%-$a}
	done
	tmp=${tmp%-any}
	echo ${tmp%-*-*}
}

pkgver_from_src() {
	local tmp
	local a
	tmp=${1##*/}
	tmp=${tmp%$SRCEXT}
	for a in ${ARCHES[@]}; do
		tmp=${tmp%-$a}
	done
	tmp=${tmp%-any}
	echo $tmp | sed 's|.*-\(.*-.*\)$|\1|g'
}

check_repo_permission() {
	local repo=$1

	in_array "${repo}" $(get_repos_for_host) || return 1

	[ -w "$FTP_BASE/$(get_pkgpool_for_host)" ] || return 1

	local arch
	for arch in ${ARCHES} any; do
		local w
		local ws=("${FTP_BASE}/${repo}/os/${arch}/"{,${repo}${DBEXT}})
		for w in ws; do
			[ -w ] || return 1
		done
	done

	return 0
}