#!/bin/sh

fail() {
  echo "${@}" >&2
  exit 1
}

on_tag() {
  if [ -n "$CURRENT_TAG" ]; then
    echo "${@}"
    eval "${@}"
    return $?
  else
    return 0
  fi
}

fail_on_error() {
  "${@}"

  exit=$?
  if [ "$exit" -ne "0" ]; then
    fail "${@} exited with $exit"
  fi

  return 0
}

verify_environment() {
  if [ -z "$TRAVIS_OS_NAME" ]; then
    fail "\$TRAVIS_OS_NAME is not set or empty."
  fi
}

verify_linux_environment() {
  if [ -z "$ARCH" ]; then
    fail "\$ARCH is not set or empty."
  fi

  if [ -z "$ARCH_CMD" ]; then
    fail "\$ARCH_CMD is not set or empty."
  fi
}

on_os() {
  os="$1"
  shift

  verify_environment

  if [ "$TRAVIS_OS_NAME" = "$os" ]; then
    echo "${@}"
    eval "${@}"
    return $?
  else
    return 0
  fi
}

on_linux() {
  fail_on_error on_os "linux" "${@}"
}

on_osx() {
  fail_on_error on_os "osx" "${@}"
}

prepare_system() {
  on_linux 'echo '"'"'{"ipv6":true, "fixed-cidr-v6":"2001:db8:1::/64"}'"'"' | sudo tee /etc/docker/daemon.json'
  on_linux sudo service docker restart

  on_osx brew update
}

build() {
  with_build_env 'make std_spec clean threads=1 junit_output=.junit/std_spec.xml'

  case $ARCH in
    i386)
      with_build_env 'make crystal threads=1'
      with_build_env 'SPEC_SPLIT="0%4" make std_spec threads=1 junit_output=.junit/std_spec.0.xml'
      with_build_env 'SPEC_SPLIT="1%4" make std_spec threads=1 junit_output=.junit/std_spec.1.xml'
      with_build_env 'SPEC_SPLIT="2%4" make std_spec threads=1 junit_output=.junit/std_spec.2.xml'
      with_build_env 'SPEC_SPLIT="3%4" make std_spec threads=1 junit_output=.junit/std_spec.3.xml'

      parts=16
      i=0
      while [ $i -lt $parts ]; do
        with_build_env "CRYSTAL_SPEC_COMPILER_THREADS=1 SPEC_SPLIT=\"$i%$parts\" make compiler_spec threads=1 junit_output=.junit/compiler_spec.$i.xml"
        i=$((i + 1))
      done

      with_build_env 'make docs threads=1'
      ;;
    *)
      with_build_env 'make crystal std_spec compiler_spec docs threads=1 junit_output=.junit/spec.xml'
      ;;
  esac

  with_build_env 'find samples -name "*.cr" | xargs -tn 1 ./bin/crystal build --no-codegen'
}

format() {
  with_build_env 'make clean crystal threads=1'
  with_build_env './bin/crystal tool format --check samples spec src'
}

prepare_build() {
  on_linux verify_linux_environment

  on_osx curl -L https://github.com/crystal-lang/crystal/releases/download/0.33.0/crystal-0.33.0-1-darwin-x86_64.tar.gz -o ~/crystal.tar.gz
  on_osx 'pushd ~;gunzip -c ~/crystal.tar.gz | tar xopf -;mv crystal-0.33.0-1 crystal;popd'

  on_osx brew install llvm@10 gmp libevent pcre openssl pkg-config
  on_osx brew link --force llvm@10
  # Note: brew link --force might show:
  #   Warning: Refusing to link macOS-provided software: llvm
  #
  #   If you need to have llvm first in your PATH run:
  #     echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.bash_profile
  #
  # This is added in the .circleci/config.yml

  on_tag verify_version
}

verify_version() {
  # If building a tag, check it matches with file
  FILE_VERSION=`cat ./src/VERSION`

  if [ "$FILE_VERSION" != "$CURRENT_TAG" ]
  then
    fail "VERSION ($FILE_VERSION) does not match GIT TAG ($CURRENT_TAG)"
  fi
}

with_build_env() {
  command="$1"

  # Ensure non GMT timezone
  export TZ="America/New_York"

  on_linux verify_linux_environment

  export DOCKER_TEST_PREFIX="${DOCKER_TEST_PREFIX:=crystallang/crystal:0.33.0}"

  case $ARCH in
    x86_64)
      export DOCKER_TEST_IMAGE="$DOCKER_TEST_PREFIX-build"
      ;;
    x86_64-musl)
      export DOCKER_TEST_IMAGE="$DOCKER_TEST_PREFIX-alpine-build"
      ;;
    i386)
      export DOCKER_TEST_IMAGE="$DOCKER_TEST_PREFIX-i386-build"
      ;;
  esac

  on_linux docker run \
    --rm -t \
    -u $(id -u) \
    -v $PWD:/mnt \
    -v /etc/passwd:/etc/passwd \
    -v /etc/group:/etc/group \
    -w /mnt \
    -e CRYSTAL_CACHE_DIR="/tmp/crystal" \
    "$DOCKER_TEST_IMAGE" \
    "$ARCH_CMD" /bin/sh -c "'$command'"

  on_osx sudo systemsetup -settimezone $TZ
  on_osx PATH="~/crystal/bin:\$PATH" \
    CRYSTAL_LIBRARY_PATH="~/crystal/embedded/lib" \
    CRYSTAL_CACHE_DIR="/tmp/crystal" \
    /bin/sh -c "'$command'"

}

usage() {
  cat <<EOF
bin/ci [-h|--help] command [parameter ...]

Helper script to prepare and run the testsuite on Travis CI.

Commands:
  prepare_system          setup any necessaries repositories etc.
  prepare_build           download and extract any dependencies needed for the build
  build                   run specs, build crystal, build samples, build the docs
  format                  build crystal, run format check
  with_build_env command  run command in the build environment
  help                    display this

EOF
}

command="$1"
shift
case $command in
  prepare_system)
    prepare_system
    ;;
  prepare_build)
    prepare_build
    ;;
  with_build_env)
    target_command="${@}"
    with_build_env "$target_command"
    ;;
  build)
    build
    ;;
  format)
    format
    ;;
  -h|--help|help)
    usage
    ;;
  *)
    if [ -n "$command" ]; then
      fail "Unknown command $command"
    else
      usage
      exit 1
    fi
    ;;
esac
