#! /bin/sh
#
#   Copyright (c) 2015-2017 Nat! - Mulle kybernetiK
#   All rights reserved.
#
#   Redistribution and use in source and binary forms, with or without
#   modification, are permitted provided that the following conditions are met:
#
#   Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
#   Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
#   Neither the name of Mulle kybernetiK nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
#   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#   POSSIBILITY OF SUCH DAMAGE.
MULLE_EXECUTABLE_VERSION_MAJOR=3
MULLE_EXECUTABLE_VERSION_MINOR=0
MULLE_EXECUTABLE_VERSION_PATCH=0

MULLE_EXECUTABLE_VERSION="${MULLE_EXECUTABLE_VERSION_MAJOR}.${MULLE_EXECUTABLE_VERSION_MINOR}.${MULLE_EXECUTABLE_VERSION_PATCH}"

#
# This is the main user interface to mulle-bootstrap
# sorta like git
#

trap_fail()
{
   exit 1
}



mulle_bootstrap_usage()
{
   cat <<EOF
usage: mulle-bootstrap [flags] [command] [options]
 Flags:
   -a -y     : default answer to questions (scripts wont be checked)
               -a (clone preferred) -y (local copy/symlink preferred)
   -f        : force operation
   -n        : do nothing creative or destructive
   --no-defer: do not defer to master, if project is a minion
   -v        : -v to be more verbose (-vv or -vvv for more verbosity)
   -V        : verbose build
   -DKEY=VAL : define local expansion key/value C style
EOF

   bootstrap_technical_option_usage

   cat <<EOF

 Commands:
   bootstrap     : does install and build recursively [default]
   build         : builds fetched repositories
   clean         : cleans mulle-bootstrap produced files.
   defer         : join a master mulle-bootstrap project
   emancipate    : emancipate from a master mulle-bootstrap project
   flags         : produces CFLAGS for cc invocation and more
   init          : initializes a bootstrap project
   update        : updates repositories (fetch).
   upgrade       : updates and checks out repositories (pull).
   show          : show repositories and brews

 Options are command specific. Use mulle-bootstrap <command> -h for help.
EOF

   if [ "${UNAME}" = 'darwin' ]
   then
      cat <<EOF
   xcode         : sets up xcodeproj settings
EOF
   fi

   if [ !  -z "${MULLE_TRACE}" ]
   then
      cat <<EOF

   systeminstall : install dependencies as system headers
   git           : run git commands on fetched repositories
   status        : show status of repositories and brews
   tag           : tag fetched repositories
EOF
   fi

   exit 1
}


_bootstrap_main()
{
   local command

   log_debug "::: bootstrap begin :::"

   . mulle-bootstrap-fetch.sh
   . mulle-bootstrap-build.sh
   . mulle-bootstrap-warn-scripts.sh

   # used for option handling only
   MULLE_BOOTSTRAP_WILL_BUILD="YES"

   warn_scripts_main "${BOOTSTRAP_DIR}" || exit 1

   if fetch_needed
   then
      MULLE_EXECUTABLE_FAIL_PREFIX="mulle-bootstrap fetch"
      fetch_main "$@" || exit 1
      # used for option handling only
      MULLE_BOOTSTRAP_DID_FETCH="YES"
   else
      log_verbose "No fetch needed"
   fi

   if build_needed
   then
      MULLE_EXECUTABLE_FAIL_PREFIX="mulle-bootstrap build"
      build_main "$@" || exit 1
   else
      log_verbose "No build needed"
   fi

   log_debug "::: bootstrap end :::"
}


bootstrap_libexec_path()
{
   local exedir
   local exedirpath
   local prefix
   local libexecpath

   exedir="`dirname "$1"`"
   exedirpath="`( cd "${exedir}" ; pwd -P )`" || fail "failed to get pwd"
   prefix="`dirname "${exedirpath}"`"
   libexecpath="${prefix}/libexec/mulle-bootstrap"

   if [ ! -x "${libexecpath}/mulle-bootstrap-functions.sh" ]
   then
      libexecpath="${exedirpath}/src"
   fi

   if [ -x "${libexecpath}/mulle-bootstrap-functions.sh" ]
   then
      echo "${libexecpath}"
   fi
}


bootstrap_init()
{
   local libexecpath

   libexecpath="`bootstrap_libexec_path "$0"`"
   if [ -z "${libexecpath}" ]
   then
      echo "could not find libexec for mulle-bootstrap" >&2
      exit 1
   fi

   PATH="${libexecpath}:$PATH"
   export PATH

   if [ ! -z "${MULLE_BOOTSTRAP_LIBEXEC_TRACE}" ]
   then
      echo "PATH=$PATH" >&2
   fi

   #
   # shell export commands with minimal
   # trap setup
   #
   case "${1}" in
      library-path)
         echo "${libexecpath}"
         exit 0
      ;;

      version)
         echo "${MULLE_EXECUTABLE_VERSION}"
         exit 0
      ;;
   esac

   #  set -e # more pain then gain in the end
   #  set -u # doesn't work with my style

   # now include this first to get
   # logging and UNAME

   . mulle-bootstrap-logging.sh
   . mulle-bootstrap-local-environment.sh || fail "not loaded"

   trap trap_fail INT

   # source_environment
}


bootstrap_main()
{
   local command
   local ps4string

   local MULLE_FLAG_ANSWER="ASK"
   local MULLE_FLAG_MAGNUM_FORCE="NO"
   local MULLE_FLAG_IGNORE_GRAVEYARD="NO"
   local MULLE_FLAG_VERBOSE_BUILD="NO"

   # technical flags
   local MULLE_FLAG_EXECUTOR_DRY_RUN="NO"
   local MULLE_FLAG_FOLLOW_SYMLINKS="YES"
   local MULLE_FLAG_LOG_CACHE="NO"
   local MULLE_FLAG_LOG_DEBUG="NO"
   local MULLE_FLAG_LOG_EXECUTOR="NO"
   local MULLE_FLAG_LOG_EXECUTOR="NO"
   local MULLE_FLAG_LOG_FLUFF="NO"
   local MULLE_FLAG_DONT_DEFER="NO"
   local MULLE_FLAG_LOG_SCRIPTS="NO"
   local MULLE_FLAG_LOG_SETTINGS="NO"
   local MULLE_FLAG_LOG_VERBOSE="NO"
   local MULLE_FLAG_MERGE_LOG="NO"
   local MULLE_TRACE_PATHS_FLIP_X="NO"
   local MULLE_TRACE_POSTPONE="NO"
   local MULLE_TRACE_RESOLVER_FLIP_X="NO"
   local MULLE_TRACE_SETTINGS_FLIP_X="NO"

   #
   # simple option handling
   #
   while [ $# -ne 0 ]
   do
      if bootstrap_technical_flags "$1"
      then
         shift
         continue
      fi

      case "$1" in
         -a|--prefer-origin)
            MULLE_FLAG_ANSWER="NO"
          ;;

         -y|--prefer-local)
            MULLE_FLAG_ANSWER="YES"
         ;;

         --no-defer)
            MULLE_FLAG_DONT_DEFER="YES"
         ;;

         # used persistently throughout so it's global
         --follow-symlinks)
            MULLE_FLAG_FOLLOW_SYMLINKS="YES"
         ;;

         -f|--force-fetch)
            MULLE_FLAG_MAGNUM_FORCE="YES"
         ;;

         --ignore-graveyard)
            MULLE_FLAG_IGNORE_GRAVEYARD="YES"
         ;;

         -h|--help)
            mulle_bootstrap_usage
         ;;

         -D*)  # just like C
            # define key values (by putting them into .bootstrap.local)
            bootstrap_define_expansion "`echo "$1" | sed s'/^-D[ ]*//'`"
         ;;

         -V|--verbose-build)
            MULLE_FLAG_VERBOSE_BUILD="YES"
         ;;

         -*)
            log_error "${MULLE_EXECUTABLE_FAIL_PREFIX}: Unknown option \"$1\""
            mulle_bootstrap_usage
         ;;

         *)
            break
         ;;
      esac

      shift
   done

   bootstrap_setup_trace "${MULLE_TRACE}"

   # source in environment now

   local_environment_main

   #
   # some commands only run when we have a .bootstrap folder
   #
   command="${1:-bootstrap}"

   if [ "${MULLE_FLAG_MAGNUM_FORCE}" = "YES" ]
   then
      set_fetch_needed
      set_build_needed
   fi

   if ! is_bootstrap_project
   then
      case "$1" in
         -h|--help)
         ;;

         *)
            case "${command}" in
               bootstrap|build|clean|defer|dist|dist-clean|emancipate|fetch|install|syteminstall|upgrade|update|setup-xcode|show|status|tag|xcode)
                  fail "There is no ${BOOTSTRAP_DIR} or ${BOOTSTRAP_DIR}.local folder here (${C_RESET_BOLD}$PWD${C_ERROR})"
               ;;
            esac
         ;;
      esac
   else
      if bootstrap_should_defer_to_master "$@"
      then
         return 1
      fi

      if [ "${MULLE_FLAG_MAGNUM_FORCE}" = "NO" ]
      then
         case "${command}" in
            clean|dist-clean|init|status|show|config|setting)
            ;;

            *)
               bootstrap_ensure_consistency
            ;;
         esac
      fi
   fi

   MULLE_EXECUTABLE_FAIL_PREFIX="${MULLE_EXECUTABLE} ${command}"
   [ $# -eq 0 ] || shift

   MULLE_EXECUTABLE_OPTIONS="$@"

   case "${command}" in
      bootstrap)
         _bootstrap_main "$@"
      ;;

      build)
         . mulle-bootstrap-build.sh

         build_main "$@" || exit 1
      ;;

      clean)
         . mulle-bootstrap-clean.sh

         clean_main "$@" || exit 1
      ;;

      config)
         . mulle-bootstrap-settings.sh

         config_main "$@" || exit 1
      ;;

      defer)
         . mulle-bootstrap-defer.sh

         defer_main "$@" || exit 1
      ;;

      dist)
         . mulle-bootstrap-clean.sh

         clean_main "dist" || exit 1
      ;;

      emancipate)
         . mulle-bootstrap-defer.sh

         emancipate_main "$@" || exit 1
      ;;

      expansion)
         . mulle-bootstrap-settings.sh

         expansion_main "$@" || exit 1
      ;;

      flags)
         . mulle-bootstrap-flags.sh

         flags_main "$@" || exit 1
      ;;

      git)
         . mulle-bootstrap-scm.sh

         git_main "$@" || exit 1
      ;;

      help)
         mulle_bootstrap_usage "$@" || exit 1
      ;;

      init)
         . mulle-bootstrap-init.sh

         init_main "$@" || exit 1
      ;;

      install|fetch)
         . mulle-bootstrap-fetch.sh

         DONT_ASK_AFTER_WARNING=YES fetch_main "$@" || exit 1
      ;;

      library-path)
         echo "$PATH" | tr ':' '\012' | head -1
         return 0
      ;;

      setting)
         . mulle-bootstrap-settings.sh

         setting_main "$@" || exit 1
      ;;

      systeminstall)
         . mulle-bootstrap-install.sh

         install_main "$@" || exit 1
      ;;

      show)
         [ -z "${MULLE_BOOTSTRAP_SHOW_SH}" ] && . mulle-bootstrap-show.sh

         show_main "$@" || exit 1
      ;;

      status)
         . mulle-bootstrap-status.sh

         local rval

         status_main "$@"
         rval="$?"

         [ "$rval" -eq 0 ] || exit $rval
      ;;

      tag)
         . mulle-bootstrap-tag.sh

         tag_main "$@" || exit 1
      ;;

      uname)
         echo "${UNAME}"
         exit 0
      ;;

      update)
         . mulle-bootstrap-fetch.sh

         update_main "$@" || exit 1
      ;;

      upgrade)
         . mulle-bootstrap-fetch.sh

         upgrade_main "$@" || exit 1
      ;;

      version)
         echo "${MULLE_EXECUTABLE_VERSION}"
         return 0
      ;;

      xcode|setup-xcode)
         . mulle-bootstrap-xcode.sh

         MULLE_EXECUTABLE_FAIL_PREFIX="mulle-bootstrap xcode"
         xcode_main "$@" || exit 1
      ;;

      *)
         log_error "${MULLE_EXECUTABLE_FAIL_PREFIX}: Unknown command \"${command}\""
         mulle_bootstrap_usage
      ;;
   esac
}



#
# service both names
#
MULLE_EXECUTABLE="`basename -- "$0"`"
MULLE_ARGUMENTS="$@"
MULLE_EXECUTABLE_FAIL_PREFIX="${MULLE_EXECUTABLE}"
MULLE_EXECUTABLE_PID="$$"
export MULLE_EXECUTABLE_PID


bootstrap_init "$@" # needs params


main()
{
   if ! bootstrap_main "$@"
   then
      # just do it again, but cd has been set differently
      main "$@"
      exit $?
   fi
}

main "$@"

trap - TERM EXIT