#!/usr/bin/env bash
# vim:expandtab:ts=4:sw=4:fdm=marker:fmr={{{,}}}
# To Inspect the code use vim, modelines and folds!!!!

#########################################################################
# This Scripts is inspired by Morlenxus' easy_e17.sh                    #
# and also a tiny bit of code is taken form it.                         #
# The easy_e17.sh script can be found at:                               #
# http://omicron.homeip.net/projects/#easy_e17.sh                       #
#                                                                       #
# License: BSD licence                                                  #
# Get the latest version at: https://github.com/ObiWahn/git_e17         #
# Coded by Jan Christoph Uhde - linux (a) obiwahn dot org               #
#########################################################################

(( $BASH_VERSINFO < 4 )) && echo -e "\nThis Script requires at least bash version >= 4\n" && exit 1

### global variable definitions {{{

## TODO ## Convert package list strings to arrays!!!
basic_lib="efl e_dbus azy elementary"
basic_bin="e evas_generic_loaders terminology exchange"
themes="dark 23oz b_and_w darkness detourious efenniht"

modules_lib="libeweather etrophy"
modules_bin="emprint --exalt"
modules_extra="alarm comp-scale cpu deskshow diskio drawer eektool elfe"
modules_extra+=" empris engage eooorg everything-aspell everything-mpris"
modules_extra+=" everything-pidgin everything-places everything-shotgun everything-skeleton"
modules_extra+=" everything-tracker everything-wallpaper everything-websearch eweather --exalt-client"
modules_extra+=" exebuf execwatch flame forecasts iiirk itask mail mem moon mpdule net news penguins"
modules_extra+=" photo places quickaccess rain screenshot skel slideshow snow taskbar tclock uptime"
modules_extra+=" weather winlist-ng winselector wlan"

extra_lib="imlib2 enlil --libast python-evas python-ecore python-e_dbusi"
extra_lib+=" python-edje python-emotion python-elementary shellementary"
extra_bin=" --clouseau --e_cho --e-type e_phys --eblock --econcentration editje eenvader.fractal --emote empower"
extra_bin+=" --enjoy enki entrance ephoto eskiss --Eterm --expedite exquisite eyelight rage"

declare -A packages_msg
packages_msg[__basic_lib]="\nenlightenment foundation libraries:"
packages_msg[__basic_bin]="\nbasic binaries:"
packages_msg[__themes]="themes:\n"
packages_basic="__basic_lib $basic_lib __basic_bin $basic_bin __themes $themes"

packages_msg[__modules_lib]="\nmodule libraries:"
packages_msg[__modules_bin]="\nmodule binaries:"
packages_msg[__modules_extra]="\nmodule extras"
packages_half="$packages_basic __modules_lib $modules_lib __modules_bin $modules_bin __modules_extra $modules_extra"

packages_msg[__extra_lib]="\nextra libraries:"
packages_msg[__extra_bin]="\nextra binaries:"
packages_full="$packages_half __extra_lib $extra_lib __extra_bin $extra_bin"

declare -A conf
declare -A g_old_conf

packages=""
packages_failed=""
g_current_commit=""
g_current_branch=""
g_old_commit=""

# signals return status of some operations as
# we lack proper return values
g_current_failed=false
g_clean_phase=false

conf[dev]=false
conf[verbose]=false
conf[debug]=false
conf[debug_level]=10
conf[animate]=false

conf[clean]=false
conf[distclean]=false
conf[uninstall]=false
conf[vmake]=false

#for use in errors
e="\n       "

bold=$(tput bold)
boldoff=$(tput sgr0)
c_red=$(tput setaf 1)
c_green=$(tput setaf 2)
c_norm=$(tput setaf 7)

green="${bold}${c_green}"
red="${bold}${c_red}"
norm="${c_norm}${boldoff}"

ok="${bold}${c_green}OK${c_norm}${boldoff}"
fail="${bold}${c_red}FAILED${c_norm}${boldoff}"
error="${bold}${c_red}Error:${c_norm}${boldoff}"

conf[git_repo]="git://git.enlightenment.fr/vcs/svn.git"

###end global vars }}}
#### main function {{{
main(){
debug 1 "$FUNCNAME() $*"

## interpret command line
get_opts "$@"
## configuration
get_git_e17_path_from_global_cfg
read_local_cfg
read_global_cfg
assign_defaults
interpret_action
check_cfg
##
uninstall
distcheck
## source
get_src
git_fetch_reset
git_pull
## package management
if ! [[ "$packages" ]]; then
    echo -e "\n${bold}No packages to build!${boldoff}\n"
    exit 0
fi
## finding changes
calculate_updates
add_failed
ignore_pkgs
##
set_env
build_main
noob_info
}
#### end main function }}}

##### functions used in main {{{
### display help - help(), devhelp() {{{
help(){
debug 1 "$FUNCNAME() $*"
#
# This functions displays the help file
#

printf '%b\n' \
   "git_e17 - simple script for building enlightenment 17" \
   "" \
   "./git_e17 [options] ${green}action${norm} [(only|plugin) <packagelist>]" \
   "" \
   "actions:" \
   "    ${green}build${norm}" \
   "        build and install" \
   "    ${green}update${norm}" \
   "        update your e17 to the latest version" \
   "    list" \
   "         list all available packages" \
   "    uninstall" \
   "        uninstall e17" \
   "" \
   "only:" \
   "     use only named packages" \
   "" \
   "plugin:" \
   "     call plugin hook on named plugins" \
   "" \
   "options:" \
   "    -h | --help" \
   "        show this page" \
   "    ${green}--git-e17-path${norm}" \
   "        workdir for this script. It will contain the source, config and log" \
   "        files and dirs in which e17 is build. The default git_e17_path should" \
   "        be ${green}set in ~/.git_e17${norm}" \
   "    -i | --ignore pkg1,pkg2,pkg2" \
   "    -s | --skip-errors" \
   "        skip non fatal errors" \
   "    ${bold}--dev-help${boldoff}" \
   "        developer help will give you more actions and options" \
   ""
}

devhelp(){
debug 1 "$FUNCNAME() $*"
#
# This functions displays the developer help file
#

printf '%b\n' \
   "" \
   "${red}Enable devmode with --dev on command line or dev=true in a configuration file.${norm}" \
   "" \
   "changed actions: (when in dev mode actions do not enforce additional options) " \
   "    build" \
   "        build and install" \
   "    update" \
   "        update source, build and install" \
   "extra actions:" \
   "    fromscratch" \
   "        delete e17 installation and install e17 from scratch" \
   "        ATTENTION: this will rm -fr $install_path" \
   "                   do not do is if you installed to /usr/local" \
   "    check" \
   "        check if your system is ready to install e17" \
   "        (In dev mode the check is not done on every run.) "\
   "" \
   "extra options:" \
   "    --dev" \
   "        options: sets noob_info=false" \
   "        avoids system checks" \
   "        there will be the time that you want it in you conf" \
   "    ${bold}-v | --verbose${boldoff}" \
   "        be more verbose during the build" \
   "    --conf <conf>" \
   "        config file - especially useful if you want multiple installs" \
   "        ONLY CONF NAME - NO FULL PATHS!!!" \
   "    --cflags  flags" \
   "    --ldflags flags" \
   "        you need the quotes!!" \
   "    -b | --branch <branch>" \
   "        branch, tag or commit to use" \
   "    -r | --remote <remote>" \
   "        select remote pull/fetch" \
   "    -p | --pull" \
   "        does a git pull --no-commit" \
   "    -f | --fetch-reset-hard" \
   "        ATTENTION: this will delete your local changes!!!!" \
   "        fetches update from remote, finds out your current branch" \
   "        and will do a git reset --hard remote/currentbrach" \
   "" \
   "    --clean / --distclean / --uninstall" \
   "        make clean / distclean / uninstall before building" \
   "    --vmake" \
   "        more verbose make (V=1)" \
   "    --conf_args" \
   "        extra configure args - for all packages" \
   "    -d | --debug" \
   "        enable script debugging mode" \
   "    -a | --animate"
}
### end help }}}

### parse command line
get_opts(){ #{{{
debug 1 "$FUNCNAME() $*"
section " reading command "
#
# This function reads the command line arguments
#
# in:
#   $@ - ARGV
#
# out:
#   sets: conf, branch, debug, verbose or calls help
#   TODO - idiot proof checking of arguments
#

copy_cfg

while :
do
    case $1 in
        --git-e17-path)
            conf[git_e17_path]="$2"
            echo "found option: --git-e17-path $2"
            shift 2
            ;;
        -c|--conf)
            conf[config_file]="$2"
            echo "found option: --conf $2"
            shift 2
            ;;
        -b|--branch)
            conf[branch]="$2"
            echo "found option: --branch $2"
            shift 2
            ;;
        -r|--remote)
            conf[remote]="$2"
            echo "found option: --remote $2"
            shift 2
            ;;
        -p|--pull)
            conf[pull]=true
            echo "found option: --pull"
            shift
            ;;
        -f|--fetch-reset-hard)
            conf[fetch]=true
            echo "found option: --fetch-reset-hard"
            shift
            ;;
        -s|--skip-errors)
            conf[on_error]=skip
            echo "found option: --skip-errors"
            shift
            ;;
        -a|--animate)
            conf[animate]=true
            conf[debug]=false
            conf[verbose]=false
            echo "found option: --animate"
            shift
            ;;
        -d|--debug)
            conf[debug]=true
            conf[animate]=false
            conf[debug_level]=4
            echo "found option: --debug"
            shift
            ;;
        -v|--verbose)
            conf[verbose]=true
            conf[animate]=false
            echo "found option: --verbose"
            shift
            ;;
        --cflags)
            conf[cflags]="$2"
            echo "found option: --cflags $2"
            shift 2
            ;;
        --ldflags)
            conf[ldflags]="$2"
            echo "found option: --ldflags $2"
            shift 2
            ;;
        --clean)
            conf[clean]=true
            echo "found option: --clean"
            shift
            ;;
        --distclean)
            conf[distclean]=true
            echo "found option: --distclean"
            shift
            ;;
        --uninstall)
            conf[uninstall]=true
            echo "found option: --uninstall"
            shift
            ;;
        -i | --ignore)
            conf[ignore]="$2"
            echo "found option: --ignore '$2'"
            shift 2
            ;;
        -d | --dev)
            conf[dev]="true"
            echo "found option: --dev '$2'"
            shift 1
            ;;
        --vmake)
            echo "found option: --vmake"
            conf[vmake]="true"
            shift 1
            ;;
        --dev-help)
            echo "found option: --dev-help"
            { help; devhelp; } | less -R
            exit 0
            ;;
        -h|--help)
            echo "found option: --help"
            help
            exit 0
            ;;
        *) #no more options beakb
            break
            ;;
    esac
done

#parse action parameter
debug 1 "action: $1"
if [[ "$1" == @(build|update|devupdate|devbuild|fromscratch|list|uninstall|quick|check) ]]; then
    conf[action]="$1"
    echo "selected action is ${conf[action]}"
    shift
else
    cmd_error
fi

if ! [[ "$1" ]]; then
    debug 1 "commandline actions are ok - options are not checked here"
elif [[ "$1" == only ]]; then
    shift
    packages="$@"
    echo "only: $packages"
elif [[ "$1" == plugin ]]; then
    shift
    packages="${@:-all}"
    echo "plugins: $packages"
    plugin
    exit
else
    cmd_error
fi

echo
print_cfg
debug_cfg
} #}}}

### get git_e17_path form ~/.git_e17 || create the file
get_git_e17_path_from_global_cfg(){ #{{{
debug 1 "$FUNCNAME() $*"
section " reading global configuration in ~/.git_e17 "
#
#   This function tries to read the global configuration file
#   or creates it. If the file exists two important config
#   items are read when they are not set by command line
#
#   out:
#       sets: conf[git_e17_path], conf[config_file]
#

copy_cfg

local conf_file="$HOME/.git_e17"

debug 1 "git e17 path: $1"
if [[ ! -f "$conf_file" ]]; then
    #create config
    echo "$(default_global_cfg)" > "$conf_file" || ferr "Unable to create $conf_file"

    if [[ ! ${conf[git_e17_path]} ]]; then
        echo -e "\nPlease edit your fresh created ~/.git_e17."
        echo    "Set the git_e17_path this dir will be the scripts working dir."
        echo    "It will contain logs, soure and build dir, configuration files"
        echo    "and other things."
    fi
fi

#read config values git_e17_path and config_file
while IFS='=' read -r item value; do
    if [[ "$item" == @(git_e17_path|config_file) ]]; then
        conf["$item"]=${conf["$item"]:-"$value"}
        debug 3 "conf[$item]=$value"
    fi
done <<< "$(grep -v ^# "$conf_file" | grep -v ^$ )"

#final check if path is set
debug 2 "The git e17 path must be set by now!! git_e17_path=${conf[git_e17_path]}"
if [[ ! ${conf[git_e17_path]} ]]; then
    ferr -e "git_e17_path is not set:(\n${e}please set it via commandline or ~/.git_e17"
fi

#remove trailing slash from git_e17_path
conf[git_e17_path]=${conf[git_e17_path]%/}

echo "git e17 path set to ${conf[git_e17_path]}"
echo "config file set to ${conf[config_file]}"

#check if git_e17_path exists
if ! [[ -d "${conf[git_e17_path]}" && -w "${conf[git_e17_path]}" ]]; then
    if ! mkdir -p "${conf[git_e17_path]}"; then
        ferr -e "unable to create or access ${conf[git_e17_path]}"
    fi
fi

echo
print_cfg
} #}}}

### read configuration in git e17 directory
read_local_cfg(){ #{{{
debug 1 "$FUNCNAME() $*"
section " reading git-e17-path configuration file (${conf[config_file]}) "
#
# This function checks if the config exists. If it does not the file is created.
# Then the configuration file is read and the values are put into the conf array.
# In case the file was newly created the user is asked if he wants to continue.
#
# out:
#   sets: ... a lot items in conf ...
#

copy_cfg

local new_cfg=false
local conf_file="${conf[git_e17_path]}/${conf[config_file]}"

if [[ ! -f "$conf_file" ]]; then
    echo "$(default_local_cfg)" > "$conf_file" || ferr "Unable to create $conf_file"
    new_cfg=true
fi

# read config
config="$(grep -v ^# < "$conf_file" | grep -v ^$)"
debug 3 "$config"
while IFS='=' read -r item value; do
    conf["$item"]=${conf["$item"]:-"$value"}
done <<< "$config"

print_cfg

if $new_cfg; then
    echo -e "\nEnlightenment 17 will be install to: ${bold} ${conf[install_path]} ${boldoff}"
    echo -e "To change this or other settings you can edit:"
    echo -e "${bold}$conf_file${boldoff}\n"
    [[ $(ask_yn "Do you want to proceed with the installation using the defaults?") == "yes" ]] || exit 1
fi
} #}}}

### read rest of ~/.git_e17 - sets only overwrite unset options
read_global_cfg(){ #{{{
debug 1 "$FUNCNAME() $*"
section " reading global cfg options "
#
# Read global configuration and copy items to conf
#

copy_cfg

config="$(grep -v ^# < "$HOME/.git_e17" | grep -v ^$)"
debug 3 "$config"
while IFS='=' read -r item value; do
    conf["$item"]=${conf["$item"]:-"$value"}
done <<< "$config"

print_cfg
}
### assign default values
assign_defaults(){
debug 1 "$FUNCNAME() $*"
section " assigning default vaules "

conf[install_path]="${conf[install_path]:-"/opt/e17"}"
#TODO - maybe the script should fail if the build path is not set
conf[build_path]="${conf[build_path]:-"${conf[git_e17_path]}/build"}"
conf[log_path]="${conf[log_path]:-"${conf[git_e17_path]}/log"}"
conf[packagelist]="${conf[packagelist]:-"full"}"
conf[on_error]="${conf[on_error]:-"ask"}"
conf[nice]="${conf[nice]:-"19"}"
conf[pull]=${conf[pull]:-false}
conf[fetch]=${conf[fetch]:-false}
conf[remote]="${conf[remote]:-"origin"}"
conf[noob_info]="${conf[noob_info]:-"true"}"
conf[dev]="${conf[dev]:-"false"}"


if [[ ! $packages ]]; then
    case "${conf[packagelist]}" in
        full)  packages="$packages_full"  ;;
        half)  packages="$packages_half"  ;;
        basic) packages="$packages_basic" ;;
        *)     ferr "No valid package list packagelist=full|half|basic" ;;
    esac
fi
print_cfg
} #}}}

interpret_action(){ #{{{
debug 1 "$FUNCNAME() $*"
section " interpret action "
#
#   Function interprets action and sets options
#
#   out:
#       sets: some options in conf so that they fit the selected action
#

copy_cfg

local action="${conf[action]}"


if ${conf[dev]}; then
    conf[noob_info]=false #we dont want no noobinfo at then end:
    case "$action" in
        build|devbuild)
            conf[action]="build"
            ;;
        update|devupdate)
            conf[action]="update"
            ;;
        quick)
            conf[action]="build"
            conf[animate]=true
            conf[debug]=false
            conf[verbose]=false
            conf[on_error]="skip"
            ;;
    esac
else
    case "$action" in
        build)
            conf[action]="build"
            conf[fetch]=false
            conf[pull]=false

            conf[animate]=true
            conf[debug]=false
            conf[verbose]=false
            conf[on_error]="skip"
            ;;
        update|quick)
            conf[action]="update"

            conf[branch]="master"
            conf[remote]="origin"
            conf[fetch]=true
            conf[pull]=false

            conf[animate]=true
            conf[debug]=false
            conf[verbose]=false
            conf[on_error]="skip"
            ;;
        devbuild)
            conf[action]="build"
            ;;
        devupdate)
            conf[action]="update"
            ;;
    esac
fi

case "$action" in
    fromscratch)
        (( $(id -u) == 0 )) && echo "idiot!!! Don't do this as root!!!" && exit 666
        rm -fr "${conf[install_path]}"
        rm -fr "${conf[build_path]}"
        rm -fr "${conf[src_path]}/trunk"
        conf[fetch]=true
        conf[action]="build"
        ;;
    list)
        #TODO
        list
        exit 1
        ;;
    check)
        conf[dev]=false #required otherwise distcheck will exit immediately
        distcheck
        exit 0
        ;;
esac

echo "These changes are forces by options"
print_cfg
} #}}}

### check for missing options and unwritable paths
check_cfg(){ #{{{
debug 1 "$FUNCNAME() $*"
section " checking configuration "
#
# check configuration
#

## FIXME ##
conf[theme_path]="$HOME/.e/e/themes" ## FIX-FOR-A-THEME
local paths="theme_path install_path git_e17_path log_path build_path log_path"
local output="debug debug_level dev verbose animate"
local conf_items="$paths src_path $output action config_file packagelist nice on_error"

for item in $conf_items; do
    if [[ -z "${conf["$item"]}" ]]; then
        ferr "Value for $item is not set - please edit your config file"
    fi
done

# check if directories are writeable
for path in $paths; do
    if [[ ! -d "${conf[$path]}" ]]; then
        mkdir -p "${conf[$path]}" && continue
        ferr "Unable to create the directory ${conf[$path]} - create it!"
    elif [[ ! -w "${conf[$path]}" ]]; then
        ferr "The directory ${conf[$path]} is not writeable for you:( - change that!"
    fi
done

#translate strings to bool
for key in ${!conf[@]}; do
    case ${conf[key]} in
        true|yes) conf[key]=true ;;
        false|no) conf[key]=false ;;
        *) continue ;;
    esac
done

echo "Configuration passed all checks."
debug_cfg
} #}}}

### uninstall e17
uninstall(){ #{{{
debug 1 "$FUNCNAME() $*"
[[ ${conf[action]} == uninstall ]] || return
section " uninstalling e17 "
#
# This functions uninstalls e17
#

#
# ok this is is frigging ugly:P - but it works :E
#

#enter build dir
cd "${conf[build_path]}" || { echo "uninstall failed"; exit 1; }

#visit all subdirs - they should match the package names
for dir in * ; do
    #change dir
    if ! cd "$dir" &>/dev/null ; then
        echo "failed to uninstall $dir"
        continue
    fi
    #test for makefile
    if [[ -e Makefile || -e makefile ]]; then
        write_name "$dir"
        #uninstall - try to run make uninstall
        nice -n "${conf[nice]}" bash -c "make uninstall" &>>"${conf[log_path]}/uninstall.log" &
        rotate $! "" "${conf[log_path]}/uninstall.log"
        wait
        echo "$ok"
    else
        echo "$fail"
    fi
    cd ..
done

exit 0
} #}}}

### functions that scan source code and select packages to be build
get_src(){ #{{{
debug 1 "$FUNCNAME() $*"
section " getting source code "
#
# Clone Repository if it does not exist and change into to it's directory
#
# out:
#   sets: g_current_branch, g_old_commit
#

g_current_failed=false

local src_path="${conf[src_path]}"
local git="${conf[git_repo]}"
local remote="${conf[remote]}"

#delete log file
rm -fr "${conf[git_e17_path]}/git.log"

#
#   TODO - check and double check that this is a reop or exit
#
#does the repo exist
if [[ ! -d ${conf[src_path]} ]]; then
    #create it
    echo "cloning ${conf[git_repo]}"
    for i in {0..10}; do
        echo "trying to clone git repository"
            if git clone -v --progress "${conf[git_repo]}" "$src_path"; then
                repo_cloned=true
                echo "successfully cloned git repo"
                break
            else
                echo "failed to clone repository"
            fi
    done
    $repo_cloned || ferr "unable to clone repository"
fi

#change to repo
echo "changing into $src_path"
cd "$src_path" || ferr "failed to cd in $src_path"

#get name of branch
g_current_branch="$(git symbolic-ref HEAD)"
g_current_branch="${g_current_branch##*/}"
[[ -n "$g_current_branch" ]] || ferr "You are not on any branch - Fix your repo!!!"

echo "Your current branch is $g_current_branch"

#get hash of current HEAD
g_current_commit="$(git log -1 --format="%H")"
g_old_commit="$(git log -1 --format="%H")"
echo "You are at commit $g_old_commit"
} #}}}

### fetch changes and reset repo to fetched head
git_fetch_reset(){ #{{{
debug 1 "$FUNCNAME() $*"
${conf[fetch]} || return
section " git fetch && git reset --hard "
#
# fetches latest
#

local remote="${conf[remote]}"
local branch="${conf[branch]:-"$g_current_branch"}"

#select right branch
echo "checking out $branch so that you local $branch matches the remote branch"
echo "i think this is a good default for people that are not messing with the code"
if [[ -n "$branch" && "$branch" != "$g_current_branch" ]]; then
    if ! git checkout ${conf[branch]}; then
        ferr "unable to checkout selected branch - fix your repository"
    fi
fi

echo "git fetch $remote"
git fetch $remote &> >( tee -a "${conf[git_e17_path]}/git.log" ) || nerr "git fetch failed"

echo "git clean -f -d"
git clean -f -d &> /dev/null

echo "git reset --hard  $remote/$branch"
git reset --hard "$remote/$branch" &> >( tee -a "${conf[git_e17_path]}/git.log" ) \
    || nerr "git reset --hard failed"
} #}}}

### pull changes into repo
git_pull(){ #{{{
debug 1 "$FUNCNAME() $*"
${conf[pull]} || return
section " git pull --no-commit "
#
#   pull updates into branch
#
section " dear god we all hope there are no conflicts "
git pull --no-commit $remote &> >( tee -a "${conf[git_e17_path]}/git.log" ) || nerr "git pull failed"
} #}}}

### calculate updates
calculate_updates(){ #{{{
debug 1 "$FUNCNAME() $*"
[[ "${conf[action]}" == "update" ]] || return
section " calculating changes "
#
#   Calculates changes to packages du to to fetch / pull operations
#

g_current_commit="$(git log -1 --format="%H")"

## Speed this up
write_name "building path map"
local -A path_map
for pkg in $packages; do
    find_local_path "$pkg" "${conf[src_path]}" "/trunk"
    local path="${conf[current_package_path]}"
    if [[ -n "$path" ]]; then
        path=${path##"$src_path"}
        path_map["$pkg"]="trunk/$path/"
        debug 3 "map $pkg: ${path_map[$pkg]}"
    else
        debug 3 "unable to map $pkg to source location"
    fi
done

#rotate does not work here
#rotate $! "" "no_file"
#wait
echo -e "$ok \n"

packages_new=""
echo "checking packages for changes: "
echo "package name .............. update status - (install status)"
echo

for pkg in $packages; do
    if [[ $pkg == __* ]]; then
        packages_new+="$pkg "
        continue
    fi
    debug 3 "map $pkg: ${path_map[$pkg]}"
    if [[ -n "${path_map[$pkg]}" ]]; then
        write_name "$pkg"
        # calulate the new dir per package
        #find the last successful install/build
        local succ_build="$(grep "^$pkg" "${conf[build_path]}/git_e17_status")"

        if [[ -z "$succ_build" ]]; then
            #build it because we have no info
            packages_new+="$pkg "
            echo "never build"
            continue
        fi

        local stat try succ_commit fail
        IFS=: read -r _ stat try succ_commit fail <<< "$succ_build"
        if [[ "$succ_commit" == "na" ]]; then
            #build it because it was never build successfully
            packages_new+="$pkg "
            echo "never build"
            continue
        fi

        debug 3 "last successfully bulid commit: $succ_commit"
        #get files that differ form last successfully build commit
        new_files="$(git diff --name-only "$succ_commit" -- "${path_map[$pkg]}" 2>/dev/null)"

        if [[ $? -ne 0 ]]; then
            #build it because git diff failed.
            packages_new+="$pkg "
            #echo "git diff --name-only $succ_commit -- ${path_map[$pkg]} failed"
            echo "git diff failed"
            continue
        fi

        debug 4 "new files for $pkg: $new_files"

        local new_dirs="$(
            while read -r line; do
                echo "${line%/*}/"
            done <<< "$new_files"
        )"

        local new_dirs_uniq="$( echo "$new_dirs" | sort | uniq 2>/dev/null)"

        debug 4 sleep "new_dirs: $new_dirs_uniq"

        #build it because there are changes in the path
        if echo $new_dirs_uniq | grep -q -E  "${path_map[$pkg]}.*" ; then
            echo "changed - ($stat)"
            packages_new+="$pkg "
        else
            echo "unchanged - ($stat)"
        fi
    fi
done

packages="$packages_new"
if [[ "$packages" ]]; then
    echo -e "The following packages are not up to date: \n"
    echo_long "$packages"
else
    echo -e "All packages are up to date! There is no need to update!!\n"
    exit 0
fi
} #}}}

### add failed packags to rebuild list
add_failed(){ #{{{
debug 1 "$FUNCNAME() $*"
section " looking for failed packages "
#
# add failed packages to packages that should be build
#
echo "not implemented"
} #}}}

### mark package as failed
pkg_failed(){ #{{{
debug 1 "$FUNCNAME() $*"
#
# add to failed
#
msg -e " - Adding $1 to failed list of failed packages\n"

local status_file="${conf[build_path]}/git_e17_status"
status_content="$(cat "$status_file")"
local name stat try succ fail
while IFS=: read -r name stat try succ fail; do
    if [[ "$pkg" == "$name" ]]; then
        echo "$name:failed:$try:$succ:$g_current_commit"
    else
        echo "$name:$stat:$try:$succ:$fail"
    fi
done <<< "$status_content" > "$status_file"

packages_failed+="$pkg "
} #}}}

### mark package as successfully build
pkg_success(){ #{{{
debug 1 "$FUNCNAME() $*"
#
# remove from failed
#
local status_file="${conf[build_path]}/git_e17_status"

local name stat try succ fail
status_content="$(cat "$status_file")"
while IFS=: read -r name stat try succ fail; do
    if [[ "$pkg" == "$name" ]]; then
        echo "$name:success:$try:$g_current_commit:$fail"
    else
        echo "$name:$stat:$try:$succ:$fail"
    fi
done <<< "$status_content" > "$status_file"
} #}}}

###
ignore_pkgs(){ #{{{
debug 1 "$FUNCNAME() $*"
[[ "${conf[ignore]}" ]] || return
section " ignore packages "
#
# remove packages to ignore to from packages
#

local ignore=${conf[ignore]//,/ }
echo "ignoring packages $ignore"
packages="$(sub_lists "$packages" "$ignore")"
} #}}}

### set up the build environment
set_env(){ #{{{
debug 1 "$FUNCNAME() $*"
local install_path="${conf[install_path]}"

section " setting environment variables "

export PATH="$install_path/bin:$PATH"
echo "PATH=$PATH"
export ACLOCAL_FLAGS="-I $install_path/share/aclocal $ACLOCAL_FLAGS"
echo "ACLOCAL_FLAGS=$ACLOCAL_FLAGS"
export LD_LIBRARY_PATH="$install_path/lib:$LD_LIBRARY_PATH"
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
export PKG_CONFIG_PATH="$install_path/lib/pkgconfig:$PKG_CONFIG_PATH"
echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH"
export CPPFLAGS="$CPPFLAGS -I$install_path/include"
echo "CPPFLAGS=$CPPFLAGS"
export LDFLAGS="$LDFLAGS -L$install_path/lib ${conf[ldflags]}"
echo "LDFLAGS=$LDFLAGS"
export LD_RUN_PATH="$install_path/lib"
echo "LD_RUN_PATH=$LD_RUN_PATH"
export CFLAGS="$CFLAGS ${conf[cflags]}"
echo "CFLAGS=$CFLAGS"
export PYTHONPATH="$($pythoncmd -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(prefix='$install_path')" 2>/dev/null)"
echo "PYTHONPATH=$PYTHONPATH"
export PYTHONINCLUDE="$($pythoncmd -c "import distutils.sysconfig; print distutils.sysconfig.get_python_inc(prefix='$install_path')" 2>/dev/null)"
echo "PYTHONINCLUDE=$PYTHONINCLUDE"
export NOCONFIGURE="true"
echo "NOCONFIGURE=$NOCONFIGURE"
export V=1
echo "V=$V"
echo


if ${conf[verbose]}; then
    sleep 5
fi

echo 'checking existence of $install_path/share/aclocal $PYTHONPATH $PYTHONINCLUDE ... '
local failed=false
for dir in "$install_path/share/aclocal" "$PYTHONPATH" "$PYTHONINCLUDE"; do
    if [[ ! -w "$dir" ]]; then
        if ! mkdir -p "$dir"; then
            echo
            echo "please create the $dir as root and allow you user to wirte to it"
            echo "sudo mkdir -p $dir"
            echo "sudo chown $USER:$USER $dir"
            failed=true
        fi
    fi
done
## TODO ## OUTPUT
$failed && ferr "can not access all required directories"
} #}}}

#calls build in main script
build_main(){ #{{{
section " entering build phase "
echo -e "The following packages will be build:\n"
echo_long "$packages"
echo

build "${conf[src_path]}" "/trunk" "${conf[build_path]}"
} #}}}}

### build
### calls: find_local_path, compile
build(){ #{{{
debug 1 "$FUNCNAME() $*"
#
# This functions should find the
#
local repo_path="$1"
local trunk="$2"
local build_path="$3"

local status_file="${conf[build_path]}/git_e17_status"

[[ -e "$status_file" ]] || touch "$status_file"

for pkg in $packages; do
    #
    # please use only
    # msg     - for normal and verbose mode messages or
    # msg_ani - for animation mode messages
    # within this loop for output
    #
    if [[ $pkg == __* ]]; then
        echo -e "${bold}${packages_msg["$pkg"]}${norm}"
        continue
    fi

    #skip-packages
    if [[ $pkg == --* ]]; then
        continue
    fi

    # TODO - ormaaj suggests to use temp files here - I was too lazy ...
    status_content="$(cat "$status_file")"      #BAD STYLE
    local name_found=false
    local name stat try succ failed
    while IFS=: read -r name stat try succ failed; do
        if [[ "$pkg" == $name ]]; then
            echo "$name:$stat:$g_current_commit:$succ:$failed"
            name_found=true
        else
            if [[ -n "$name" ]]; then
                echo "$name:$stat:$try:$succ:$failed"
            fi
        fi
    done <<< "$status_content" > "$status_file" #BAD STYLE
    if ! $name_found; then
        echo "$pkg:x:$g_current_commit:na:na" >> "$status_file"
    fi


    msg "- trying to build $pkg ..."

    #initialize vars
    debug 2 "init vars"
    g_current_failed=false
    conf[current_package_path]=""

    #find src
    find_local_path "$pkg" "$repo_path" "$trunk"
    local cpath="${repo_path}${trunk}/${conf[current_package_path]}"
    local bpath="${conf[build_path]}/$pkg"

    if [[ -z "$cpath" ]]; then
        nerr "source path of of package $pkg not found"
        msg_ani "SKIP"
        continue
    else
        #for animation
        ${conf[animate]} && write_name "$pkg"

        fix_pkg "$pkg" "$cpath" "$bpath"

      ###compile it!
        declare -g g_retry=true
        while $g_retry; do
            g_retry=false
            compile "$pkg" "$cpath" "$bpath"

            if ${conf[animate]} && $g_current_failed; then
            ## BEGIN ANIMATION MODE OUTPUT
            #  this is the place for extra information in
            #  animation mode - it is shown when a building step failed
                msg_ani "$fail"
                pkg_err "$pkg"
                $g_retry && write_name "$pkg"
            fi
        done
      ####

      #### management of failed / successful packages
        if $g_current_failed; then
            #
            # ADD PKG TO FAILED
            pkg_failed "$pkg"
        else
            #for animation
            msg_ani "$ok"
            if [[ $pkg == e ]] && ! ${conf[dev]}; then
                echo -e "${bold}E17 is installed :) The rest is optional!!\nDo not bug me or people in #e if other packages fail!${norm}"
            fi
            # REMOVE PKG FROM FAILED
            pkg_success "$pkg"
        fi
    fi
done

[[ $packages_failed ]] && echo -e "\n${bold}The following packages failed to build:\n${red}$packages_failed${norm}"
} #}}}

### find the source code of a package
find_local_path (){ #{{{
debug 1 "$FUNCNAME() $*"
#
# Function that should find the source path to the current package
#
# in:
#   $1 - package name
#   $2 - path to repo
#   $3 - trunk
# out:
#   sets: conf[current_package_path]
#
local name="$1"
local repo_path="$2"
local trunk="$3"

local path=""
local src_path="${repo_path}${trunk}"

conf[current_package_path]="$path"

#prune dirs to ignore
local prune=""
for dir in devs DOCS E16 EXAMPLES TEST web packaging; do
    prune="-o -path $src_path/$dir $prune"
done
prune=${prune#-o}

#use find to get possible dirs
#debug-x
dirs="$(find "$src_path" -maxdepth 3 -type d \( $prune \) -prune -o -type d -name "$name" -printf "%P\n" )"
#debug+x
debug 5 "$dirs"

#pick the one with the shortest path
local path_len=666
while read -r path ;do
    if [[ -d "$src_path/$path" ]] && (( ${#path} < $path_len ));  then
        conf[current_package_path]="$path"
        path_len=${#path}
    fi
done <<< "$dirs"
debug 5 "path selected for $pkg: $src_path/${conf[current_package_path]}"

if [[ -z "${conf[current_package_path]}" ]] ; then
    debug 1 "Source path for $pkg was not found!"
fi
} #}}}

### sets up configure parameters
### calls: run_cmd
compile(){ #{{{
debug 1 "$FUNCNAME() $*"
#
#
#
local pkg="$1"
debug 1 "package: $pkg"
local src_path="$2"
debug 1 "srcpath: $src_path"
local build_path="$3"
debug 1 "build_path: $build_path"

g_current_failed=false

local install_path="${conf[install_path]}"
local pkg_conf_args="${conf["${pkg}_conf_args"]}"
local build_type=""
local log_file="${conf[log_path]}/$pkg.log"
[[ -e "$log_file" ]] && rm "$log_file"

# find type of build process
if   [ -e "$src_path/CMakeLists.txt" ];
    then build_type="cmake"
elif   [ -e "$src_path/autogen.sh" ];
    then build_type="autogen"
elif [ -e "$src_path/bootstrap" ];
    then build_type="bootstrap"
elif [ -e "$src_path/Makefile.PL" ];
    then build_type="perl"
elif [ -e "$src_path/Makefile" ]; then
    build_type="make"
    export PREFIX="${conf[install_path]}"
elif [ -e "$src_path/setup.py" ];
    then build_type="python"
elif [ -e "$src_path/configure" ];
    then build_type="configure"
else
    export PREFIX=""
fi

debug 1 "Type of build system $build_type"

declare -g g_clean_phase=true
case "$build_type" in
    autogen|bootstrap|cmake|configure)
        ${conf[uninstall]} && run_cmd "$pkg" "$src_path" "$build_path" "make uninstall" "make uninstall"
        g_current_failed=false
        ${conf[clean]}     && run_cmd "$pkg" "$src_path" "$build_path" "make clean"     "make clean"
        g_current_failed=false
        ${conf[distclean]} && run_cmd "$pkg" "$src_path" "$build_path" "make distclean" "make distclean"
        g_current_failed=false
        ;;&
    make|perl)
        ${conf[uninstall]} && run_cmd "$pkg" "$src_path" "" "make uninstall" "make uninstall"
        g_current_failed=false
        ${conf[clean]}     && run_cmd "$pkg" "$src_path" "" "make clean"     "make clean"
        g_current_failed=false
        ;;
    *)
        :
        ;;
esac
g_clean_phase=false
g_current_failed=false

#use the power of ;;& - muahahh
#UGLY HACK!!
  case "$build_type" in
      autogen)
          run_cmd "$pkg" "$src_path" "" "autogen" "./autogen.sh"
          ;;&
      cmake)
          run_cmd "$pkg" "$src_path" "" "cmake" "cmake -DCMAKE_INSTALL_PREFIX=$install_path $pkg_conf_args $src_path"
          ;;&
      bootstrap)
          run_cmd "$pkg" "$src_path" "" "boostrap" "./bootstrap"
          ;;&
      perl)
          run_cmd "$pkg" "$src_path" "$build_path" "perlmake" "perl $src_path/Makefile.PL prefix=$install_path"
          ;;&
      autogen|bootstrap|configure) #configure
          $g_current_failed || run_cmd "$pkg" "$src_path" "$build_path" "configure" "$src_path/configure --prefix=$install_path $pkg_conf_args"
          ;;&
      autogen|cmake|bootstrap|perl|configure) # make && make install
        $g_current_failed || run_cmd "$pkg" "$src_path" "$build_path" "make" "make -j ${conf[threads]}"
        $g_current_failed || run_cmd "$pkg" "$src_path" "$build_path" "install" "make install"
          ;;
      make) # make && make install
        run_cmd "$pkg" "$src_path" "" "make" "$make_extra make -j ${conf[threads]}"
        $g_current_failed || run_cmd "$pkg" "$src_path" "" "install" "make install"
          ;;
      python)
        run_cmd "$pkg" "$src_path" "" "setup.py-build" "python setup.py build build_ext --include-dirs=$PYTHONINCLUDE $pkg_conf_args"
        $g_current_failed || run_cmd "$pkg" "$src_path" "" "setup-py-install" "$pythoncmd setup.py install --prefix=$install_path install_headers --install-dir=$PYTHONINCLUDE"
          ;;
      *)
          msg "  - no build system ... $fail"
          g_current_failed=true
          return
  esac

} #}}}

### runs the commands that are actually used to build the code
### calls: rotate
run_cmd(){ #{{{
debug 1 "$FUNCNAME() $*"
#
# in:
#   $1 - pkg - package name
#   $2 - src - source path
#   $3 - build - path in which configure will be run
#   $4 - title - titel of the displayed action while the command is running
#   $@ - the command to be run
#
# out:
#   compiles the code and sets g_current_failed in case of failure
#
#   FUNCTION NEEDS TO BE REWRITTEN - it is UGLY and STUIP
#   a better way than writing $@ to call the command would be nice
#
#   the 3 different calls might be handled in another way
#   maybe it is better to write a separate run_animated function
#

local pkg="$1"
local src="$2"
local build_path="$3"
local title="$4"
shift 4

if [[ -n "$build_path" ]]; then
    #clean it whatever it means
    mkdir -p "$build_path" || on_error "unable to create $build_path"
    cd "$build_path" || on_error "unable to cd into $build_path"
else
    cd "$src" || on_error "unable to cd into $src"
fi

## check if makefile is available
if [[ $title == "make"* ]]; then
    if ! [[ -e Makefile || -e makefile ]]; then
    #TODO - elif check source dir and cd into it
        g_current_failed=true
        #We will not fail in clean_phase

        # make sure animation mode is not interrupted
        ${conf[animate]} && return
        # BEGIN NORMAL OUTPUT
        msg -n "  - $title ... "
        if $g_clean_phase; then
            msg  "$fail - make may fail during clean phase"
        else
            msg  "$fail"
            msg  "no Makefile :("
            pkg_err "$pkg"
        fi
        # BEGIN NORMAL OUTPUT
        return
    fi
fi

log_file="${conf[log_path]}/$pkg.log"
debug 1 "running in path: $(pwd)"

echo -e "\n\n" >>"$log_file"
echo -e "git_e17 - $title\n" >>"$log_file"
echo -e "command:  $* " >>"$log_file"
echo -e "dir:      $(pwd)" >>"$log_file"
echo -e "revision: $g_current_commit \n\n" >>"$log_file"

#run only if pre checks did not fail
if ! $g_current_failed; then
    #if animation mode is used
    if ${conf[animate]}; then
        #call
        nice -n "${conf[nice]}" bash -c "$@" &>>"$log_file" &
        #get pid
        pid="$!"
        #start animation
        ( rotate "$pid" "${title}: " "$log_file" ) &
        #wait for command to finish and check exit status
        if ! wait $pid; then
            g_current_failed=true
        else
            #check log for errors
            if tail -n 20 "$log_file" | grep -q -i error &> /dev/null; then
                g_current_failed=true
            fi
        fi
        #wait for animation - is that necessary?
        wait
        # success
        #return here and now do not go any further - HACK!!!
        return

    #if a more verbose mode is used
    elif ${conf[verbose]} ; then
        echo -e "  - RUNNING: ${bold}$@${boldoff} ... "
        echo -e "         IN: $(pwd)\n"
        sleep 2
        if ! nice -n "${conf[nice]}" bash -c "$@" &> >(tee -a "$log_file"); then
            g_current_failed=true
        else
            if tail -n 20 "$log_file" | grep -q -i error &> /dev/null; then
                g_current_failed=true
            else
                echo
                return
            fi
        fi
    #if normal mode is used
    else
        msg -n "  - $title ... "
        if ! nice -n "${conf[nice]}" bash -c "$@" &>>"$log_file"; then
                g_current_failed=true
        else
            if tail -n 20 "$log_file" | grep -q -i error &> /dev/null; then
                g_current_failed=true
            else
                msg "$ok"
                return
            fi
        fi
    fi
    msg "$fail"
    #
    # NORMAL AND VERBOSE MODE OUTPUT HERE !!!!!!
    #
    echo
    tail -n 30 "$log_file"
    echo
    $g_clean_phase || pkg_err $pkg
        :
fi #end of pre checks of
} #}}}

### fix packages
fix_pkg(){ #{{{
debug 1 "$FUNCNAME() $*"
#
# fix packages
#

local pkg="$1"
local cpath="$2"
local bpath="$3"

case $pkg in
    drawer|itask|winlist-ng|enlil|enki)
        msg -n "  - fixing ... "
        rsync -av "$cpath/" "$bpath/" &>/dev/null
        msg "PRAY!"
        ;;
    evas)
        #delete old svg loader instead of unistalling evas
        rm -fr "${conf[install_path]}/lib/evas/modules/loaders/svg" 2>/dev/null
        ;;
esac
} #}}}
##### functions used in main }}}

#### distcheck functions {{{

distcheck(){ #{{{
#echo -e "${bold}checking distribution ...${boldoff}"
debug 1 "$FUNCNAME() $*"
section " distribution setting and checks "

#allow distros to set their own python command
# TODO - this should probably go into conf[]
# or up to the other global vars. on the other hand
# people are more likely to find it here
pythoncmd="python"

# Default binaries and libs to check.
# Can be overriden by distribution (See Arch)
deps_bin="git make automake byacc g++ gcc libtool pkg-config"
deps_dev="dbus-1 fontconfig freetype GL jpeg lua5.1 png udev xml2 X11 Xext Xrandr xcb"

#find  distribution
if [ -e "/etc/debian_version" ]; then
    echo "found distribution debian"
    ! ${conf[dev]} || return
    debian_check
elif [ -e "/etc/arch-release" ] ; then
	echo "found distribution arch"
    deps_bin="${deps_bin/byacc/yacc}"
    deps_dev="${deps_dev/lua5.1/lua}"
    pythoncmd="python2"
    ! ${conf[dev]} || return
	archlinux_check
elif [ -e "/etc/fedora-release" ]; then
    echo "found distribution fedora"
    deps_dev="${deps_dev/lua5.1/lua}"
    ! ${conf[dev]} || return
    fedora_check
    unsupported_dist_note
elif [ -e "/etc/gentoo-release" ]; then
    echo "found distribution gentoo"
    ! ${conf[dev]} || return
    unsupported_dist_note
elif [ -e "/etc/redhat-release" ]; then
    echo "found distribution redhat"
    ! ${conf[dev]} || return
    unsupported_dist_note
elif [ -e "/etc/SuSE-release" ]; then
    echo "found distribution Suse"
    ! ${conf[dev]} || return
    unsupported_dist_note
else
    echo "did not find your distribution"
    ! ${conf[dev]} || return
    unsupported_dist_note
fi

#
#   general check
#
echo -e "${bold}proceeding with general checks\n${boldoff}"
local failed=false

echo -e "checking commands:"
for bin in $deps_bin; do
    write_name "$bin"
    if ! type $bin &> /dev/null; then
        failed=true
        echo "$fail - missing"
    else
        echo "$ok "
    fi
done

echo -e "\nchecking includes:"
compfile="/tmp/include_test.c"
echo "main(){}" >$compfile
for dep in $deps_dev; do
    write_name "$dep"
    if ! gcc -o /dev/null $compfile -l$dep &>/dev/null; then
        failed=true
        echo "$fail - include missing"
    else
        echo "$ok"
    fi
done

$failed && ferr "Please install the missing build dependencies"
} #}}}

##  debian specific stuff
debian_check(){ #{{{
debug 1 "$FUNCNAME() $*"

#must be tested for stable
local deps_debian_common="git autoconf automake1.9 autotools-dev autoconf-archive gettext libtool libfreetype6-dev "
deps_debian_common+="libpng12-dev libtiff4-dev libgif-dev libbz2-dev libltdl-dev pkg-config libxine-dev build-essential flex bison byacc "
deps_debian_common+="libxcursor-dev libtag1-dev sqlite libxml2-dev libsqlite3-dev libxslt1.1 libxslt1-dev giblib1 "
deps_debian_common+="giblib-dev libtool libtagc0-dev libmpd1 libmpd-dev libxcomposite-dev libxcomposite1 libxdamage-dev "
deps_debian_common+="libxdamage1 libxkbfile-dev libxkbfile1 libxkbfile-dev libxkbfile1 libdbus-1-dev libtheora-dev libpopt-dev "
deps_debian_common+="libglib2.0-dev libfontconfig1-dev libxrandr-dev libasound2-dev libxinerama-dev cvs automake libgstreamer0.10-dev menu "
deps_debian_common+="menu-xdg xdg-utils liblua5.1-0-dev dbus-x11 libexif-dev mesa-common-dev libudev-dev cython "
deps_debian_common+="libxcb-shape0-dev python-dev libcanberra-dev libtuxcap-dev chipmunk-dev autopoint libpoppler-private-dev "
deps_debian_common+="libxcb-keysyms1-dev x11proto-print-dev libxp-dev libxtst-dev libfribidi-dev libxss-dev libmount-dev "
deps_debian_common+="libblkid-dev check libsndfile1-dev libgstreamer-plugins-base0.10-dev"

##TODO finish the lists
local stable="libpam-dev libiptcdata-dev libcurl4-gnutls-dev libsdl-dev "
local sid="libpam0g-dev libiptcdata0-dev libcurl4-openssl-dev libsdl1.2-dev"

## use this for now
local deps_debian_sid="$deps_debian_common $sid"

local pkg_list="$(dpkg -l)"
local missing=""

echo -e "\n ### This check should work for Debian wheezy/sid and Ubuntu precise ###"
echo -e "\ndependences:"
echo_long "${deps_debian_sid}\n"

write_name "checking packages"
for dep in $deps_debian_sid; do
    if ! echo "$pkg_list" | grep -q -E "^ii\s*$dep" &>/dev/null ; then
        missing+="$dep "
    fi
    wait
done


if [[ $missing ]]; then
    echo -e "${fail}\n\nThe following packages are missing:\n${missing}\n"
    if (( $(id -u) == 0 )); then
        if [[ $(ask_yn "do you want to install the missing packages?") == "yes" ]]; then
            apt-get install $missing || exit 1
        fi
    else
        ask_to_cont "Do you want to proceed with the installation ?"
    fi
else
    echo -e "${ok} \n"
fi
} #}}}

archlinux_check(){
local deps_arch="git autoconf automake binutils bison fakeroot flex gcc libtool m4 make patch pkg-config "
deps_arch+="dbus fontconfig freetype2 libjpeg-turbo lua libpng libxml2 libx11 libxext libxrandr libxcb libxp"

local missing=""

echo -e "\n ### Checking Deps... ###"

for dep in $deps_arch; do
	if ! pacman -Qs $dep | grep -q -E "^local/$dep " &>/dev/null ; then
		missing+="$dep "
	fi
	wait
done

if [[ $missing ]]; then
	echo -e "${fail}\n\nThe following packages are missing: \n${missing}\n"
	if (( $(id -u) == 0 )); then
		if [[ $(ask_yn "do you want to install the missing packages?") == "yes" ]]; then
			pacman -S $missing || exit 1
		fi
	else
        ask_to_cont "Do you want to proceed with the installation ?"
	fi
else
	echo -e "${ok} \n"
fi
}

fedora_check(){

echo "the following packages should be installed:"
echo
echo "git subversion automake autoconf libtool libtool-ltdl libtool-ltdl-devel gcc"
echo "libX11-devel libpng-devel libjpeg-devel giflib-devel libtiff-devel bzip2-devel"
echo "libid3tag libid3tag-devel id3lib-devel id3lib fontconfig-devel libXext-devel2"
echo "libXrender-devel libXcomposite-devel libXdamage-devel libXrandr-devel"
echo "libXcursor-devel libXinerama-devel openssl-devel curl-devel pam-devel"
echo "freetype-devel texinfo gettext-devel xine-lib-devel flex bison libxml2-devel"
echo "libxslt-devel taglib-devel sqlite-devel giblib-devel libxkbfile-devel"
echo "librsvg2-devel libmpd libmpd-devel gcc-c++ libogg-devel libtheora-devel"
echo "dbus-devel cvs libudev-devel lua-devel libXp-devel pulseaudio-libs-devel "
echo "libsndfile-devel libmount-devel bullet-devel xcb-util-keysyms-devel"
echo
}

unsupported_dist_note(){
echo "there are currently no checks for you distribution / OS available"
echo "please send me a patch or tell me which packages are required"
echo "and how to check their presence"
}

#### distcheck functions }}}

#### Helper functions {{{
inter_lists(){
#intersect list $1 and $2
local result=""
for list1_item in $1; do
    for list2_item in $2; do
        if [[ "$list1_item" == "$list2_item" ]]; then
            result+="$list1_item "
        fi
    done
done
}

# subtract 2nd list from first
sub_lists(){
local result=""
for list1_item in $1; do
    for list2_item in $2; do
        if ! [[ "$list1_item" == "$list2_item" ]]; then
            result+="$list1_item "
        fi
    done
done
}
sort_as_full(){
    #bring items in $@ in the same order as in $packages_full
    inter_lists "$packages_full" "$@"
}
## OUTPUT
write_bold(){ tput bold; echo "$@"; tput sgr0; }

msg(){ ${conf[animate]} || echo "$@"; }             #normal mode message - only when not animated

verbose(){ ${conf[verbose]} && echo -e "$@";}       #verbose message

msg_ani(){ ${conf[animate]} && echo "$@"; }         #animation mode message

# print scetion banner
section(){
    local fill_symbol="-"
    local title="$@"

    local width=80
    local fill=$(( width-${#title} ))
    local rest=$(( fill % 2 ))
    fill=$(( fill / 2))
    #printf "$fill_symbol%.0s" {1..$(( fill + rem ))}  - fuck no
    for (( n=1 ; n <= fill ; n++ )); do echo -n "$fill_symbol" ; done
    tput bold
    echo -n "$title"
    tput sgr0
    for (( n=1 ; n <= (fill+rest) ; n++ )); do echo -n "$fill_symbol" ; done
    echo
}

## print configuration
copy_cfg(){
unset g_old_conf
declare -g -A g_old_conf
for key in "${!conf[@]}"; do
    g_old_conf["$key"]="${conf["$key"]}"
done
}

print_cfg(){
local changed=false
local changes="$(
for item in "${!conf[@]}"; do
    if [[ "${conf["$item"]}" != "${g_old_conf["$item"]}" ]]; then
        echo "$item=${conf["$item"]}"
        changed=true
    fi
done)"

if [[ "$changes" ]]; then
    echo "The following Configuration items changed:"
    echo "$changes"
else
    echo "The configuration did not Change!"
fi
}

echo_padd(){
local len="$1"
local padd="$2"
local args=
shift 2
## get aditional arguments
while : ; do
    if [[ $1 == @(-n|-e|-ne) ]]; then
        args+=$1
        shift
    else
        break
    fi
done

msg="$@ "
#fill with padd
while (( ${#msg} < len-1 )); do
    msg+="$padd"
done
msg+=" "
#print
echo $args "$msg"
}

echo_long(){
# breaks a long line in single lines that fit width
# if the first argument is -f the second is inseted
# in the beginning of the lines

local fill=""
if [[ "$1" == "-f" ]]; then
    fill="$2"
    shift 2
fi
# break output at width
local line_out="$fill"
local width=75
for item in $@; do
    if (( (${#line_out} + ${#item})  > width )); then
        echo -ne "${line_out}\n"
        line_out="${fill}"
    else
        line_out+="$item "
    fi
done
echo -ne "${line_out}\n"
}

print_array(){
#call: print_array array[@]
declare -a array=("${!1}")
for item in "${array[@]}"; do
    echo "$item"
done
}

## ERROR
err(){ echo; echo -n "$error "; echo "$@"; }
#fatal error
ferr(){ err "$@"; exit 1; }
#non fatal error

nerr(){
debug 1 "$FUNCNAME() $*"
[[ ${conf[on_error]} == "skip" ]] && return
g_current_failed=true
err "$@"
case ${conf[on_error]} in
    exit) exit 1 ;;
    ask)  local rv="$(ask_yn "The Program encountered an error - do you want to continue?")"; [[ "$rv" == "no" ]] && exit 1 ;;
esac
}

# ask to continue on error
ask_yn(){
while true; do
    read -p "$@ (y/n) " yn
    case "$yn" in
        [Yy]*) echo "yes"; break ;;
        [Nn]*) echo "no" ; break ;;
        *) echo "Please answer with yes or no." 1>&2 ;;
    esac
done
}
ask_to_cont(){
    [[ $(ask_yn "$1") == "yes" ]] || exit 1
}

pkg_err(){
debug 1 "$FUNCNAME() $*"
[[ ${conf[on_error]} == "skip" ]] && return
g_current_failed=true
local pkg="$1"
shift
err "The installation of package $pkg failed!"
case ${conf[on_error]} in
    exit)
        #
        # ADD PGK TO FAILED
        pkg_failed "$pkg"
        exit
        ;;
    ask)
        while true; do
            read -p "       Do you want to continue, retry or quit?  (${bold}Y${boldoff}es/${bold}R${boldoff}etry/${bold}Q${boldoff}uit) " yrq
            case "$yrq" in
                [Rr]*)
                    declare -g g_retry=true
                    break 1
                    ;;
                [Yy]*)
                    break
                    ;;
                [Qq]*)
                    #
                    # ADD PKG TO FAILED
                    pkg_failed "$pkg"
                    exit 1
                    ;;
                *) echo "Please answer with yes, no, retry." 1>&2 ;;
            esac
        done
        echo
        ;;
    *)
        ;;
esac
}

cmd_error(){
#section ""
err -e "No vaild command! See --help for usage.\n"
echo -e "       Or try: ./git_e17 --git-e17-path ~/path/to/work-dir build\n"
exit 1
}

## helper debuging
debug(){
#
# This function displays debug information
# when the debug level set via command line
# is greater or equal the level passed to
# the function
#
# In:
# $1 - level
# $2 - message
#
${conf[debug]} || return
local action=""
local level="$1"
shift
if [[ "$1" == @(ask|sleep) ]]; then
    action="$1"
    shift
fi
msg="$@"

if (( level <= ${conf[debug_level]} )); then
    while read -r debug_line <&13; do
        printf '%q\n' "debug $level: $debug_line" 1>&2
    done 13<<< "$msg"
fi
[[ "$action" == "sleep" ]] && sleep 5
[[ "$action" == "ask" ]]   && read -p "press key to continue" -n 1
}

debug-x(){ ${conf[debug]} && set -x; }
debug+x(){ ${conf[debug]} && set +x; }

## debug configuration
debug_cfg(){
${conf[debug]} || return
echo
debug 4 "current configuration:"
for item in ${!conf[@]}; do
    debug 4 "$item=${conf["$item"]}"
done
echo
}

### fancy output functions
### taken from easy e17.sh - the code ist mostly unrevised:(
del_word(){
cnt=0
max=${#1}
while (( cnt < max )); do
    echo -n -e "\b \b"
    (( cnt++ ))
done
}

## TODO - rewrite
rotate ()
{
local pid=$1
local name="$2"
local log_file=$3
local animation_state=1
local log_line=""
echo -n "$name"
echo -n "     "
while [[ "$(ps -p $pid -o comm=)" ]]; do
    [[ $log_file == "no_file" ]] || last_line=$(tail -1 "$log_file")
    if [[ $log_file == "no_file" ]] || [[ "$log_line" != "$last_line" ]]; then
                echo -e -n "\b\b\b\b\b"
                case $animation_state in
                    1)
                        echo -n "["
                        echo -n -e "\033[1m"
                        echo -n "\\o\\"
                        echo -n -e "\033[0m"
                        echo -n "]"
                        animation_state=2
                        ;;
                    2)
                        echo -n "["
                        echo -n -e "\033[1m|o|\033[0m"
                        echo -n "]"
                        animation_state=3
                        ;;
                    3)
                        echo -n "["
                        echo -n -e "\033[1m/o/\033[0m"
                        echo -n "]"
                        animation_state=4
                        ;;
                    4)
                        echo -n "["
                        echo -n -e "\033[1m|o|\033[0m"
                        echo -n "]"
                        animation_state=5
                        ;;
                    5)
                        echo -n "["
                        echo -n -e "\033[1m"
                        echo -n "\\o/"
                        echo -n -e "\033[0m"
                        echo -n "]"
                        animation_state=6
                        ;;
                    6)
                        echo -n "["
                        echo -n -e "\033[1m|o|\033[0m"
                        echo -n "]"
                        animation_state=1
                        ;;

                esac
        log_line=$last_line
    fi
    sleep 1
done
del_word "$name[|-|]"
}

write_name(){
    echo_padd 28 . -n "$@"
}

### END HELPER FUNCTIONS }}}

list(){ #{{{
debug 1 "$FUNCNAME() $*"
[[ ${conf[action]} == list ]] || return
section " list of available enlightenment packages "
echo
echo "not implemented"
echo

exit 0
} #}}}

plugin(){ #{{{
debug 1 "$FUNCNAME() $*"
#
# plugins will be called form here
#
get_git_e17_path_from_global_cfg
read_local_cfg
read_global_cfg
assign_defaults
interpret_action
check_cfg
distcheck
set_env

section " installing plugins "

# TODO add to config

pushd ${PWD} &>/dev/null
cd "${conf[src_path]}" || ferr "unable to cahnge to build path"
g_current_commit="$(git log -1 --format="%H")" || ferr "unable to get current commit"
popd &>/dev/null

local plugin_path="${BASH_SOURCE%/*}"

echo "slected plugins: $packages"
if [[ $packages == "all"* ]]; then
    cd "$plugin_path" || ferr "unable to cd into plugin path"
    local plugins=""
    for plugin in git_e17-plugin-*; do
        plugins+=${plugin#git_e17-plugin-}
    done
else
    local plugins="$packages"
fi

#backup configuration
local -A conf_backup
for key in ${!conf[@]}; do
    conf_backup[$key]=${conf[$key]}
done

for plugin in $plugins; do
    section " installing $plugin plugin "

    #source plugin
    local plugin_path="$plugin_path/git_e17-plugin-$plugin"
    echo "sourcing: $plugin_path"
    . "$plugin_path" || ferr "failed to source: $plugin_path"

    #run plugin
    plugin_build $plugin

    #restore configuration
    for key in ${!conf_backup[@]}; do
        conf[$key]=${conf_backup[$key]}
    done
done

## exit when finished
## do not delete the exit
exit
} #}}}



default_global_cfg(){ #{{{
#
# default content of ~/.git_e17
#
printf '%b\n' \
   "# git_e17 - global configuration file" \
   "#" \
   "# INFO:" \
   "# - local configurations and command line options override this file" \
   "# - lines starting with # are ignored" \
   "# - make sure your user has write access to all chosen paths" \
   "# - you are able to set the path outside the git e17 path" \
   "" \
   "# item=val - NO SPACES - NO QUOTES" \
   "" \
   "# default git_e17_path" \
   "# THIS IS THE PATH TO THE SCRIPTS WORKING DIR - PLEASE SET IT" \
   "git_e17_path=${conf[git_e17_path]%/}" \
   "# default config file" \
   "config_file=default.conf" \
   "" \
   "# enable developer mode" \
   "#dev=true" \
   "# disable noob info" \
   "#noob_info=false"
} #}}}

default_local_cfg(){ #{{{
printf '%b\n' \
   "# git_e17 - local configuration file" \
   "#" \
   "# INFO:" \
   "# - lines starting with # are ignored" \
   "# - make sure your user has write access to all chosen paths" \
   "# - you are able to set the paths outside the git e17 directory" \
   "" \
   "# item=val - NO SPACES - NO QUOTES" \
   "" \
   "# this is the target dir of your installation" \
   "install_path=/opt/e17" \
   "# git repoistory directory" \
   "src_path=${conf[git_e17_path]}/e.org.git" \
   "# dir in which the" \
   "build_path=${conf[git_e17_path]}/build" \
   "log_path=${conf[git_e17_path]}/log" \
   "" \
   "# enable developer mode" \
   "#dev=true" \
   "# disable noob info" \
   "#noob_info=false" \
   "# additional CFLAGS" \
   "cflags=-g" \
   "# additional LDFLAGS" \
   "#ldflags=" \
   "# clean " \
   "#clean=false " \
   "# packagelist to use basic, full" \
   "packagelist=full" \
   "# branch to checkout" \
   "#branch=master" \
   "# remote to pull from" \
   "#remote=origin" \
   "" \
   "# on on compile error action: ask,exit,continue" \
   "on_error=ask" \
   "# number ot threads" \
   "threads=8" \
   "#niceness level" \
   "nice=19" \
   "#create documentation" \
   "doc=false" \
   "" \
   "## extra configure flags" \
   "evas_conf_args=--enable-gl-x11"
} #}}}

noob_info(){ #{{{
${conf[noob_info]} || return
#test if e was installed
install_e=false
for pkg in $packages; do
    if [[ $pkg == e ]]; then
        install_e=true
    fi
done

$install_e || { echo; exit 0; }
section " info "

for pkg in $packages_failed; do
    if [[ $pkg == e ]]; then
        echo "Enlightenment failed to install or update :("
        echo "Please check your logs at: ${conf[log_path]}"
        exit 1
    fi
done

printf '%b\n' \
   "Your installation of Enlightenment is successful! " \
   "" \
   "You can rebuild failed packages with ${bold}./git_e17 build only names of failed. ${norm}" \
   "To update you installation use ${bold}./git_e17 update${norm}" \
   "You ${red}need${norm} to add the following lines to your ${green}~/.profile${norm} or shells profile file:" \
   "$green" \
   "    # Make e17 bins like enlightenment_start available from any location" \
   "    export PATH=\"${conf[install_path]}/bin:\$PATH\"" \
   "" \
   "    # Add new python2 libs to PYTHONPATH" \
   "    export PYTHONPATH=\"${conf[install_path]}/$($pythoncmd -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(prefix='$install_path')" 2>/dev/null):\$PYTHONPATH\"" \
   "" \
   "    # Add e17 libs to your users linker search path " \
   "    # Doing it like this is considered harmful by some people and most programs" \
   "    # are compiled with RPATH anyway - Read on LD_LIBRARY_PATH!!!" \
   "    #export LD_LIBRARY_PATH=\"${conf[install_path]}/bin:\$LD_LIBRARY_PATH\"" \
   "$norm" \
   "In order to use enlightenment with ${bold}gdm, kdm, xdm ... ${norm} as root you ${red}need${norm} to:" \
   "${green}ln -s ${conf[install_path]}/share/xessions/enlightenment.desktop /usr/share/xsessions/$norm" \
   "If it does not work ask for an ${red}equivalent${norm} of ${red}/usr/share/xessions${norm}" \
   "at ${green}your distribution's$norm website or (freenode) IRC channel."
} #}}}

### program starts here
# make sure main program ist not run as root as it cause serious damage
# i like to be sloppy - if you override this don't blame me:P
if (( $(id -u) == 0 )); then
    ferr "${red}You should not run scripts as root!${norm}"
fi
main "$@"