#!/bin/bash

export LANG=C.UTF-8

MARCH=$( uname -m )
if [ -z "$ARCH" ]; then
  case "$MARCH" in
    i?86)    export ARCH=i586 ;;
    armv7hl) export ARCH=$MARCH ;;
    arm*)    export ARCH=arm ;;
    # Unless $ARCH is already set, use uname -m for all other archs:
    *)       export ARCH=$MARCH ;;
  esac
fi

VERSION="0.1.8"
IRRADIUM_VERSION=${IRRADIUM_VERSION:-$(irradium | cut -d ' ' -f3)}
MIRRORS=${MIRRORS:-"/etc/pkg-get/mirrors"}
if [ -e ${MIRRORS} ]; then
    IRRADIUM_URL=$(grep -v '^#' ${MIRRORS} | grep "${IRRADIUM_VERSION}" | tr -d ' \t' | sed "s:ARCH:${ARCH}:g")
fi
IRRADIUM_URL=${IRRADIUM_URL:-"https://dl.irradium.org/irradium/packages/${ARCH}/${IRRADIUM_VERSION}"}
IRRADIUM_CACHE="/var/cache/pkg-get"
PORTS="/usr/bin/ports"
PORTS_PATH="/usr/ports"
DOWNLOAD="/usr/bin/curl"
SIGNIFY="/usr/bin/signify"
SIGNIFY_PATH=${SIGNIFY_PATH:-"/etc/pkg-get"}
SIGNIFY_NAME="pkg-get"
COMPRESSION="gz"
PORT_SUFFIX=".pkg.tar.${COMPRESSION}"
PRTGET="/usr/bin/prt-get"
PKGADD="/usr/bin/pkgadd"

WORK_DIR=$(mktemp -d)


trap 'cleaning' INT
trap 'cleaning' SIGINT
trap 'cleaning' TERM


message() {
    # parametr 1 - type message
    #    o: OK
    #    w: WARN
    #    e: ERROR
    # parametr 2 - action message
    # parametr 3 - text message
    local type="$1"
    local action="$2"
    local message="$3"

    if [[ "$type" == "e" ]]; then
        type="ERROR:"
    elif [[ "$type" == "w" ]]; then
        type="WARN:"
    elif [[ "$type" == "o" ]]; then
        type="OK:"
    fi

    if [[ ! -z "$type" ]]; then
        printf '%-6s %-12s %s\n' "$type" "$action" "$message"
    else
        printf '%-12s %s\n' "$action" "$message"
    fi
}

gen_packages_install() {
    local packages="$@"

    PACKAGES=( $(echo "$packages" | grep -e "^\[\s\].*" | cut -d ']' -f2- | tac -s ' ' | tac | uniq | xargs -I {} echo {}) )

    for pkg in ${PACKAGES[@]}; do
        local package_path=$($PRTGET path $pkg)
        if [[ -e $package_path/Pkgfile ]]; then
            local port=$(echo $package_path | rev | cut -d '/' -f2 | rev)
            local version=$(source $package_path/Pkgfile ; echo $version)
            local release=$(source $package_path/Pkgfile ; echo $release)
            if [[ ! -z ${version} ]]; then
                echo $port $pkg ${version}-${release} >> $WORK_DIR/ports.diff
            fi
        fi
    done
}

check_install() {
    local package="$1"
    local type="$2"

    status=$($PRTGET isinst $package)

    if [[ $status != *not*installed ]]; then
        echo "$status"
    else
        if [[ $type == "depinst" ]]; then
            packages=$($PRTGET depends $package)
        fi
        if [[ -z $packages ]]; then
            packages=("[ ] $package")
        fi
        if [[ $packages == *not*found ]]; then
            echo "$packages"
        else
            gen_packages_install "$packages"
        fi
    fi
}

check_for_updates() {
    readarray -t ports_temp < <($PORTS -d | sed -n '2,$p')

    for port_temp in "${ports_temp[@]}";do
        port=$(echo $port_temp | cut -d ' ' -f1)
        package=$(echo $port_temp | cut -d ' ' -f2)
        for port_list in $(cat /etc/prt-get.conf | grep ^prtdir | cut -d ' ' -f2); do
            port_sort=$(echo $port_list | rev | cut -d '/' -f1 | rev)
            # checking packets by port hierarchy
            if [[ -e "$port_list/$package/Pkgfile" && $port_sort != $port ]]; then
                break
            elif [[ -e "$port_list/$package/Pkgfile" && $port_sort == $port ]]; then
                ports_diff+=("$port_temp")
            fi
        done
    done

    # recording checked packages
    if [[ "${ports_diff[@]}" ]]; then
        printf "%s\n" "${ports_diff[@]}" > $WORK_DIR/ports.diff
    else
        if [[ -e "$WORK_DIR/ports.diff" ]]; then
            rm $WORK_DIR/ports.diff
        fi
    fi
}

verify_sign() {
    local port="$1"
    local pkg="$2"
    local type="$3"

    if [[ -e "${IRRADIUM_CACHE}/${port}/${pkg}.sig" ]]; then
        if [[ ! $($SIGNIFY -p "${SIGNIFY_PATH}/${SIGNIFY_NAME}.pub" -V -x "${IRRADIUM_CACHE}/${port}/${pkg}.sig" -m "${IRRADIUM_CACHE}/${port}/${pkg}" 2>&1 > /dev/null) ]]; then
            message "o" "signature" "port: ${port}   package: ${pkg}"
            return 0
        else
            message "e" "signature" "port: ${port}   package: ${pkg}"
            return 1
        fi
    else
        message "e" "signature" "port: ${port}   missing: ${pkg}.sig"
        return 1
    fi
}

package_install() {
    local type="$1"

    if [[ -e "$WORK_DIR/$type" ]]; then
        while read -r line; do
            local opt=" -f "
            if [[ $type == "update" ]]; then
                opt+=" -u "
            fi
            local pkgadd=$($PKGADD $opt $line 2>&1 > /dev/null)
            local pkg=$(echo $line | rev | cut -d '/' -f1 | rev)
            local pkgname=$(echo $pkg | cut -d '#' -f1)
            local port=$(echo $line | rev | cut -d '/' -f2 | rev)
            local portdir
            if [[ -z $pkgadd ]]; then
                if [[ -f "$PORTS_PATH/irradium-$port/$pkgname/post-install" ]]; then
                    portdir="$PORTS_PATH/irradium-$port/$pkgname/post-install"
                elif [[ -f "$PORTS_PATH/$port/$pkgname/post-install" ]]; then
                    portdir="$PORTS_PATH/$port/$pkgname/post-install"
                fi
                if [[ ! -z $portdir ]]; then
                    chmod +x "$portdir"
                    bash -c "$portdir" 2>/dev/null
                    unset portdir
                fi
                message "o" "$type" "port: ${port}   package: ${pkg}"
            else
                message "e" "$type" "port: ${port}   package: ${pkg}"
            fi
        done < $WORK_DIR/$type
    fi
}

sign_files() {
    local path="$1"

    find $path -iname "*${PORT_SUFFIX}" -exec $SIGNIFY -s "${SIGNIFY_PATH}/${SIGNIFY_NAME}.sec" -S -m "{}" -x "{}.sig" \;
}

downloads() {
    local port="$1"
    local name="$2"
    local version="$3"
    local url=${IRRADIUM_URL}/${port}/${name}'%23'${version}${PORT_SUFFIX}
    local pkg=${name}'#'${version}${PORT_SUFFIX}

    if [[ ! -d "${IRRADIUM_CACHE}/${port}" ]]; then
        mkdir -p "${IRRADIUM_CACHE}/${port}"
    fi

    if [[ -e "${IRRADIUM_CACHE}/${port}/${pkg}" ]]; then
        message "o" "download" "port: ${port}   file in cache: ${pkg}"
    else
        if [[ $($DOWNLOAD -o /dev/null -k --silent -Iw '%{http_code}' $url) == "200" ]]; then
            # download package
            $DOWNLOAD -k -e robots=off -C - $url \
                      -o ${IRRADIUM_CACHE}/${port}/${pkg}
        else
            message "e" "download" "port: ${port}   remote file missing: ${pkg}"
        fi
    fi

    if [[ -e "${IRRADIUM_CACHE}/${port}/${pkg}.sig" ]]; then
        message "o" "download" "port: ${port}   file in cache: ${pkg}.sig"
    else
        if [[ $($DOWNLOAD -o /dev/null -k --silent -Iw '%{http_code}' ${url}.sig) == "200" ]]; then
            # download package
            $DOWNLOAD -k -e robots=off -C - ${url}.sig \
                      -o ${IRRADIUM_CACHE}/${port}/${pkg}.sig
        else
            message "e" "download" "port: ${port}   remote file missing: ${pkg}.sig"
        fi
    fi
}

prepare_ports() {
    local operation="$1"
    local type="$2"

    if [[ -e "$WORK_DIR/ports.diff" ]]; then
        while read -r line; do
            port=$(echo $line | cut -d ' ' -f1)
            name=$(echo $line | cut -d ' ' -f2)
            version=$(echo $line | cut -d ' ' -f3)
            if [[ $operation == "download" ]]; then
                downloads ${port##*-} $name $version
            elif [[ $operation == "verify" ]]; then
                verify_sign ${port##*-} ${name}'#'${version}${PORT_SUFFIX}
                if [[ "$?" != 1 ]]; then
                    echo "${IRRADIUM_CACHE}/${port##*-}/${name}#${version}${PORT_SUFFIX}" >> $WORK_DIR/$type
                fi
            fi
        done < $WORK_DIR/ports.diff
    fi
}

claering_cache() {
    # clean cache directory
    if [[ -d ${IRRADIUM_CACHE} ]]; then
        rm -rf ${IRRADIUM_CACHE}/*
    fi
}

cleaning() {
    # clean work directory
    if [[ -d ${WORK_DIR} ]]; then
        rm -rf ${WORK_DIR}
    fi
    exit 0
}

usage() {
    echo
    echo "usage: $COMMAND [options]"
    echo "options:"
    echo "   -s,  --sign [path]   sign files"
    echo "   -i,  --install       install package"
    echo "   -di, --depinst       install packages and their dependencies"
    echo "   -u,  --update        update packages"
    echo "   -c,  --clear         clearing cache"
    echo "   -v,  --version       print version and exit"
    echo "   -h,  --help          print help and exit"
    echo
}

parse_options() {
    OPT_MODE=""
    OPT_COLLECTIONS=""

    for OPT in "$@"; do
        case "$OPT" in
        -s|--sign)
            OPT_MODE="sign" ;;
        -i|--install)
            OPT_MODE="install" ;;
        -di|--depinst)
            OPT_MODE="depinst" ;;
        -u|--update)
            OPT_MODE="update" ;;
        -c|--clear)
            OPT_MODE="cleaaring_cache" ;;
        -v|--version)
            echo "$COMMAND $VERSION"
            exit 0
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        -*)
            echo && echo "$COMMAND: invalid option $OPT"
            usage
            exit 1
            ;;
         *)
            OPT_COLLECTIONS="$OPT_COLLECTIONS $OPT" ;;
        esac
    done
}

main() {
    parse_options "$@"

    if [[ -z "$@" ]]; then
        usage
        exit 0
    fi

    [[ $EUID != 0 ]] && echo -e "\nThis script must be run with root privileges\n" && exit 1


    if [[ $OPT_MODE == "sign" && ( ! -z $OPT_COLLECTIONS && -d ${OPT_COLLECTIONS/[[:space:]]/} ) ]]; then
        sign_files ${OPT_COLLECTIONS/[[:space:]]/}
    fi

    if [[ ( $OPT_MODE == "install"  || $OPT_MODE == "depinst" ) && ! -z ${OPT_COLLECTIONS/[[:space:]]/} ]]; then
        check_install ${OPT_COLLECTIONS/[[:space:]]/} $OPT_MODE
        prepare_ports "download" "$OPT_MODE"
        prepare_ports "verify" "$OPT_MODE"
        package_install "$OPT_MODE"
    fi

    if [[ $OPT_MODE == "update" ]]; then
        check_for_updates
        prepare_ports "download" "$OPT_MODE"
        prepare_ports "verify" "$OPT_MODE"
        package_install "$OPT_MODE"
    fi

    if [[ $OPT_MODE == "cleaaring_cache" ]]; then
        claering_cache
    fi

    cleaning
}

COMMAND=${0##*/}

main "$@"

# End of file
