#! /bin/bash
# Last edited on 2017-07-28 22:05:27 by jstolfi

cmd="$0"; cmd="${cmd##*/}"

# Usage: ${cmd} {DIR1} {DIR2} {ITEM}..
#
# Moves the specified files and subdirectories from directory
# {DIR1} to {DIR2}, creating subdirectories if needed.
# For example 
#
#  "${cmd} ber/bar yap/wek/tuk goo.txt alpha/beta/gamma alpha/xxx.txt
#
# will do basically
#
#    mv ber/bar/goo.txt          yap/wek/tuk/
#    mkdir -p                    yap/wek/tuk/alpha/beta
#    mv ber/bar/alpha/beta/gamma yap/wek/tuk/alpha/beta/
#    mv ber/bar/alpha/xxx.txt    yap/wek/tuk/alpha/
#
# If an {ITEM} already exists in {DIR1}, the action depends
# on its type.  If the {ITEM} is a single file, 
# and its date in {DIR2} is older than in {DIR2},
# renames the old version with "~~~" suffix before moving
# the new one over it.
# If the {ITEM} is a directory, or is a single file
# with a newer date in {DIR2}, aborts with error.

dir1="$1"; shift
dir2="$1"; shift
items=( "$@" )

function mvfile(){
  pdir="$1"; shift
  file="$1"; shift
  
  # Full path of file to be moved
  d1pfile="${dir1}/${pdir}/${file}"
  if [[ ! ( -f ${d1pfile} ) ]]; then
    echo "** ${d1pfile} does not exist or is not a file" 1>&2 ; exit 1
  fi
  
  # Full path of receiving parent directory:
  d2pdir="${dir2}/${pdir}"
  if [[ ! ( -d ${d2pdir} ) ]]; then
    echo "** ${d2pdir} does not exist or is not a directory" 1>&2 ; exit 1
  fi
  
  # Full path of moved file:
  d2pfile="${dir2}/${pdir}/${file}"
  if [[ -e ${d2pfile} ]]; then
    if [[ ${d2pfile} -nt ${d1pfile} ]]; then
      echo "** ${d2pfile} exists and is newer than ${d1pfile}" 1>&2 ; exit 1
    else
      mv -v ${d2pfile} ${d2pfile}~~~
    fi
  fi
  mv -vi ${d1pfile} ${d2pdir}/ 
  ls -l ${d2pfile}
}

function mvdir(){
  pdir="$1"; shift # Parent subdir.
  sdir="$1"; shift # Subdir to be moved.
  
  # Full path of directory to be moved
  d1psdir="${dir1}/${pdir}/${sdir}"
  if [[ ! ( -d ${d1psdir} ) ]]; then
    echo "** ${d1psdir} does not exist or is not a directory" 1>&2 ; exit 1
  fi
  
  # Full path of receiving parent directory:
  d2pdir="${dir2}/${pdir}"
  if [[ ! ( -d ${d2pdir} ) ]]; then
    echo "** ${d2pdir} does not exist or is not a directory" 1>&2 ; exit 1
  fi
  
  # Full path of moved directory:
  d2psdir="${dir2}/${pdir}/${sdir}"
  if [[ -e ${d2psdir} ]]; then
    echo "** ${d2psdir} already exists" 1>&2 ; exit 1
  fi
  
  mv -vi ${d1psdir} ${d2pdir}/ 
  ls -d ${d2psdir}
}

for d in ${dir1} ${dir2} ; do 
  if [[ ! ( -d ${d} ) ]]; then
    echo "** \"${d}\" does not exist or is not a directory" 1>&2 ; exit 1
  fi
done

for item in "${items[@]}" ; do
  
  # Check if item exists in ${dir1}:
  d1item="${dir1}/${item}"
  if [[ ! ( -e "${d1item}" ) ]]; then
    echo "** \"${d1item}\" does not exist" 1>&2 ; exit 1
  fi

  # Check if ${item} has the right type:
  if [[ ! ( ( -d "${d1item}" ) || ( -f "${d1item}" ) || ( -L "${d1item}" ) ) ]]; then
    echo "** \"${d1item}\" is not a directory, file, or symlink" 1>&2 ; exit 1
  fi


  # Split ${item} into parent subdir and entry:
  xx="./${item}" # Make sure that it has at least one "/".
  pdir="${xx%/*}" # Parent subdir.
  entry="${xx##*/}" # Entry to be moved.
  pdir="${pdir#./}"; # Remove superfluous "./" from parent subdir. 
  
  # Create the parent subdir in ${dir2}, if needed:
  if [[ ! ( -d "${dir2}/${pdir}" ) ]]; then
    mkdir -pv "${dir2}/${pdir}"
  fi
  
  # Check if item exists in ${dir1}:
  d1item="${dir1}/${item}"
  if [[ -d "${d1item}" ]]; then
    mvdir "${pdir}" "${entry}"
  elif [[ ( -f "${d1item}" ) || ( -L "${d1item}" ) ]]; then
    mvfile "${pdir}" "${entry}"
  else
    echo "** PROG ERROR at ${d1item} " 1>&2 ; exit 1
  fi
done
