#!/bin/bash

#
# File .......... swuk_repos_join
# Author ........ Steve Haywood
# Website ....... http://www.spacewire.co.uk
# Project ....... Common (SpaceWire UK Tutorial)
# Date .......... 12 Feb 2025
# Version ....... 1.0
# Description ...
#   Join an ordered history of project subdirectories into a new single GIT
# repository on a remote server. Also make use of a history file that includes
# the tab separated values of commit comment, tag name & tag comment.
#

# Strict
set -euo pipefail

################################################################################
# Perform the repository join.
# Arguments ...... None
# Return ......... None
# Shared (In) .... argv, optfix, project, user, addr, repo
# Shared (Out) ... argv
do_join()
{
  # Declare local constants
  local -r now=$(date +"%d-%b-%Y_%H_%M_%S")  # Str: Current date & time
  local -r history="history.tsv"             # Str: File containing GIT history

  # Declare local variables
  local -a projdirs=()                       # Arr: List of project directories (alphanumeric order)
  local    projdir                           # Str: Current project directory from projdirs array
  local -a projhist=()                       # Arr: List of project git change history
  local -i index=0                           # Int: Current directory index

  # Get all separate repository directories into an ordered array
  readarray -t projdirs < <(find ${project} -mindepth 1 -maxdepth 1 -type d | sort | cut -d'/' -f2-)

  # Get GIT history into an array
  readarray -t projhist < ${project}/${history}

  # Backup existing remote repository
  ssh -t ${user}@${addr} "if [[ -d ${repo}.git ]]; then mv ${repo}.git ${repo}_${now}.git; fi"

  # Create a new remote repository
  ssh -t ${user}@${addr} "git init --bare ${repo}.git"

  # Move into project directory
  cd ${project}

  # Iterate through project subdirectories
  for projdir in ${projdirs[@]}
  do
    # Move into project subdirectory
    cd ${projdir}

    echo -ne "\e[93mProcessing project directory : ${projdir}\e[0m\n"

    # Create local repository
    if [[ ${index} -eq 0 ]]; then
      git init
      git remote add origin ${user}@${addr}:${repo}.git
    fi

    # Move repository (.git) from previous to current
    if [[ ${index} -gt 0 ]]; then
      mv ../${projdirs[$((index-1))]}/.git .
    fi

    # Get GIT commit comment, tag & tag comment
    IFS=$'\n\t'
    local -a split=(${projhist[index]}) # Get history values (tab-separated) into array
    unset IFS
    [[ ${#split[@]} -ge 1 ]] && local comment=${split[0]} || local comment=""
    [[ ${#split[@]} -ge 2 ]] && local tag=${split[1]} || local tag=""
    [[ ${#split[@]} -ge 3 ]] && local tcomment=${split[2]} || local tcomment=""

    # Add files, commit & push to remote
    git add -A

    # Action the --fix option
    if (($(option_set ${optfix}))); then
      # Force through the ignored .gitignore files
      [[ ($(expr ${projdir} + 0) -ge 18) && ($(expr ${projdir} + 0) -le 21) ]] &&
        git add -f zedboard_leds_switches/.gitignore
      # Force through the wrongly placed apache2_%.bbappend (only if it exists, i.e. not fixed during splice)
      [[ ($(expr ${projdir} + 0) -eq 24) && (-f zedboard_linux/os/petalinux/components/yocto/layers/meta-openembedded/meta-webserver/recipes-httpd/apache2/apache2_%.bbappend) ]] &&
        git add -f zedboard_linux/os/petalinux/components/yocto/layers/meta-openembedded/meta-webserver/recipes-httpd/apache2/apache2_%.bbappend
    fi

    git commit -a -m "${comment}"
    git push -u origin master

    # Add tag & push to remote
    if [ -n "${tag}" ]; then
      git tag -a "${tag}" -m "${tcomment}"
      git push origin "${tag}"
    fi

    # Delete local repository
    if [[ $((index+1)) -eq ${#projhist[@]} ]]; then
      rm -rf ".git"
    fi

    # Move out of project subdirectory
    cd ..

    # Increment current directory index
    ((index+=1))
  done

  # 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, optfix, project, user, addr, repo
main()
{
  # Declare local constants
  local -r  optfix="--fix"    # Str: Fix option name
  local -r  opthelp="--help"  # Str: Help option name
  local -Ar options=(         # ARR: Options & associated help information
    [${optfix}]="Fix known repository issues."
    [${opthelp}]="Display this help and exit."
  )
  local -ar optorder=(        # Arr: Help options display order
    ${optfix}
    ${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) (tab separated)
  local    user=""            # Str: GIT repository user [arg] (mandatory)
  local    addr=""            # Str: GIT repository address [arg] (mandatory)
  local    repo=""            # Str: GIT repository name [arg] (mandatory)
  local    option             # Str: Current option from optorder array

  # Display help information
  if (($(option_set ${opthelp}))); then
    echo "Usage: $(basename ${0}) PROJECT-DIRECTORY... GIT-REPOSITORY... [OPTION]..."
    echo "Combine an ordered history of project subdirectories in PROJECT-DIRECTORY into a new single GIT-REPOSITORY."
    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 ${project} ]]; then  # Project directory
      project=${arg}
      [[ ! -d ${project} ]] && echo "Project directory (${arg}) does not exist!" && exit 1
    elif [[ -z ${url} ]]; then  # GIT repository URL
      url=$(sed -n 's/^\(.\+\)@\(.\+\):\(.\+\)$/\1\t\2\t\3/p' <<< ${arg})
      [[ -z ${url} ]] && echo "GIT repository URL (${arg}) seems incorrect!" && exit 1
      local -ra split=(${url})  # Get url values (tab-separated) into array
      user=${split[0]}
      addr=${split[1]}
      repo=${split[2]}
    else
      echo "Unexpected argument (${arg}) found!" && exit 1
    fi
  done

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

  # Do the join operation
  do_join
}


################################################################################
# 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
