#!/bin/bash

#
# File .......... swuk_repos_split
# Author ........ Steve Haywood
# Website ....... http://www.spacewire.co.uk
# Project ....... Common (SpaceWire UK Tutorial)
# Date .......... 12 Feb 2025
# Version ....... 1.0
# Description ...
#   Clone a repository and checkout every one of its commits into a separate
# directory.
#

# Strict
set -euo pipefail

################################################################################
# Split a repository into its historial parts.
# Arguments ...... None
# Return ......... None
# Exit code ...... None
# Shared (In) .... argv, optkeepgit, project, url
# Shared (Out) ... argv
split()
{
  # Declare local constants
  local -r repo="repo"            # Str: Repository checkout directory
  local -r history="history.tsv"  # Str: History details file

  # Declare local variables
  local    hash                   # Str: Commit hash
  local -i index=1                # Int: Subdirectory index

  # Create project directory
  mkdir ${project}

  # Move into project directory
  cd ${project}

  # Clone repository
  echo -e "\e[93mCloning ... ${url} into ${repo}\e[0m"
  git clone ${url} ${repo}

  # Move into repository directory
  cd ${repo}

  # Iterate through all repository commits (versions)
  for hash in $(git log --reverse --pretty=format:"%h")
  do
    local tmp=$(printf "%04d" ${index})
    echo -e "\e[93mChecking out ... ${hash} into ${tmp}\e[0m"

    # Switch to a commit (version) using its hash
    git checkout ${hash}

    # Copy all the files in the commit (version)
    cp -r . ../${tmp}
    [[ ($(option_set ${optkeepgit}) -eq 0) ]] && rm -rf ../${tmp}/.git

    # Get the commit comment, tag & tag comment
    local comment=$(git show ${hash} --no-patch --oneline --pretty="%s")
    local tag=$(git describe --tags --exact-match ${hash})
    local tcomment=""
    if [ -n "${tag}" ]; then
      tcomment=$(git tag -l --format='%(contents)' ${tag})
    fi

    # Output the 3 fields into the history details file
    echo -e "${comment}\t${tag}\t${tcomment}" >> ../${history}

    # Increment subdirectory index
    ((index+=1))
  done

  # Move out of repository directory
  cd ..

  # Delete repository directory
  rm -rf ${repo}

  # Move out of project directory
  cd ..
}


################################################################################
# Check if an argument option is set.
# Arguments ...... $1 ... Str: Option
# Return ......... Set (0=unset, 1=set)
# Shared (In) .... argv
# Shared (Out) ... None
option_set()
{
  [[ " ${argv[*]} " =~ " ${1} " ]] && echo 1 || echo 0
}


################################################################################
# Main function.
# Arguments ...... None
# Return ......... None
# Exit code ...... Status (0=success, 1=failure)
# Shared (In) .... argv
# Shared (Out) ... argv, optkeepgit, project, url
main()
{
  # Declare local constants
  local -r  optkeepgit="--keepgit"  # Str: KeepGIT option name
  local -r  opthelp="--help"        # Str: Help option name
  local -Ar options=(               # ARR: Options & associated help information
    [${optkeepgit}]="Keep the .git repository directories."
    [${opthelp}]="Display this help and exit."
  )
  local -ar optorder=(              # Arr: Help options display order
    ${optkeepgit}
    ${opthelp}
  )

  # Declare local variables
  local    arg                      # Str: Current argument from argv array
  local    project=""               # Str: Project directory [arg] (mandatory)
  local    url=""                   # Str: GIT repository URL [arg] (mandatory)
  local    option                   # Str: Current option from optorder array

  # Display help information
  if (($(option_set ${opthelp}))); then
    echo "Usage: $(basename ${0}) GIT-REPOSITORY... PROJECT-DIRECTORY... [OPTION]..."
    echo "Clone a GIT-REPOSITORY and checkout every one of its commits into a separate subdirectory within the PROJECT-DIRECTORY."
    echo
    for option in ${optorder[@]}
    do
      echo "      ${option} $(printf ' %.0s' {1..12} | head -c $((12-${#option}))) ${options[${option}]}"
    done
    echo
    exit 0
  fi

  # Get & check the arguments
  for arg in ${argv[@]}; do
    if [[ ${arg:0:2} == "--" ]]; then  # Option
      [[ ! -v options[${arg}] ]] && echo "Option (${arg}) is not recognised!" && exit 1
    elif [[ -z ${url} ]]; then  # GIT repository URL
      url=$(sed -n 's/^\(.\+@.\+:.\+\)$/\1/p' <<< ${arg})
      [[ -z ${url} ]] && echo "GIT repository URL (${arg}) seems incorrect!" && exit 1
    elif [[ -z ${project} ]]; then  # Project directory
      project=${arg}
      [[ -d ${project} ]] && echo "Project Directory (${arg}) already exist!" && exit 1
    else
      echo "Unexpected argument (${arg}) found!" && exit 1
    fi
  done

  # Further check the arguments
  [[ -z ${url} ]] && echo "No GIT repository URL specified!" && exit 1
  [[ -z ${project} ]] && echo "No project directory specified!" && exit 1

  # Do the repository split
  split
}


################################################################################
# Opening gambit.
# Arguments ...... None
# Return ......... None
# Exit code ...... Status (0=success, 1=failure)
# Shared (In) .... $#, $@
# Shared (Out) ... argc, argv
declare -ri argc=${#}    # Int: Get argument count
declare -ra argv=(${@})  # Arr: Get argument values (space-separated) into array
main
exit 0
