#!/usr/bin/env bash

#
# # VCS (SCM) Module
#

# ## detect\_uri()
#
#
#
# Code Walkthrough
detect_uri()
{
  local extensions_path=$1 _uri_path
  shift
  _uri_path=( $(rebuild_path ".uri" $extensions_path $@ ) )

  if [[ -s "${_uri_path}" ]]
  then
    uri_path=${_uri_path}
    return 0
  else
    return 1
  fi
}

# ## scm\_identifier()
#
#
#
# Code Walkthrough
scm_identifier()
{
  local _url=$1
  variables_must_be_nonempty _url
  printf "${_url//\//_}"
}

# ## directory\_get()
#
#
#
# Code Walkthrough
directory_get()
{
  local initial_pwd="$PWD" _source=$1 _target=$2
  variables_must_be_nonempty _source _target
  rm -rf "${_target}" 2>&1 >/dev/null

  if cp -fR ${_source} "${_target}" >/dev/null 2>&1
  then
    echo "$(cd "${_source}"; pwd)" > "${_target}/.uri"
  else
    fail "There was an error copying '${_source}'"
  fi
}

# ## git\_get()
#
#
#
# Code Walkthrough
git_get()
{
  local initial_pwd="$PWD" _url=$1 _final_target=$2 branch=${3:-master} remote=${4:-origin}
  variables_must_be_nonempty _url _final_target

  local _target="${scm_path}/$( scm_identifier "${_url}" )"

  _final_target=$(
    builtin cd "${initial_pwd}"
    mkdir -p "${_final_target}"
    builtin cd "${_final_target}"
    pwd
  )

  if [[ -d "$_target/.git" ]]
  then
    builtin cd "${_target}"
    git checkout master -f -q 2>&1 >/dev/null
    git reset --hard HEAD 2>&1  >/dev/null
    git fetch ${remote} 2>&1 >/dev/null
    if [[ -z "$(git branch | awk "/${branch}$/" 2>/dev/null)" ]]
    then
      if ! (
        builtin cd "${repo_path}"
        git checkout -b "${branch}" --track "${remote}/${branch}" 2>&1 >/dev/null
        )
      then
        error "$remote $branch remote branch not found."
      fi
    elif [[ -z "$(git branch | awk "/\* $branch$/{print \$2}" 2>/dev/null)" ]]
    then
      if ! (
        builtin cd "${repo_path}"
        git checkout $branch >/dev/null 2>&1
        )
      then
        error "Unable to checkout $branch."
      fi
    fi

    if ! (
      builtin cd "${repo_path}"
      git pull --rebase origin $branch 2>&1 >/dev/null
      )
    then
      fail "Git pull failed."
    fi
  else
    mkdir -p "${_target}"
    if git clone --depth 1 ${_url} "${_target}" 2>&1 >/dev/null
    then
      echo "${_url}" > "${_target}/.uri"
    else
      local _url_https="${_url/git:\/\//https:\/\/}"
      if git clone --depth 1 ${_url_https} "${_target}" 2>&1 >/dev/null
      then
        echo "${_url_https}" > "${_target}/.uri"
      else
        fail "There was an error while cloning the repository from the url '${_url}'"
      fi
    fi
  fi

  rm -rf "${_final_target}" 2>&1 >/dev/null

  if ! cp -fR "${_target}" "${_final_target}" 2>&1 >/dev/null
  then
    fail "There was an error copying '${_target}' -> '${_final_target}'"
  fi

  builtin cd "$initial_pwd"
}

hg_get()
{
  local initial_pwd="$PWD" _url=$1 _target=$2
  builtin cd "${_target}"
  hg pull
}

svn_get()
{
  local initial_pwd="$PWD" _url=$1 _target=$2
  builtin cd "${_target}"
  svn update
}

# ## is\_github\_url()
#
#
#
# Code Walkthrough
is_github_url()
{
  #TODO: add validation that given repository exists on github
  ematch "$1" "^[[:alnum:]_-]+\/[[:alnum:]_-]+$"
}

# ## github\_get()
#
#
#
# Code Walkthrough
github_get()
{
  local _url="$1" _target="$2"
  shift
  shift
  # use git for fetching github repo
  git_get "git://github.com/${_url}.git" "${_target}" "$@"
  # and overwrite repo uri
  echo "${_url}" > "${_target}/.uri"
}

archive_get() {
  local _url="$1" _final_target="$2" _package_md5="${3:-#}"
  local _archive="${archives_path}/${_url##*/}"
  shift
  shift

  mkdir -p "${archives_path}"

  if [[ -f "${_archive}" ]] \
  && ! file_matches_md5 "${_archive}" "${_package_md5}"
  then
    log "${_url##*/} does not match md5 ${_package_md5}, removing and downloading."
    rm -f "${_archive}"
  fi

  if [[ ! -f "${_archive}" ]]
  then
    if [[ -f "${_url}" ]]
    then
      if ! copy_file --force "${_url}" to "${_archive}"
      then
        log "Copying from '${_url}' to '${_archive}' failed "
        return 1
      fi
    else
      if ! curl -L "${_url}" -o "${_archive}"
      then
        log "Downloading from '${_url}' to '${_archive}' failed "
        return 1
      fi
    fi
  fi

  rm -rf "${_final_target}" || true
  mkdir -p "${_final_target}"
  builtin cd "${_final_target}"

  case "${_archive}" in
    *.tar) # tar
      tar xf "${_archive}" || error "There was an error while extracting the archive '${_archive}'"
      ;;
    *.tar.gz|*.tgz) # gzip
      tar zxf "${_archive}" || error "There was an error while extracting the archive '${_archive}'"
      ;;
    *.tar.bz2|*.tbz2) # bzip
      tar jxf "${_archive}" || error "There was an error while extracting the archive '${_archive}'"
      ;;
    *.tar.xz) # LZMA
      # TODO: if 'xz' command exists, use that, otherwise use tar J
      tar Jxf "${_archive}" || error "There was an error while extracting the archive '${_archive}'"
      ;;
    *.zip)
      unzip "${_archive}" || error "There was an error while extracting the archive '${_archive}'"
      ;;
    *)
      error "Unknown archive format for ${_archive}"
      ;;
  esac

  if ! ls -d * &>/dev/null
  then
    error "Empty archive ${_archive}"
  fi
}

is_archive_uri() {
  case "$1" in
    *.tar|*.tgz|*.txz|*.tbz2|*.tar.xz|*.tar.bz2|*.tar.gz|*.zip)
      return 0
      ;;
  esac
  return 1
}

# ## fetch\_uri()
#
#
#
# Code Walkthrough
fetch_uri()
{
  trace_filter vcs

  [[ $# -ge 2 ]] || fail "Missing params _uri _target"

  log "${log_prefix}Fetching $1"
  unset scm_type

  if [[ -d "$1" ]] # check for directory
  then
    scm_type='directory'
    directory_get "$@"
    return $?

  elif is_archive_uri "$1" # check for packages
  then
    scm_type='package'
    archive_get "$@"
    return $?

  elif is_github_url "$1" # check for pattern user/repository from github
  then
    scm_type='github'
    github_get "$@"
    return $?
  fi

  case "$1" in
    (*git*)
      scm_type='git'
      git_get "$@"
      ;;
    (*svn*)
      scm_type='svn'
      NIY "Subversion scm_update"
      svn_get
      ;;
    (*hg*)
      scm_type='hg'
      NIY "hg scm_update"
      hg_get
      ;;
    (*fossil*)
      scm_type='fossil'
      NIY "fossil scm_update"
      ;;
    (*)
      error "Unknownn SCM for url '${_url}'"
      ;;
  esac
}

fetch()
{
  fetch_uri "$@"
}

scm_help()
{
  NIY "display help for scm"
}
