# official repository / репозиторий официальной Psi
GIT_REPO_PSI=git://git.psi-im.org/psi.git

# do not update anything from repositories until required
WORK_OFFLINE=${WORK_OFFLINE:-0}

# skip patches which applies with errors / пропускать глючные патчи
SKIP_INVALID_PATCH=${SKIP_INVALID_PATCH:-0}

# log of applying patches / лог применения патчей
PATCH_LOG=${PATCH_LOG:-/dev/null}

# configure options / опции скрипта configure
DEFAULT_CONF_OPTS="${DEFAULT_CONF_OPTS:---enable-plugins --enable-whiteboarding}"
CONF_OPTS="${DEFAULT_CONF_OPTS} ${CONF_OPTS}"

# install root / каталог куда устанавливать (полезно для пакаджеров)
INSTALL_ROOT="${INSTALL_ROOT:-/}"

# icons for downloads / иконки для скачивания
ICONSETS="${ICONSETS:-system clients activities moods affiliations roster}"

# system libraries directory
[ "`uname -m`" = "x86_64" ] && [ -d /usr/lib64 ] && LIBDIRNAME=${LIBDIRNAME:-lib64} || LIBDIRNAME=${LIBDIRNAME:-lib}

# bin directory of compiler cache util
CCACHE_BIN_DIR="${CCACHE_BIN_DIR:-/usr/${LIBDIRNAME}/ccache}"

SVN_BASE_REPO="http://psi-dev.googlecode.com/svn/trunk"
SVN_FETCH="${SVN_FETCH:-svn co --trust-server-cert --non-interactive}"
SVN_UP="${SVN_UP:-svn up --trust-server-cert --non-interactive}"

#######################
# FUNCTIONS / ФУНКЦИИ #
#######################

helper() {
  case "${LANG}" in
    "ru_"*) cat <<END
Скрипт для сборки Psi+

-h,--help	Помощь
--enable-webkit Собрать с поддержкой технологий webkit
--prefix=pass	Задать установочный каталог (автоопределение по умолчанию)

	Описание переменных окружения:
PLUGINS="*"		Собрать все плагины
PLUGINS="hello world"	Собрать плагины "hello" и "world"
WORK_OFFLINE=[1,0]	Не обновлять из репозитория
SKIP_INVALID_PATCH=[1,0] Пропускать глючные патчи
PATCH_LOG		Лог применения патчей
INSTALL_ROOT		Каталог куда устанавливать (полезно для пакаджеров)
ICONSETS		Иконки для скачивания
CCACHE_BIN_DIR		Каталог кеша компилятора
LIBDIRNAME		Имя системного каталога с библиотеками (lib64/lib32/lib)
			Автодетектится если не указана
END
    ;;
    *) cat <<END
Script to build the Psi+

-h,--help	This help
--enable-webkit Build with themed chats and enabled smileys animation
--prefix=pass	Set the installation directory

	Description of environment variables:
PLUGINS="*"		Build all plugins
PLUGINS="hello world"	Build plugins "hello" and "world"
WORK_OFFLINE=[1,0]	Do not update anything from repositories until required
SKIP_INVALID_PATCH=[1,0] Skip patches which apply with errors
PATCH_LOG		Log of patching process
INSTALL_ROOT		Install root (usefull for package maintainers)
ICONSETS		Icons to download
CCACHE_BIN_DIR		Bin directory of compiler cache util
LIBDIRNAME		System libraries directory name (lib64/lib32/lib)
			Autodetected when not given
END
  esac
  exit 0
}

# Exit with error message
die() {
  echo; echo " !!!ERROR: $@";
  exit 1;
}

check_env() {
  # Setting some internal variables
  local have_prefix=0
  [ -n "${COMPILE_PREFIX}" ] && have_prefix=1

  case "`uname`" in
  FreeBSD)
    MAKEOPT=${MAKEOPT:--j$((`sysctl -n hw.ncpu`+1))}
    STAT_USER_ID='stat -f %u'
    STAT_USER_NAME='stat -f %Su'
    SED_INPLACE_ARG=".bak"
    COMPILE_PREFIX="${COMPILE_PREFIX-/usr/local}"
    ;;
  SunOS)
    CPUS=`/usr/sbin/psrinfo | grep on-line | wc -l | tr -d ' '`
    if test "x$CPUS" = "x" -o $CPUS = 0; then
      CPUS=1
    fi
    MAKEOPT=${MAKEOPT:--j$CPUS}
    STAT_USER_ID='stat -c %u'
    STAT_USER_NAME='stat -c %U'
    SED_INPLACE_ARG=".bak"
    COMPILE_PREFIX="${COMPILE_PREFIX-/usr/local}"
    ;;
  *)
    MAKEOPT=${MAKEOPT:--j$((`cat /proc/cpuinfo | grep processor | wc -l`+1))}
    STAT_USER_ID='stat -c %u'
    STAT_USER_NAME='stat -c %U'
    SED_INPLACE_ARG=""
    COMPILE_PREFIX="${COMPILE_PREFIX-/usr}"
    ;;
  esac

  echo -n "Testing environment.. "
  v=`git --version 2>/dev/null` || \
    die "You should install Git first. / Сначала установите Git"
  v=`svn --version 2>/dev/null` || \
    die "You should install subversion first. / Сначала установите subversion"

  # Make
  echo -n "Checking for gmake.. "
  v=`gmake --version 2>/dev/null`
  MAKE="gmake"
  if [ -z "$v" ]; then
    echo "not found / не найден"
    echo -n "trying just make / пробуем просто make.."
    v=`make --version 2>/dev/null`
    MAKE="make"
  fi
  [ -n "$v" ] && echo "OK" || die "You should install GNU Make first / "\
    "Сначала установите GNU Make"

  # patch
  [ -z "`which patch`" ] &&
    die "patch tool not found / утилита для наложения патчей не найдена"
  # autodetect --dry-run or -C
  [ -n "`patch --help 2>/dev/null | grep dry-run`" ] && PATCH_DRYRUN_ARG="--dry-run" \
    || PATCH_DRYRUN_ARG="-C"
  
  # qmake
  for qm in qmake-qt4 qmake4 qmake; do
    [ -n "`$qm --version 2>/dev/null |grep Qt`" ] && QMAKE=$qm
  done
  [ -z "${QMAKE}" ] && die "You should install "\
    "Qt framework / Сначала установите Qt framework"
  
  # QConf
  for qc in qt-qconf qconf qconf-qt4; do
    v=`$qc --version 2>/dev/null |grep affinix` && QCONF=$qc
  done
  [ -z "${QCONF}" ] && die "You should install "\
    "qconf(http://delta.affinix.com/qconf/) / Сначала установите qconf"
  
  # CCache
  [ -n "`which ccache`" ] && [ -e "${CCACHE_BIN_DIR}/gcc" ] && {
    echo "Found ccache. going to use it."
    export PATH="${CCACHE_BIN_DIR}:${PATH}"
  }

  # Plugins
  plugins_enabled=0
  case "${CONF_OPTS}" in *--enable-plugins*) plugins_enabled=1; ;; *) ;; esac

  [ -n "${PLUGINS}" ] && [ "${plugins_enabled}" = 0 ] && {
    echo "WARNING: there are selected plugins but plugins are disabled in"
    echo "configuration options. no one will be built"
    PLUGINS=""
  } || {
    local plugins_tmp=""
    [ "$PLUGINS" = "*" ] && PLUGINS=`echo $(svn ls ${SVN_BASE_REPO}/plugins/generic | grep 'plugin/' | sed 's:plugin/::')`
    for name in ${PLUGINS}; do
      plugins_tmp="${plugins_tmp} ${name}plugin"
    done
    PLUGINS="${plugins_tmp}"
  }
 
  [ $have_prefix = 0 ] && CONF_OPTS="${CONF_OPTS} --prefix=$COMPILE_PREFIX"
  echo "Compile prefix=${COMPILE_PREFIX}"

  echo "Environment is OK"
}

prepare_workspace() {
  echo -n "Init directories.. "
  if [ ! -d "${PSI_DIR}" ]
  then
    mkdir "${PSI_DIR}" || die "can't create work directory ${PSI_DIR}"
  fi
  rm -rf "${PSI_DIR}"/build
  [ -d "${PSI_DIR}"/build ] && \
    die "can't delete old build directory ${PSI_DIR}/build"
  mkdir "${PSI_DIR}"/build || \
    die "can't create build directory ${PSI_DIR}/build"
  echo "OK"
}

# fetches defined set of something from psi-dev svn. ex: plugins or iconsets
#
# _fetch_set(name, remote_path, items, [sub_item_path])
# name - a name of what you ar fetching. for example "plugin"
# remote - a path relative to SVN_BATH_REPO
# items - space separated items string
# sub_item_path - checkout subdirectory of item with this relative path
#
# Example: _fetch_set("iconset", "iconsets", "system, mood", "default")
_fetch_set() {
  local name="$1"
  local remote="$2"
  local items="$3"
  local subdir="$4"
  cd "${PSI_DIR}"
  [ -n "${remote}" ] || die "invalid remote path in set fetching"
  if [ ! -d "${remote}" ]; then
    mkdir -p "${remote}"
  fi
  cd "${remote}"

  for item in ${items}; do
    if [ -d "$item" ]; then
      [ $WORK_OFFLINE = 0 ] && {
        echo -n "Update ${item} ${name} ... "
        $SVN_UP "${item}" || die "${item} ${name} update failed"
      }
    else
      echo "Downloading ${item} ${name} .."
      $SVN_FETCH "${SVN_BASE_REPO}/${remote}/${item}/${subdir}" "$item" \
      || die "${item} ${name} checkout failed"
    fi
  done
}

fetch_sources() {
  cd "${PSI_DIR}"
  if [ -d "git/.git" ]
  then
    echo "Starting updating.."
    cd git
    if [ $WORK_OFFLINE = 0 ]; then
      git pull || die "git update failed"
    else
      echo "Update disabled in options"
    fi
  else
    echo "New fresh repo.."
    git clone "${GIT_REPO_PSI}" git || die "git clone failed"
    cd git
  fi
  git submodule update --init || die "git submodule update failed"

  echo "downloading patches.."
  cd "${PSI_DIR}"
  if [ -d patches ]
  then
    [ $WORK_OFFLINE = 0 ] && { $SVN_UP patches || die "patches update failed"; }
  else
    $SVN_FETCH "${SVN_BASE_REPO}/patches" patches \
  	|| die "patches checkout failed"
  fi

  _fetch_set "iconset" "iconsets" "${ICONSETS}" "default"

  cd "${PSI_DIR}"
}

fetch_plugins_sources() {
  _fetch_set "plugin" "plugins/generic" "${PLUGINS}"
}

fetch_all() {
  fetch_sources
  fetch_plugins_sources
}

#smart patcher
spatch() {
  popts=""
  PATCH_TARGET="${1}"

  echo -n " * applying ${PATCH_TARGET} ..." | tee -a $PATCH_LOG

  if (patch -p1 ${popts} ${PATCH_DRYRUN_ARG} -i ${PATCH_TARGET}) >> $PATCH_LOG 2>&1
  then
    if (patch -p1 ${popts} -i ${PATCH_TARGET} >> $PATCH_LOG 2>&1)
    then
    	echo " done" | tee -a $PATCH_LOG
	return 0
    else
    	echo "dry-run ok, but actual failed" | tee -a $PATCH_LOG
    fi
  else
    echo "failed" | tee -a $PATCH_LOG
  fi
  return 1
}

prepare_sources() {
  echo "Exporting sources"
  cd "${PSI_DIR}"/git
  git archive --format=tar HEAD | ( cd "${PSI_DIR}/build" ; tar xf - )
  (
    export ddir="${PSI_DIR}/build"
    git submodule foreach '( git archive --format=tar HEAD ) \
    	| ( cd "${ddir}/${path}" ; tar xf - )'
  )

  cd "${PSI_DIR}"
  rev=`svnversion patches`
  PATCHES=`ls -1 patches/*diff 2>/dev/null`
  cd "${PSI_DIR}/build"
  [ -e "$PATCH_LOG" ] && rm "$PATCH_LOG"
  for p in $PATCHES; do
     spatch "${PSI_DIR}/${p}"
     if [ "$?" != 0 ]
     then
       [ $SKIP_INVALID_PATCH = "0" ] \
         && die "can't continue due to patch failed" \
         || echo "skip invalid patch"
     fi
  done
  sed -i${SED_INPLACE_ARG} "s/.xxx/.${rev}/"  src/applicationinfo.cpp
  sed -i${SED_INPLACE_ARG} \
    "s:target.path.*:target.path = ${COMPILE_PREFIX}/${LIBDIRNAME}/psi-plus/plugins:" \
    src/plugins/psiplugin.pri

  # prepare icons
  for name in ${ICONSETS}; do
    if [ ! -d ${PSI_DIR}/build/iconsets/$name ]; then
      mkdir "${PSI_DIR}/build/iconsets/$name"
    fi
    svn export "${PSI_DIR}/iconsets/$name" \
        "${PSI_DIR}/build/iconsets/$name/default" --force
  done

}

prepare_plugins_sources() {
  [ -d "${PSI_DIR}/build/src/plugins/generic" ] || \
    die "preparing plugins requires prepared psi+ sources"
  for name in ${PLUGINS}; do
    svn export "${PSI_DIR}/plugins/generic/$name" \
      "${PSI_DIR}/build/src/plugins/generic/$name" --force
  done
}

prepare_all() {
  prepare_sources
  prepare_plugins_sources
}

compile_psi() {
  cd "${PSI_DIR}/build"
  $QCONF
  echo "./configure ${CONF_OPTS}"
  ./configure ${CONF_OPTS} || die "configure failed"
  $MAKE $MAKEOPT || die "make failed"
}

compile_plugins() {
  failed_plugins="" # global var

  for name in ${PLUGINS}; do
    echo "Compiling ${name} plugin.."
    cd  "${PSI_DIR}/build/src/plugins/generic/$name"
    $QMAKE "PREFIX=${COMPILE_PREFIX}" && $MAKE $MAKEOPT || {
      echo
      echo "Failed to make plugin ${name}! Skipping.."
      echo
      failed_plugins="${failed_plugins} ${name}"
    }
  done
}

compile_all() {
  compile_psi
  compile_plugins
}

install_any() {
  [ -d "${1}" ] && cd "${1}"
  [ ! -d "${INSTALL_ROOT}" ] && { mkdir -p "${INSTALL_ROOT}" \
    || die "can't create install root directory ${INSTALL_ROOT}."; }
  local ir_user_id=`$STAT_USER_ID "${INSTALL_ROOT}"`
  [ -z "${ir_user_id}" ] && die "Failed to detect destination directory's user id"
  if [ "${ir_user_id}" = "`id -u`" ]
  then
    $MAKE INSTALL_ROOT="${INSTALL_ROOT}" install || die "make install failed"
  else
    ir_user=`$STAT_USER_NAME "${INSTALL_ROOT}"`
    [ -z "${ir_user}" ] && die "Failed to detect destination directory's owner"
    echo "owner of ${INSTALL_ROOT} is ${ir_user} and this is not you."
    echo "please enter ${ir_user}'s password.."
    echo "Executing: su -m \"${ir_user}\" -c \"$MAKE INSTALL_ROOT=\\\"${INSTALL_ROOT}\\\" install\""
    su -m "${ir_user}" -c "$MAKE INSTALL_ROOT=\"${INSTALL_ROOT}\" install" \
      || die "make install failed"
  fi
}

install_psi() {
  echo "Installing psi.."
  install_any "${PSI_DIR}/build"
}

install_plugins() {
  for name in ${PLUGINS}; do
    case "$failed_plugins" in
      *"$name"*)
        echo "Skipping installation of failed plugin ${name}"
	;;
      *)
        echo "Installing ${name} plugin.."
        install_any "${PSI_DIR}/build/src/plugins/generic/$name"
        ;;
    esac
  done
}

install_all() {
  install_psi
  install_plugins
}

unset COMPILE_PREFIX
until [ -z "$1" ]; do
  case "$1" in
    "-h" | "--help")
      helper
      ;;
    "--prefix="*)
      COMPILE_PREFIX=${1#--prefix=}
      ;;
  esac
  shift
done
