#!/bin/bash
#########################################################
#                                                       #
# This is BashStyle-NG                                  #
#                                                       #
# Licensed under GNU GENERAL PUBLIC LICENSE v3          #
#                                                       #
# Copyright Christopher Roy Bratušek                    #
#                                                       #
#########################################################

##############################
# Setup
##############################
shopt -s extglob
TOP_PID=$$

CWD=$(dirname "$(readlink -m "${BASH_SOURCE[0]}")")

source "${CWD}/.settings"
CFG_VERSION="5.0.0"
CFG_RESULTS="${CWD}/.configure_results"

PREFIX="/usr"
BINDIR="${PREFIX}/bin"
PCDIR="${PREFIX}/share/pkgconfig"
DATADIR="${PREFIX}/share"
DOCDIR="${DATADIR}/doc"
MANDIR="${DATADIR}/man"
GIRPATH="$(pkg-config --variable=typelibdir gobject-introspection-1.0)"

PYTHON=$(command -v python3 || command -v python)

PYMINVER_P=">=3.x"
PYMAXVER_P="<=4.x"

PYMINVER=3
PYMAXVER=4

DISABLE_POSTINSTALL=0

for lang in "${APP_LANGUAGES[@]}"; do
    LC_MESSAGES_DIRS+="${lang}/LC_MESSAGES"
done

PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"

rm -f "${CWD}/.configure_results"

##############################
# Color Settings
##############################
set_colors () {
    RED="\033[01;31m"
    GREEN="\033[01;32m"
    YELLOW="\033[01;33m"
    BLUE="\033[01;34m"
    MAGENTA="\033[01;35m"
    CYAN="\033[01;36m"
    WHITE="\033[01;37m"

    case ${TERM} in
        *xterm*color )
            ORANGE="\033[01;38;5;202m"
            SILVER="\033[01;38;5;246m"
        ;;
        * )
            ORANGE=${YELLOW}
            SILVER=${WHITE}
        ;;
    esac
}

##############################
# Screen messages
##############################
section_message () {
    case ${1} in
        APP )           echo -e "\n${RED}< Required ${WHITE}Applications" ;;
        OPT_APP )       echo -e "\n${MAGENTA}< Optional ${WHITE}Applications" ;;
        SBINAPP )       echo -e "\n${RED}< Required ${WHITE}SuperUser Applications" ;;
        OPT_SBINAPP )   echo -e "\n${MAGENTA}< Optional ${WHITE}SuperUser Applications" ;;
        BUILD )         echo -e "\n${RED}< Required ${WHITE}Build Tools" ;;
        OPT_BUILD )     echo -e "\n${MAGENTA}< Optional ${WHITE}Build Tools" ;;
        PY )            echo -e "\n${RED}< Python Interpreter${WHITE} ${PYMINVER_P} && ${PYMAXVER_P}" ;;
        PYMOD )         echo -e "\n${RED}< Required ${WHITE}Python Modules" ;;
        OPT_PYMOD )     echo -e "\n${MAGENTA}< Optional ${WHITE}Python Modules" ;;
        LIB )           echo -e "\n${RED}< Required ${WHITE}Libraries" ;;
        OPT_LIB )       echo -e "\n${MAGENTA}< Optional ${WHITE}Libraries" ;;
        GIR )           echo -e "\n${RED}< Required ${WHITE}GIR Repositories" ;;
        OPT_GIR )       echo -e "\n${MAGENTA}< Optional ${WHITE}GIR Repositories" ;;
    esac
    tput sgr0
}

start_message () {
    echo -e "\n${WHITE}Configuring ${GREEN}${APP_NAME}${YELLOW} v${APP_VERSION} ${CYAN}(${APP_CODENAME})"
    tput sgr0
}

ok_message () {
    if [[ -n ${2} ]]; then
            echo -e "${WHITE}\t+ ${CYAN}${1}${MAGENTA} >${GREEN} OK${SILVER} ( ${2} )"
    else    echo -e "${WHITE}\t+ ${CYAN}${1}${MAGENTA} >${GREEN} OK"
    fi
    tput sgr0
}

fail_message () {
    case ${1} in
        m )     echo -e "${WHITE}\t+ ${CYAN}${2}${MAGENTA} >${RED} !MISSING!" ;;
        o )     echo -e "${WHITE}\t+ ${CYAN}${2}${MAGENTA} >${RED} !OLDER THAN ${3}!" ;;
        p )     echo -e "${WHITE}\t+ ${CYAN}Python ($PYTHON)${MAGENTA} >${RED} !MISSING!" ;;
        p- )    echo -e "${WHITE}\t+ ${CYAN}Python ($PYTHON)${MAGENTA} >${RED} !OLDER THAN ${PYMINVER_P}!" ;;
        p+ )    echo -e "${WHITE}\t+ ${CYAN}Python ($PYTHON)${MAGENTA} >${RED} !IS ${PYMAXVER_P} OR ABOVE!" ;;
    esac
    tput sgr0
}

warn_message () {
    echo -e "${WHITE}\t+ ${CYAN}${1}${MAGENTA} >${YELLOW} Not Available\
        \n\t  > ${WHITE}${2}"
    tput sgr0
}

end_message () {
    echo -e "\n${BLUE}Notes from configure:\
         \n${YELLOW}Prefix\t\t\t${WHITE}>${MAGENTA} ${PREFIX}\
         \n${YELLOW}Python\t\t\t${WHITE}>${MAGENTA} ${PYTHON}"

    if [ "${DISABLE_POSTINSTALL}" -eq 1 ]; then
            echo -e "${YELLOW}Post-Install Tasks\t${WHITE}>${RED} Disabled"
    else    echo -e "${YELLOW}Post-Install Tasks\t${WHITE}>${GREEN} Enabled"
    fi

    echo -e "\n\t${SILVER}You may want to continue with './make build'.\n"
    tput sgr0
}

help_message () {
    set_colors
    echo -e "\n${GREEN}${APP_NAME} ${MAGENTA}v${APP_VERSION}${WHITE} / ${YELLOW}Configure v${CFG_VERSION} ${CYAN}help
    \n${WHITE}Options:"

    echo -e "   ${ORANGE}--help${WHITE} *|${GREEN} Display this help message
    ${ORANGE}--prefix=<prefix>${WHITE} *|${GREEN} Change installation prefix${WHITE} *|${BLUE} default: /usr
    ${ORANGE}--bindir=<bindir>${WHITE} *|${GREEN} Change binary path${WHITE} *|${BLUE} default: /usr/bin
    ${ORANGE}--docdir=<docdir>${WHITE} *|${GREEN} Change doc path${WHITE} *|${BLUE} default: /usr/share/doc
    ${ORANGE}--mandir=<mandir>${WHITE} *|${GREEN} Change man path${WHITE} *|${BLUE} default: /usr/share/man
    ${ORANGE}--datadir=<datadir>${WHITE} *|${GREEN} Change data path${WHITE} *|${BLUE} default: /usr/share
    ${ORANGE}--pcdir=<pcdir>${WHITE} *|${GREEN} Change pkg-config path${WHITE} *|${BLUE} default: /usr/share/pkgconfig
    ${ORANGE}--girpath=<girpath>${WHITE} *|${GREEN} Where to look for G-I typelibs${WHITE} *|${BLUE} default: /usr/lib/girepository-1.0
    ${ORANGE}--python=<python>${WHITE} *|${GREEN} Use specific Python interpreter${WHITE} *|${BLUE} default: /usr/bin/python3
    ${ORANGE}--no-postinstall${WHITE} *|${GREEN} Disable post-installation tasks${WHITE} *|${BLUE} for package-maintainers" | column -t -s \*
    echo
    tput sgr0

    exit 0
}

##############################
# Dependency checks
##############################
check () {
    [[ -x "/usr/bin/${1}" || -x "/bin/${1}" ]] && return 0

    local dir
    while IFS= read -r -d : dir || [[ -n "${dir}" ]]; do
        [[ -x "${dir}/${1}" ]] && return 0
    done <<< "$PATH:"

    return 1
}

check_helper () {
    if (( ${#BUILD[@]} > 0 )); then
        section_message BUILD
        check_apps "false" "${BUILD[@]}"
    fi

    if (( ${#OPT_BUILD[@]} > 0 )); then
        section_message OPT_BUILD
        check_apps "true" "${OPT_BUILD[@]}"
    fi

    if (( ${#APPS[@]} > 0 )); then
        section_message APP
        check_apps "false" "${APPS[@]}"
    fi

    if (( ${#OPT_APPS[@]} > 0 )); then
        section_message OPT_APP
        check_apps "true" "${OPT_APPS[@]}"
    fi

    if (( ${#SAPPS[@]} > 0 )); then
        section_message SBINAPP
        check_apps "false" "${SAPPS[@]}"
    fi

    if (( ${#OPT_SAPPS[@]} > 0 )); then
        section_message OPT_SBINAPP
        check_apps "true" "${OPT_SAPPS[@]}"
    fi

    if (( ${#PYMODS[@]} > 0 )); then
        check_python
        section_message PYMOD
        check_pymods "false" "${PYMODS[@]}"
    fi

    if (( ${#OPT_PYMODS[@]} > 0 )); then
        (( ${#PYMODS[@]} == 0 )) && check_python

        section_message OPT_PYMOD
        check_pymods "true" "${OPT_PYMODS[@]}"
    fi

    if (( ${#LIBS[@]} > 0 )); then
        section_message LIB
        check_libs "false" "${LIBS[@]}"
    fi

    if (( ${#OPT_LIBS[@]} > 0 )); then
        section_message OPT_LIB
        check_libs "true" "${OPT_LIBS[@]}"
    fi

    if (( ${#GIR[@]} > 0 )); then
        section_message GIR
        check_gir "false" "${GIR[@]}"
    fi

    if (( ${#OPT_GIR[@]} > 0 )); then
        section_message OPT_GIR
        check_gir "true" "${OPT_GIR[@]}"
    fi
}

check_apps () {
    local opt="${1}"
    shift

    local arg app desc app2 app2_p dep_found1 dep_found2

    for arg in "${@}"; do
        IFS=: read -r app desc app2 <<< "$arg"

        [[ -n ${app2} ]] && app2_p="(${app2})" || app2_p=""

        check "${app}" && dep_found1=true || dep_found1=false

        if [[ -n $app2 ]]; then
            check "${app2}" && dep_found2=true || dep_found2=false
        else
            dep_found2=false
        fi

        if [[ ${dep_found1} == true || ${dep_found2} == true ]]; then
            ok_message "${app} ${app2_p}"
        elif [[ ${opt} == "true" ]]; then
            warn_message "${app} ${app2_p}" "${desc}"
            MISSING+=" ${app}"
        else
            fail_message m "${app} ${app2_p}"
            kill -s TERM "${TOP_PID}"
        fi
    done
}

check_libs () {
    local opt="${1}"
    shift

    local arg lib version pc_file desc exist_return dep_return

    for arg in "${@}"; do
        IFS=: read -r lib version pc_file desc <<< "$arg"

        pkg-config --exists "${pc_file}" 2>/dev/null
        exist_return=$?

        if [[ ${exist_return} -ne 0 ]]; then
            if [[ ${opt} == "true" ]]; then
                warn_message "${lib}" "${desc}"
                MISSING+=" ${lib}"
            else
                fail_message m "${lib}"
                kill -s TERM "${TOP_PID}"
            fi
        else
            pkg-config --atleast-version="${version}" "${pc_file}" 2>/dev/null
            dep_return=$?

            if [[ ${dep_return} -eq 0 ]]; then
                ok_message "${lib}" "${version}+"
            else
                fail_message o "${lib}" "${version}"
                kill -s TERM "${TOP_PID}"
            fi
        fi
    done
}

check_gir () {
    local opt="${1}"
    shift

    local arg gir_name version type_lib desc
    local gir_file file_path xver exist_return dep_return

    for arg in "${@}"; do
        IFS=: read -r gir_name version type_lib desc <<< "$arg"

        exist_return=1
        dep_return=0

        file_path=""
        for gir_file in "${GIRPATH}/${type_lib}"*.typelib; do
            if [[ -f "${gir_file}" ]]; then
                file_path="${gir_file}"
                break
            fi
        done

        if [[ -n "$file_path" ]]; then
            exist_return=0

            xver="${file_path##*/}"
            xver="${xver#*-}"
            xver="${xver%.typelib}"

            if awk -v v1="$xver" -v v2="${version}" 'BEGIN {exit !(v1 >= v2)}'; then
                dep_return=1
            fi
        fi

        if [[ ${exit_return} -ne 0 ]]; then
            if [[ ${opt} == "true" ]]; then
                warn_message "${gir_name}" "${desc}"
                MISSING+=" ${gir_name}"
            else
                fail_message m "${gir_name}"
                kill -s TERM "${TOP_PID}"
            fi
        else
            if [[ ${dep_return} -eq 1 ]]; then
                ok_message "${gir_name}" "${version}+"
            else
                fail_message o "${gir_name}" "${version}"
                kill -s TERM "${TOP_PID}"
            fi
        fi
    done
}

check_python () {
    section_message PY

    if [[ -z ${PYTHON} || ! -x ${PYTHON} ]]; then
        fail_message p
        kill -s TERM "${TOP_PID}"
    fi

    local py_major
    py_major=$(${PYTHON} -c 'import sys; print(sys.version_info[0])' 2>/dev/null)

    local py_ver_str
    py_ver_str=$(${PYTHON} -c 'import platform; print(platform.python_version())' 2>/dev/null)

    if (( py_major < PYMINVER )); then
        fail_message p-
        kill -s TERM "${TOP_PID}"
    elif (( py_major >= PYMAXVER )); then
        fail_message p+
        kill -s TERM "${TOP_PID}"
    else
        ok_message Python "${py_ver_str}"
    fi
}

check_pymods () {
    local opt="${1}"
    shift

    local arg mod smod desc dep dep_return

    for arg in "${@}"; do
        IFS=: read -r mod smod desc <<< "$arg"

        [[ -n ${smod} ]] && dep="${mod} (${smod})" || dep="${mod}"

        case ${mod} in
            gi.repository.* )
                gi_submod="${mod##*.}"
                ${PYTHON} -c "import gi; gi.require_version('${gi_submod}', '${smod}'); import ${mod}" 2>/dev/null
                dep_return=$?
            ;;
            * )
                ${PYTHON} -c "import ${mod}" 2>/dev/null
                dep_return=$?
            ;;
        esac

        if [[ ${dep_return} -eq 0 ]]; then
            ok_message "${dep}"
        elif [[ ${opt} == "true" ]]; then
            warn_message "${dep}" "${desc}"
            MISSING+=" ${dep}"
        else
            fail_message m "${dep}"
            kill -s TERM "${TOP_PID}"
        fi
    done
}

##############################
# Variable substitution
##############################
copy_in_files () {
    for in_file in "${IN_FILES[@]}"; do
        cp "${in_file}".in "${in_file}"
    done
}

substitute_values () {
    sed -e "s>@VERSION@>${APP_VERSION}>g" \
        -e "s>@PREFIX@>${PREFIX}>g" \
        -e "s>@CODENAME@>${APP_CODENAME}>g" \
        -e "s>@DATADIR@>${DATADIR}>g" \
        -e "s>@PYTHON@>${PYTHON}>g" \
        -e "s>@BSNG_YEAR@>${APP_YEAR}>g" \
        -i "${IN_FILES[@]}"
}

##############################
# Export results
##############################
export_result ()
{
    echo -e "PREFIX=\"${PREFIX}\"
prefix=\"${PREFIX}\"
EXECDIR=\"${EXECDIR}\"
DATADIR=\"${DATADIR}/bashstyle-ng\"
DATADIR_LIST=(ui rc)
DOCDIR=\"${DOCDIR}/bashstyle-ng\"
SCREENSHOTDIR=\"${DOCDIR}/bashstyle-ng/screenshots\"
MANDIR=\"${MANDIR}/man1\"
BINDIR=\"${BINDIR}\"
LOCALEDIR=\"${DATADIR}/locale\"
LOCALEDIR_LIST=(${LC_MESSAGES_DIRS})
ICONDIR=\"${DATADIR}/icons/hicolor/32x32/apps\"
DESKTOPDIR=\"${DATADIR}/applications\"
PCDIR=\"${PCDIR}\"
APP_LANGUAGES=\"${APP_LANGUAGES}\"
DISABLE_POSTINSTALL=${DISABLE_POSTINSTALL}" \
    > "${CFG_RESULTS}"
}

##############################
# Main Loop
##############################
for opt in "${@}"; do
    case ${opt} in
        --prefix=* )        PREFIX="${opt/*=}" ;;
        --pcdir=* )         PCDIR="${opt/*=}" ;;
        --bindir=* )        BINDIR="${opt/*=}" ;;
        --docdir=* )        DOCDIR="${opt/*=}" ;;
        --mandir=* )        MANDIR="${opt/*=}" ;;
        --girpath=* )       GIRPATH="${opt/*=}" ;;
        --datadir=* )       DATADIR="${opt/*=}" ;;
        --python=* )        PYTHON="${opt/*=}" ;;
        --help | -h )       help_message ;;
        --no-postinstall )  DISABLE_POSTINSTALL=1 ;;
        * )                 echo "unknown option: ${opt}" ;;
    esac
    shift
done

##############################
# Do stuff
##############################
set_colors
start_message
check_helper
copy_in_files
substitute_values
end_message
export_result
