#!/bin/bash
set -e
set -x

if [ $EUID == 0 ]; then
    echo "This script must not be run as root" 1>&2
    exit 1
fi

DPUT_DIR="/tmp/dput"
CONF_FILE="/etc/xivo-build-tools/config"
GPG_KEY="1ECB941518AEB7EBED87CEE2E02C372DF5F94E5B"
PGK_FOLDER="/home/builder/packages"
DEB_FOLDER="/home/builder/build_from_sources"
DT=$(date '+%a, %d %b %Y %H:%M:%S %Z');
RELEASE_SIGNED="Release.gpg"

# source conffile
if [ -e $CONF_FILE ]; then
    . $CONF_FILE
else
    echo "$CONF_FILE: no such file" >&2
    exit 1
fi

export NAME="$DEBFULLNAME"
export EMAIL="$DEBEMAIL"

usage() {
    cat << EOF
You have to provide some informations to allow building:

    Usage: $(basename $0) [OPTION]

    Options:
        -d <distribution>: Distribution where to upload package (default: $DISTRIBUTION_DEFAULT)
        -b <branch_name>: Git branch to build from (default: $GIT_BRANCH_DEFAULT)
        -m <mirror>: Mirror where to upload package (default: $MIRROR_DEFAULT)
        -a: Build all packages
        -s: Build only sources packages
        -S: Build packages from sources
        -p <package>: Build only <package>
        -f: Force package build without checking for changes in repository
        -K: Build dahdi dkms module
        -v: Set xivo version
        -V: Set absolute package version
        -u: Use xivo version in filename (default: False)
        -h: show this help

EOF
    exit 0
}

hash_release_deb(){

    cat > Release <<EOF
Archive: xivo
Origin: Debian
Label: Local Debian Xivo Repository
Architecture: all
Date: $DT
MD5Sum:
EOF
printf ' '$(md5sum Packages | cut --delimiter=' ' --fields=1)' %16d Packages\n' \
  $(wc --bytes Packages | cut --delimiter=' ' --fields=1) >> Release
printf ' '$(md5sum Packages.gz | cut --delimiter=' ' --fields=1)' %16d Packages.gz' \
  $(wc --bytes Packages.gz | cut --delimiter=' ' --fields=1) >> Release
cat <<EOT >> Release

SHA256:
EOT
printf ' '$(sha256sum Packages | cut --delimiter=' ' --fields=1)' %16d Packages\n' \
  $(wc --bytes Packages | cut --delimiter=' ' --fields=1) >> Release
printf ' '$(sha256sum Packages.gz | cut --delimiter=' ' --fields=1)' %16d Packages.gz' \
  $(wc --bytes Packages.gz | cut --delimiter=' ' --fields=1) >> Release

}

update_release_deb() {
    cd $DEB_FOLDER/
    dpkg-scanpackages . /dev/null > Packages && gzip -9c Packages > Packages.gz
    hash_release_deb
    if [ -f $RELEASE_SIGNED ] ; then
        rm $RELEASE_SIGNED
    fi
    gpg --armor --detach-sign --output Release.gpg Release
}

hash_release_src(){

    cat > Release <<EOF
Archive: xivo
Origin: Debian
Label: Local Debian Xivo Repository
Architecture: all
Date: $DT
MD5Sum:
EOF
printf ' '$(md5sum Sources | cut --delimiter=' ' --fields=1)' %16d Sources\n' \
  $(wc --bytes Sources | cut --delimiter=' ' --fields=1) >> Release
printf ' '$(md5sum Sources.gz | cut --delimiter=' ' --fields=1)' %16d Sources.gz' \
  $(wc --bytes Sources.gz | cut --delimiter=' ' --fields=1) >> Release
cat <<EOT >> Release

SHA256:
EOT
printf ' '$(sha256sum Sources | cut --delimiter=' ' --fields=1)' %16d Sources\n' \
  $(wc --bytes Sources | cut --delimiter=' ' --fields=1) >> Release
printf ' '$(sha256sum Sources.gz | cut --delimiter=' ' --fields=1)' %16d Sources.gz' \
  $(wc --bytes Sources.gz | cut --delimiter=' ' --fields=1) >> Release

}

update_release_src() {
    cd $PGK_FOLDER/
    dpkg-scansources . /dev/null > Sources && gzip -9c Sources > Sources.gz
    hash_release_src
    if [ -f $RELEASE_SIGNED ] ; then
        rm $RELEASE_SIGNED
    fi
    gpg --armor --detach-sign --output Release.gpg Release
}

valid_package() {
    local package="$1"

    if [[ $PACKAGES =~ $package ]]; then
        return 0
    else
        echo "Package: $package does not exist, see $CONF_FILE for more informations" >&2
        exit -1
    fi
}

cd_to_git_dir_package() {
    local package="$1"

    local package_dir="${PACKAGES_DIR}/${package}"
    cd "${package_dir}"
    return 0
}

git_remote_branch_exist() {
    local remote_branch="$1"

    git branch -r | grep -q "origin/$remote_branch"
    return $?
}

git_init_local_branch() {
    local local_branch="$1"

    git show-ref --verify --quiet "refs/heads/$local_branch"
    return $?
}

get_mirror_package_version() {
    local package="$1"
    local mirror_uri="dists/${distribution}/main/binary-${ARCH}/Packages"

    case $package in
        "xivo-provisioning") local package="xivo-provd";;
        "xivo-dao") local package="xivo-libdao";;
        "xivo-dird") local package="xivo-libdird";;
    esac

    wget -qO- ${MIRROR_URL}/${mirror_uri} | grep -A 2 "^Package: $package" | grep '^Version:' | head -n1 | awk '{print $2}'
    return 0
}

get_source_version() {
    local package="$1"

    cd_to_git_dir_package $package

    # using date now instead of commit date
    #local git_timestamp=$(git log -1 --pretty='%ct')
    local timestamp=$(date -u +%Y%m%d.%H%M%S)
    local git_hash=$(git log -1 --pretty='%h')
    local computed="+${timestamp}.${git_hash}"

    if [ $use_version -eq 0 ]; then
        echo ${xivo_version}${computed}
    else
        echo ${computed}
    fi
    return 0
}

get_debian_version() {
    local package="$1"
    local source_version="$2"

    cd_to_git_dir_package $package

    local existing_version=$(get_mirror_package_version $package) || return 1

    if [ -z "$existing_version" ]; then
        # look for a package version
        local changelog=$(head -1 debian/changelog)
        if [[ "$changelog" =~ \((.*)\) ]] ; then
            existing_version=${BASH_REMATCH[1]}
        else
            echo "The first line of $dir/changelog is '$changelog' and does not match \\(.*\\)" >&2
            return 1
        fi
        # sanity check on the package name
        if ! [[ "$changelog" =~ ^$package ]] ; then
            echo "The first line of $dir/changelog is '$changelog' and does not start with $package" >&2
            return 2
        fi
    fi

    # extract the epoch, if any
    local epoch
    if [[ "$existing_version" =~ ([0-9]+:) ]] ; then
        epoch=${BASH_REMATCH[1]}
    fi

    local version=${epoch}${source_version}

    #if ! dpkg --compare-versions $existing_version le $version ; then
    #    echo "Existing version of XiVO is ($existing_version) that is greater or equal (according to dpkg --compare-versions) than the version computed ($version)" >&2
    #    return 4
    #fi

    echo $version
    return 0
}

generate_changelog() {
    local package="$1"
    local version="$2"

    echo "Generating changelog for package $package"

    cd_to_git_dir_package $package

    local message="$(git log -1 HEAD --pretty=format:%s)"
    if [ -n "$version" ]; then
        dch -v $version --distribution $distribution --force-distribution "$message"
	#dch --distribution $distribution --force-distribution "$message"
    else
        dch -i --no-auto-nmu --distribution $distribution --force-distribution "$message"
    fi

    local changelog=$(head -1 debian/changelog)
    if [[ "$changelog" =~ \((.*)\) ]] ; then
        current_version=${BASH_REMATCH[1]}
    fi

    return 0
}

install_build_depends() {
    local package="$1"
    local source_mirror="$2"
    sudo apt-get -o=APT::Get::Only-Source=true -y build-dep $package
    return 0
}

wait_for_apt_lock() {
        while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do
           sleep 1
        done
}

build_package_from_source() {
    local package="$1"
    local options="-k$GPG_KEY"
    local source_mirror="$2"

    local build_dir="$build_package_from_source_directory/$package"

    if [ -d $build_dir ]
    then
        rm -rf $build_dir
    fi

    mkdir -p $build_dir
    cd $build_dir

    wait_for_apt_lock
    sudo apt-get update --allow-releaseinfo-change -yqq
    install_build_depends $package $source_mirror
    apt-get source -o=APT::Get::Only-Source=true $package

    local directory_source=$(find  -maxdepth 1 -type d | grep ./)
    cd $directory_source
    if [ ${ARCH} = "amd64" ]
    then
        options="-b "$options
    else
        options="-b "$options
    fi

    debuild --no-tgz-check --no-lintian --preserve-envvar=PATH $options
    cd $build_dir
    return 0
}

build_source_package() {
    local package="$1"
    local options="-d -k$GPG_KEY"

    cd_to_git_dir_package $package
    ls
    if debian/rules get-orig-source 2> /dev/null
    then
        echo "We have a get-orig-source"
        options="-sa "$options
    fi
    debuild --no-tgz-check -S $options
    return 0
}

build_dahdi_dkms_module() {
    local package_module=dahdi-linux
    local build_package_dahdi_dkms_directory=${PGK_FOLDER}/${package_module}

    if [ $build_package_from_source -eq 1 ]
    then
        if [ -d ${PGK_FOLDER} ]; then
            rm -rf ${PGK_FOLDER}/${package_module}
        fi
        mkdir -p ${PGK_FOLDER}

        echo "Processing $package_module"
        prepare_repository $package_module
        update_git_repo

        cd ${PGK_FOLDER}/${package_module}

        make
        sudo build_tools/dkms-helper generate_conf > dkms.conf
        make clean

        local timestamp=$(date -u +%Y%m%d.%H%M%S)
        debian_version="$package_version+${timestamp}"
        local package_directory=/var/lib/dkms/${package_module}/${debian_version}

        generate_changelog ${package_module}/"dahdi-linux-dkms-mkdsc" ${debian_version}

        cd ..

        sudo rm -rf /usr/src/dahdi-linux-${debian_version}
        sudo mkdir -p /usr/src/dahdi-linux-${debian_version}
        sudo cp -R . /usr/src/dahdi-linux-${debian_version}

        sudo rm -rf /var/lib/dkms/dahdi-linux
        sudo dkms add -m dahdi-linux -v ${debian_version}

        sudo dkms build -m dahdi-linux -v ${debian_version}
        sudo dkms mkdsc -m dahdi-linux -v ${debian_version} --source-only

        sudo mv ${package_directory}/dsc/* ../

        cd ..
        # Fix build error "debsign: Can't find or can't read buildinfo file ...source.buildinfo!"
        sed -i "/_source.buildinfo/d" dahdi-linux-dkms_${debian_version}_source.changes
        debsign --re-sign -k$GPG_KEY $(ls *.changes)
    else
        build_package_from_source dahdi-linux-dkms ${distribution}
    fi

    upload_to_mirror ${package_module}
}

clean_package() {
    local package="$1"

    cd_to_git_dir_package $package

    dh_clean
    return 0
}

build_dput_conf() {
    local dput_file="$1"

    cp ${DPUT_TPL_FILE} $dput_file
    sed -i "s|{{ distribution }}|${distribution}|g" $dput_file
    sed -i "s|{{ fqdn }}|${MIRROR_HOST}|g" $dput_file
    sed -i "s|{{ incoming }}|${MIRROR_INCOMING}|g" $dput_file
    return 0
}

upload_to_mirror() {
    if [ -z $1 ]; then
        local package=$(dpkg-parsechangelog | grep Source | awk -F ': ' '{print $NF}')
    else
        local package=$1
    fi
    echo $PWD
    if [ $build_only_source -eq 0 ]; then
	if [ $build_module_kernel -eq 1 ]; then
            local changes_file=$(ls *.changes)
        else
            #cd "${package}"
            local changes_file=$(ls *.changes)
        fi
    else
        if [ $build_dahdi_dkms_module -eq 1 ]; then
            local changes_file=$(ls *.changes)
        else
            local version=$(echo "$2" | awk -F ':' '{print $NF}')
            local changes_file="../${package}_${version}_source.changes"
        fi
    fi

    local dput_file="${DPUT_DIR}/${package}.cf"

    mkdir -p $DPUT_DIR

    build_dput_conf $dput_file
    dput -f -c $dput_file $distribution $changes_file
    return 0
}

process_packages() {
    for package in $PACKAGES; do
        process_package $package
    done
}

package_is_up_to_date() {
    local package="$1"

    echo "Checking updated package: $package"

    valid_package $package

    cd_to_git_dir_package $package

    git fetch origin
    local need_update="$(git log $branch_name_builded..origin/$branch_name_builded --oneline)"

    if [ "$need_update" == "" ]; then
        echo "Package $package already up-to-date."
        return 0
    else
        echo "Package $package needs update."
        return 1
    fi
}

extract_branch_infos() {
    local package="$1"

    echo "Extracting branch infos for package: $package"

    branch_name_builded="$branch_name"

    cd_to_git_dir_package $package

    if ! git_remote_branch_exist $branch_name ; then
        echo "Remote branch $branch_name not exist, switch to default branch master"
        branch_name_builded="master"
    fi

    git_init_local_branch $branch_name_builded
    echo "$package fetch branch: <${branch_name_builded}>"

    return 0
}

prepare_repository() {
    local package="$1"

    echo "Initializing package: $package"

    cd $PACKAGES_DIR
    if [ ! -d "${package}/.git" ]; then
        echo "Cloning repository..."
        git clone ${GIT_URL}/${package}.git
        force_rebuild=1
    fi
    cd $package
    git clean -fxd
    git reset --hard
    return 0
}

update_git_repo() {
    git reset --hard
    branch_name_builded="$branch_name"
    git fetch --tags
    git checkout $branch_name_builded
    git pull origin $branch_name_builded
}

process_package() {
    local package="$1"
    local source_mirror="$2"

    echo "Processing $package"

    prepare_repository $package

    #if ! extract_branch_infos $package; then
    #    return 1
    #fi

    #if package_is_up_to_date $package && [ $force_rebuild -eq 0 ]; then
    #    echo
    #    return 0
    #fi

    update_git_repo

    if [ -z "$package_version" -a -z "$xivo_version" ]; then
        generate_changelog $package
    else
        local debian_version
        if [ -n "$package_version" ]; then
	    local timestamp=$(date -u +%Y%m%d.%H%M%S)
            local git_hash=$(git log -1 --pretty='%h')
            debian_version="$package_version+${timestamp}.${git_hash}"
            #debian_version="$package_version"
        else
            local source_version=$(get_source_version $package) || return 1
            debian_version=$(get_debian_version $package $source_version) || return 1
        fi

        if [ -z "$debian_version" ]; then
            exit 1
        fi

        generate_changelog $package $debian_version
    fi

    if [ $build_package_from_source -eq 0 ]
    then
        build_source_package $package
        clean_package $package
        #update_release_src
    else
        build_package_from_source $package $source_mirror
        #update_release_deb
    fi
    [ $? == 0 ] && upload_to_mirror $package $debian_version
    return 0
}

validate_repo_name() {
    local var="$1"
    case "$var" in
     *\ * )
        echo "ERROR: Invalid repository name, space is not allowed" >&2
        exit 1
        ;;
    *_* )
        echo "ERROR: Invalid repository name, underscore is not allowed" >&2
        exit 1
        ;;
    *)
        return 0
        ;;
    esac
}

if [ -z "$1" ]; then
    usage
fi

while getopts :d:p:v:b:m:V:afhsSkK opt
do
    case $opt in
        d) distribution=$OPTARG;;
        p) force_package=$OPTARG;;
        v) force_xivo_version=$OPTARG;;
        b) branch_name=$OPTARG;;
        s) build_only_source=1;;
        S) build_package_from_source=1;;
        m) conf_mirror=$OPTARG;;
        V) force_package_version=$OPTARG;;
        k) build_module_kernel=1;;
        K) build_dahdi_dkms_module=1;;
        a) all=1;;
        f) force_rebuild=1;;
        u) use_version=1;;
        h) usage;;
        '?') echo "$0 : option $OPTARG is not valid" >&2
            usage
        ;;
    esac
done

distribution="${distribution:-"${DISTRIBUTION_DEFAULT}"}"
branch_name="${branch_name:-"${GIT_BRANCH_DEFAULT}"}"
conf_mirror="${conf_mirror:-"${MIRROR_DEFAULT}"}"
xivo_version="${force_xivo_version:-""}"
package_version="${force_package_version:-""}"
all="${all:-"0"}"
build_only_source="${build_only_source:-"0"}"
build_package_from_source="${build_package_from_source:-"0"}"
build_dahdi_dkms_module="${build_dahdi_dkms_module:-"0"}"
force_rebuild="${force_rebuild:-"0"}"
use_version="${use_version:-"0"}"


validate_repo_name $distribution
config_mirror_file="${MIRROR_DIR}/$conf_mirror"
build_package_from_source_directory="${HOME}/build_from_sources"

if [ ! -f "$config_mirror_file" ]; then
    echo "ERROR: $config_mirror_file: No such mirror definition file" >&2
    exit 1
fi

# source mirror configuration
. $config_mirror_file

if [ $build_dahdi_dkms_module -eq 1 ]; then
    build_dahdi_dkms_module
    exit 0
fi

if [ $all -eq 1 ]; then
    process_packages
elif [ -n "$force_package" ]; then
    process_package $force_package $distribution
fi
