#!/bin/bash
#
#  This file is part of nzbget
#
#  Copyright (C) 2015-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# Setup strict bash error handling
set -o nounset
set -o errexit

# Uncomment next line for debuging
#set -x

ALLTARGETS="dist i686 x86_64 armel armhf mipsel mipseb ppc6xx ppc500 x86_64-bsd"
ROOT=`pwd`
ROOTPATH=$PATH
OUTPUTDIR=$ROOT/output

# Configuration
BUILD=no
PLATFORMS=""
TARGETS=""
OUTPUTS=""
REVISION=""
BRANCH=""
CONFIGS=""
CLEAN=no
PCH=no
COREX="1"

PrintUsage()
{
    echo "Usage:"
    echo "  $(basename $0) [targets] [output] [revision] [reppath] [configs] [cleanup] [pch] [corex]"
    echo "    targets   : all (default) $ALLTARGETS"
    echo "    output    : bin installer"
    echo "    revision  : head (default) work <commit-hash>"
    echo "    branch    : develop (default) master tags/XXX branches/XXX"
    echo "    configs   : release debug (default) release-nostrip debug-strip"
    echo "    cleanup   : cleanup output directory before building"
    echo "    pch       : create and use precompiled headers"
    echo "    corex     : multicore make (x is a number of threads)"
    echo
}

ParseCommandLine()
{
    for PARAM in "$@"
    do
        case $PARAM in
            release|release-nostrip|debug|debug-strip)
                # using xargs to trim spaces
                CONFIGS=`echo "$CONFIGS $PARAM" | xargs`
                ;;
            master|develop|tags/*)
                BRANCH="$PARAM"
                ;;
            branches/*)
                BRANCH="${PARAM:9}"
                ;;
            head|work|[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]*)
                REVISION="$PARAM"
                ;;
            clean|cleanup)
                CLEAN=yes
                ;;
            pch)
                PCH=yes
                ;;
            core[1-9])
                COREX="${PARAM:4}"
                ;;
            bin|installer)
                # using xargs to trim spaces
                OUTPUTS=`echo "$OUTPUTS $PARAM" | xargs`
                ;;
            *)
                if [[ " $ALLTARGETS " == *" $PARAM "* ]]; then
                    # using xargs to trim spaces
                    TARGETS=`echo "$TARGETS $PARAM" | xargs`
                    if [ "$PARAM" == "all" ]; then
                        PARAM=$ALLTARGETS
                    fi
                elif [ -d toolchain/$PARAM ]; then
                    # non-standard target but the toolchain exist
                    # using xargs to trim spaces
                    TARGETS=`echo "$TARGETS $PARAM" | xargs`
                else
                    echo "Invalid parameter: $PARAM"
                    exit 1
                fi
                ;;
        esac

    done

    if [ "$TARGETS" == "" ]; then
        TARGETS="$ALLTARGETS"
    fi

    if [ "$OUTPUTS" == "" ]; then
        OUTPUTS="bin installer"
    fi

    if [ "$REVISION" == "" ]; then
        REVISION="head"
    fi

    if [ "$BRANCH" == "" ]; then
        BRANCH="develop"
    fi

    if [ "$CONFIGS" == "" ]; then
        CONFIGS="release debug"
    fi

    for TARGET in $TARGETS
    do
        if [[ $TARGET == *-bsd ]]; then
            PLATFORM="freebsd"
        else
            PLATFORM="linux"
        fi
        if [[ " $PLATFORMS " != *" $PLATFORM "* ]]; then
              PLATFORMS=`echo "$PLATFORMS $PLATFORM" | xargs`
        fi
    done
}

PrintConfig()
{
    echo "Active configuration:"
    echo "  platforms: $PLATFORMS"
    echo "  targets  : $TARGETS"
    echo "  outputs  : $OUTPUTS"
    echo "  revision : $REVISION"
    echo "  branch   : $BRANCH"
    echo "  configs  : $CONFIGS"
    echo "  cleanup  : $CLEAN"
    echo "  pch      : $PCH"
    echo "  cores    : $COREX"
    echo 
}

UpdateFromRepository()
{
    if [ ! -d nzbget ]; then
        echo "Initial checkout"
        git clone https://github.com/nzbget/nzbget.git
    fi

    cd nzbget

    BUILDDIR=$ROOT/nzbget

    cd $BUILDDIR

    if [ $REVISION != "work" ]; then
        echo "Updating to $REVISION"
        git pull
        git checkout $BRANCH
        if [ $REVISION != "head" ]; then
            git checkout $REVISION
        fi
        touch Makefile.in configure config.h.in

        echo "Updating root certificates"
		cd ../setup
		curl --remote-name --time-cond cacert.pem https://curl.haxx.se/ca/cacert.pem
	    cd $BUILDDIR
    fi
}

DetectVersionRevision()
{
    VERSION=`grep "AC_INIT(nzbget, " configure.ac`
    VERSION=`expr "$VERSION" : '.*, \(.*\),.*)'`

    # Building revision name
    B=`git branch | sed -n -e 's/^\* \(.*\)/\1/p' | sed 's/ /-/g' | sed 's/(//g' | sed 's/)//g'`
    M=`git status --porcelain`
    if [ "$M" != "" ]; then
        M="M"
    fi
    if [ "$B" == "master" ]; then
        REVISION="$M"
    elif [ "$B" == "develop" ]; then
        REVISION=`git rev-list HEAD | wc -l | xargs`
        REVISION="${REVISION}$M"
    else
        REVISION=`git rev-list HEAD | wc -l | xargs`
        REVISION="${REVISION}$M-($B)"
    fi
}

ConstructOutputFilename()
{
    BASENAME="nzbget-$VERSION"
    if [ `expr "$VERSION" : ".*-testing"` != 0 ]; then
        BASENAME="$BASENAME-r$REVISION"
    fi
}

ConstructSuffix()
{
    case $CONFIG in
        release)
            SUFFIX=""
            ;;
        debug)
            SUFFIX="-debug"
            ;;
        debug-strip)
            SUFFIX="-debug-strip"
            ;;
        release-nostrip)
            SUFFIX="-nostrip"
            ;;
    esac

    if [ $PLATFORM == "freebsd" ]; then
        PLATSUFF="-bsd"
    else
        PLATSUFF=""
    fi
}

ConstructArchParams()
{
    ARCH=$TARGET
    case $TARGET in
        mipseb)
            ARCH=mips
            ;;
        armhf)
            ARCH=arm
            ;;
        arm*)
            ARCH=arm
            ;;
        ppc500)
            ARCH=powerpc
            ;;
        ppc*)
            ARCH=powerpc
            ;;
    esac

    TOOLCHAIN_ROOT=$ROOT/toolchain/$TARGET$PLATSUFF
    TOOLPATH=$TOOLCHAIN_ROOT/output/host/usr/bin
    PATH=$TOOLPATH:$ROOTPATH
    STAGING="$TOOLCHAIN_ROOT/output/staging/usr"
    case $PLATFORM in
        linux)
            TOOLKIND=buildroot
            TOOLNAME=linux
        ;;
        freebsd)
            TOOLKIND=crossgcc
            TOOLNAME=pc-freebsd9
        ;;
    esac
}

ConfigureTarget()
{
    STRIP=""
    if [ $CONFIG == "debug-strip" -o $CONFIG == "release" ]; then
        STRIP="-s"
    fi

    DEBUG=""
    if [ $CONFIG != "release" ]; then
        DEBUG="-g"
    fi

    case "$TOOLKIND-$CONFIG" in
        buildroot-debug|buildroot-debug-strip)
            LDFLAGS="-static $STRIP" \
                CXXFLAGS="-std=c++14 -g -fasynchronous-unwind-tables" \
                LIBS="-lcrypto -ldl -lz -lubacktrace" \
                ./configure --disable-dependency-tracking --host=$ARCH-$TOOLNAME --enable-debug
            ;;
        buildroot-release|buildroot-release-nostrip)
            LDFLAGS="-static $STRIP" \
                CXXFLAGS="-std=c++14 -O2 $DEBUG" \
                LIBS="-lcrypto -ldl -lz" \
                ./configure --disable-dependency-tracking --host=$ARCH-$TOOLNAME
            ;;
        crossgcc-debug|crossgcc-debug-strip)
            LDFLAGS="-static $STRIP" \
                CXXFLAGS="-std=c++14 -g -fasynchronous-unwind-tables -I$STAGING/include" \
                PKG_CONFIG_LIBDIR="$STAGING/lib/pkgconfig" \
                ./configure --disable-dependency-tracking --host=$ARCH-$TOOLNAME --enable-debug
                ;;
        crossgcc-release|crossgcc-release-nostrip)
            LDFLAGS="-static $STRIP" \
                CXXFLAGS="-std=c++14 -O2 $DEBUG -I$STAGING/include" \
                PKG_CONFIG_LIBDIR="$STAGING/lib/pkgconfig" \
                ./configure --disable-dependency-tracking --host=$ARCH-$TOOLNAME
                ;;
    esac
}

PrecompileHeaders()
{
    rm -f nzbget.h.gch
    if [ $PCH == "yes" ]; then
        OPTIM=""
        if [ $CONFIG == "release" -o $CONFIG == "release-nostrip" ]; then
            OPTIM="-O2"
        fi

        $ARCH-$TOOLNAME-g++ -std=c++14 -DHAVE_CONFIG_H \
            -I. -I$STAGING/include -I$STAGING/include/libxml2 \
            -g $OPTIM daemon/main/nzbget.h -o nzbget.h.gch
    fi
}

PackTarget()
{
    rm -r -f $OUTPUTDIR/install
    make DESTDIR=$OUTPUTDIR/install install

    cd $OUTPUTDIR
    rm -r -f nzbget
    mkdir -p nzbget
    mv install/usr/local/bin/nzbget nzbget
    mv install/usr/local/share/doc/nzbget/* nzbget
    mv install/usr/local/share/nzbget/webui nzbget
    mv install/usr/local/share/nzbget/scripts nzbget
    CONFTEMPLATE=nzbget/webui/nzbget.conf.template
    mv install/usr/local/share/nzbget/nzbget.conf $CONFTEMPLATE

    rm -r -f $OUTPUTDIR/install

    # adjusting nzbget.conf
    sed 's:^MainDir=.*:MainDir=${AppDir}/downloads:' -i $CONFTEMPLATE
    sed 's:^DestDir=.*:DestDir=${MainDir}/completed:' -i $CONFTEMPLATE
    sed 's:^InterDir=.*:InterDir=${MainDir}/intermediate:' -i $CONFTEMPLATE
    sed 's:^WebDir=.*:WebDir=${AppDir}/webui:' -i $CONFTEMPLATE
    sed 's:^ScriptDir=.*:ScriptDir=${AppDir}/scripts:' -i $CONFTEMPLATE
    sed 's:^LogFile=.*:LogFile=${MainDir}/nzbget.log:' -i $CONFTEMPLATE
    sed 's:^ConfigTemplate=.*:ConfigTemplate=${AppDir}/webui/nzbget.conf.template:' -i $CONFTEMPLATE
    sed 's:^AuthorizedIP=.*:AuthorizedIP=127.0.0.1:' -i $CONFTEMPLATE

    tar -czf $BASENAME-bin-$PLATFORM-$TARGET$SUFFIX.tar.gz nzbget

    rm -r -f nzbget
}

BuildTarget()
{
    echo "Building $TARGET ($PLATFORM $CONFIG)"

    cd $BUILDDIR

    ConstructArchParams

    ConfigureTarget

    make clean

    PrecompileHeaders

    make -j $COREX

    PackTarget

    echo "Completed build in `pwd` for $TARGET ($CONFIG)"
}

BuildInstaller()
{
    echo "Creating installer for $PLATFORM $CONFIG..."

    cd $OUTPUTDIR

    # checking if all targets exists
    for TARGET in $TARGETS
    do
        ALLEXISTS="yes"
        if [ $TARGET != "dist" ]; then
            if [ ! -f $BASENAME-bin-$PLATFORM-$TARGET$SUFFIX.tar.gz ]; then
                echo "Could not find $BASENAME-bin-$PLATFORM-$TARGET$SUFFIX.tar.gz"
                ALLEXISTS="no"
            fi
        fi
    done

    if [ "$ALLEXISTS" == "no" ]; then
        exit 1;
    fi

    echo "Unpacking targets..."
    rm -r -f nzbget
    for TARGET in $TARGETS
    do
        ALLEXISTS="yes"
        if [ "$TARGET" != "dist" ]; then
            tar -xzf $BASENAME-bin-$PLATFORM-$TARGET$SUFFIX.tar.gz
            mv nzbget/nzbget nzbget/nzbget-$TARGET
            cp ../setup/unrar-$TARGET$PLATSUFF nzbget/unrar-$TARGET
            cp ../setup/7za-$TARGET$PLATSUFF nzbget/7za-$TARGET
        fi
    done

    # adjusting nzbget.conf
    sed 's:^UnrarCmd=unrar:UnrarCmd=${AppDir}/unrar:' -i nzbget/webui/nzbget.conf.template
    sed 's:^SevenZipCmd=7z:SevenZipCmd=${AppDir}/7za:' -i nzbget/webui/nzbget.conf.template
    sed 's:^CertStore=.*:CertStore=${AppDir}/cacert.pem:' -i nzbget/webui/nzbget.conf.template
    sed 's:^CertCheck=.*:CertCheck=yes:' -i nzbget/webui/nzbget.conf.template

    INSTFILE=$BASENAME-bin-$PLATFORM$SUFFIX.run

    echo "Building installer package..."
    cp $BUILDDIR/linux/installer.sh $INSTFILE
    cp $BUILDDIR/linux/package-info.json nzbget/webui
    cp $BUILDDIR/linux/install-update.sh nzbget
    cp $BUILDDIR/pubkey.pem nzbget
    cp ../setup/license-unrar.txt nzbget
    cp ../setup/license-7zip.txt nzbget
    cp ../setup/cacert.pem nzbget

    # adjusting update config file
    sed "s:linux:$PLATFORM:" -i nzbget/webui/package-info.json
    sed "s:linux:$PLATFORM:" -i nzbget/install-update.sh

    # creating payload
    cd nzbget
    tar czf - * > ../$INSTFILE.data
    cd ..

    # creating installer script
    sed "s:^TITLE=$:TITLE=\"$BASENAME$SUFFIX\":" -i $INSTFILE
    sed "s:^PLATFORM=$:PLATFORM=\"$PLATFORM\":" -i $INSTFILE
    DISTTARGETS="${TARGETS/dist/}"
    DISTTARGETS=`echo "$DISTTARGETS" | xargs`
    sed "s:^DISTARCHS=$:DISTARCHS=\"$DISTTARGETS\":" -i $INSTFILE

    MD5=`md5sum "$INSTFILE.data" | cut -b-32`
    sed "s:^MD5=$:MD5=\"$MD5\":" -i $INSTFILE

    PAYLOAD=`stat -c%s "$INSTFILE.data"`
    PAYLOADLEN=${#PAYLOAD}

    HEADER=`stat -c%s "$INSTFILE"`
    HEADERLEN=${#HEADER}
    HEADER=`expr $HEADER + $HEADERLEN + $PAYLOADLEN`

    TOTAL=`expr $HEADER + $PAYLOAD`
    TOTALLEN=${#TOTAL}

    HEADER=`expr $HEADER - $PAYLOADLEN + $TOTALLEN`
    TOTAL=`expr $TOTAL - $PAYLOADLEN + $TOTALLEN`

    sed "s:^HEADER=$:HEADER=$HEADER:" -i $INSTFILE
    sed "s:^TOTAL=$:TOTAL=$TOTAL:" -i $INSTFILE

    # attaching payload
    cat $INSTFILE.data >> $INSTFILE
    rm $INSTFILE.data
    chmod +x $INSTFILE

    rm -r nzbget
}

BuildDist()
{
    echo "Building dist"

    cd $BUILDDIR
    ./configure --disable-dependency-tracking --disable-cpp-check
    make dist
    cp nzbget-$VERSION.tar.gz $OUTPUTDIR/$BASENAME.tar.gz
}

BuildConfig()
{
    ConstructSuffix

    for OUTPUT in $OUTPUTS; do
        if [ $OUTPUT == "bin" ]; then
            for TARGET in $TARGETS; do
                if [ $TARGET == "dist" ]; then
                    if [ ! -f $OUTPUTDIR/$BASENAME.tar.gz ]; then
                        BuildDist
                    fi
                else
                    BuildTarget
                fi
            done
        elif [ $OUTPUT == "installer" ]; then
            BuildInstaller
        fi
    done
}

FilterTargets()
{
    TARGETS=""
    for TARGET in $BUILDTARGETS
    do
        if [[ $TARGET == *-bsd ]]; then
            PLAT="freebsd"
            TARGET="${TARGET%-bsd}"
        else
            PLAT="linux"
        fi
        if [ $PLATFORM == $PLAT ]; then
              TARGETS=`echo "$TARGETS $TARGET" | xargs`
        fi
    done
}

CleanupIfNecessary()
{
    if [ $CLEAN == "yes" ]; then
        rm -r -f $OUTPUTDIR/*
    fi
}

##### Main script body #####

# Parsing command line
PrintUsage
ParseCommandLine $@
PrintConfig

# Checkout and update from git
UpdateFromRepository

# File name format for output files
DetectVersionRevision
ConstructOutputFilename

# Building
mkdir -p $OUTPUTDIR
CleanupIfNecessary

BUILDTARGETS=$TARGETS

for PLATFORM in $PLATFORMS; do
    FilterTargets
    for CONFIG in $CONFIGS; do
        BuildConfig
    done
done

