# rename files to strip all special characters.
# usage: name [FILES]
name()
{
  local IFS=$'\n'
  local targets=("${@}") # target file(s).
  local count=0          # processed count.
  local total=${#}       # total to process.
  local failed=0

  # all targets except hidden by default.
  if [[ "${targets}" = "" ]]; then
    targets=([^.]*)
    total=${#targets[@]}
  fi

  # process.
  for target in "${targets[@]}"; do
    # increment counter.
    ((count++))

    # parse new name.
    local ext=""
    local name="${target}"

    # ext only for files.
    if [[ -f "${target}" ]]; then
      ext=".${target##*.}"
      name="${target%.*}"
    fi
    
    # Files w/o extension support.
    [[ "${ext#.}" = "${name}" ]] && ext=""

    # Get new name.
    local new_name=$(parse_simplify "${name}")${ext,,}

    # status line.
    local status="[${count}/${total}] ${target} -> ${new_name}"
    echo -e "${status}"

    # check if same name.
    [[ "${target}" = "${new_name}" ]] && continue

    # check if target name already exists.
    if [[ -f "${new_name}" ]]; then
      echo -e "${color_bred}${status}: already exists!${color_default}"
      ((failed++))
    fi

    # rename target.
    mv -- "${target}" "${new_name}"

    if [[ ${?} != 0 ]]; then
      echo -e "${color_bred}${status}: Failed.${color_default}"
      ((failed++))
    fi
  done

  if [[ ${failed} != 0 ]]; then
    echo -e "${color_bred}Failed: ${failed}.${color_default}"
    false
  fi
}

# rename all files to their hashes while keeping extensions.
# usage: name_hash [FILES]
name_hash()
{
  local IFS=$'\n'
  local targets=("${@}") # target file(s).
  local count=0          # processed counter.
  local total=${#}       # total to process.
  local failed=0

  # all files except hidden by default.
  if [[ "${targets}" = "" ]]; then
    targets=($(ls --classify | grep -v /\$))
    total=${#targets[@]}
  fi

  # process.
  for target in "${targets[@]}"; do
    # increment count.
    ((count++))

    # extract extension.
    local extension="${target##*.}"
    if [[ "${extension}" = "${target}" ]]; then
      extension=""
    else
      extension=".${extension}"
    fi

    # hash the new name.
    local hash=$(pv "${target}" | sha1sum | cut -d\  -f1)
    new_name="${hash,,}${extension,,}"
    
    # prepare status.
    local status="[${count}/${total}] ${target} -> ${new_name}"

    # check if same name.
    if [[ "${target}" = "${new_name}" ]]; then
      # echo -e "${color_green}${status}: No change.${color_default}"
      echo -e "${status}"
      continue
    fi

    # show change.
    echo -e "${status}"

    # rename target.
    mv -- "${target}" "${new_name}"
    
    if [[ ${?} != 0 ]]; then
      echo -e "${color_bred}${status}: Failed.${color_default}"
      ((failed++))
    fi
  done

  if [[ ${failed} != 0 ]]; then
    echo -e "${color_bred}Failed: ${failed}.${color_default}"
    false
  fi
}

# check hashes for renamed files.
# usage: name_hash_check [FILES]
name_hash_check()
{
  local IFS=$'\n'
  local targets=("${@}") # target file(s).
  local total=${#}       # total to process.
  local count=0          # processed counter.
  local failed=0

  # all targets by default.
  if [[ "${targets}" = "" ]]; then
    targets=($(ls --classify | grep -v /\$))
    total=${#targets[@]}
  fi

  # process.
  for target in "${targets[@]}"; do
    # increment count.
    ((count++))

    # status info.
    local status="[${count}/${total}] ${target}"

    echo -e "${status}"

    # extract hashes.
    local stored="${target%%.*}"
    local actual=$(pv "${target}" | sha1sum | cut -d\  -f1)

    # compare hashes.
    if [[ "${stored}" != "${actual}" ]]; then
      echo -e "${color_bred}${status}: Failed.${color_default}"
      ((failed++))
    fi
  done

  if [[ ${failed} != 0 ]]; then
    echo -e "${color_bred}Failed: ${failed}.${color_default}"
    false
  fi
}

# rename files for Jellyfin series, i.e. Episode S01E01.mkv
# usage: name_series <SEASON> [FILES]
name_series()
{
  local IFS=$'\n'
  local season="${1}"      # Season number.
  local targets=("${@:2}") # Target files.
  local count=0            # Processed counter.
  local episode=0          # Current episode count.
  local total=${#}         # Total to process.
  local failed=0

  # error when no season number specified.
  if [[ "${season}" = "" ]]; then
    echo "usage: name_series <SEASON> [FILES]"
    return 2
  fi

  # all targets by default.
  if [[ "${targets}" = "" ]]; then
    targets=($(ls --classify | grep -v /\$))
    total=${#targets[@]}
  fi

  # process.
  for target in "${targets[@]}"; do
    # increment episode number.
    ((count++))
    ((episode++))

    # extract new name.
    local new_name="Episode S${season}E$(printf %02d ${episode}).${target##*.}"

    # prepare status.
    local status="[${count}/${total}] ${target} -> ${new_name}"
    echo -e "${status}"

    # Warning on no change.
    [[ "${target}" = "${new_name}" ]] && continue

    # rename target.
    mv -- "${target}" "${new_name}"
    
    if [[ ${?} != 0 ]]; then
      echo -e "${color_bred}${status}: Failed.${color_default}"
      ((failed++))
    fi
  done

  if [[ ${failed} != 0 ]]; then
    echo -e "${color_bred}Failed: ${failed}.${color_default}"
    false
  fi
}

# rename files for Kavita manga format.
# usage: name_manga <SEASON> [FILES]
name_manga()
{
  local IFS=$'\n'
  local season="${1}"      # Season number.
  local targets=("${@:2}") # Target files.
  local count=0            # Processed counter.
  local episode=0          # Current episode count.
  local total=${#}         # Total to process.
  local manga="${PWD##*/}" # Manga name.
  local failed=0

  # error when no season number specified.
  if [[ "${season}" = "" ]]; then
    echo "usage: name_manga <SEASON> [FILES]"
    return 2
  fi

  # all targets by default.
  if [[ "${targets}" = "" ]]; then
    targets=($(ls --classify | grep -v /\$))
    total=${#targets[@]}
  fi

  # process.
  for target in "${targets[@]}"; do
    # increment episode number.
    ((count++))
    ((episode++))

    # extract new name.
    local new_name="${manga} Vol.${season} Ch.${episode}.${target##*.}"

    # prepare status.
    local status="[${count}/${total}] ${target} -> ${new_name}"
    echo -e "${status}"

    # Warning on no change.
    [[ "${target}" = "${new_name}" ]] && continue

    # rename target.
    mv -- "${target}" "${new_name}"
    
    if [[ ${?} != 0 ]]; then
      echo -e "${color_bred}${status}: Failed.${color_default}"
      ((failed++))
    fi
  done

  if [[ ${failed} != 0 ]]; then
    echo -e "${color_bred}Failed: ${failed}.${color_default}"
    false
  fi
}

# rename files for new extension.
# usage: name_ext <EXTENSION> [FILES]
name_ext()
{
  local IFS=$'\n'
  local extension="${1}"   # new extension.
  local targets=("${@:2}") # target file(s).
  local count=0            # processed counter.
  local total=$((${#}-1))  # total to process.
  local failed=0

  # error when no new extension specified.
  if [[ "${extension}" = "" ]]; then
    echo "Usage: name_ext <EXTENSION> [FILES]"
    return 2
  fi

  # all targets by default.
  if [[ "${targets}" = "" ]]; then
    targets=($(ls --classify | grep -v /\$))
    total=${#targets[@]}
  fi
  
  # process.
  for target in "${targets[@]}"; do
    # increment count.
    ((count++))

    # extract new name.
    local new_name="${target%.*}"."${extension}"

    # Prepare status.
    local status="[${count}/${total}] ${target} -> ${new_name}"
    echo -e "${status}"

    # Warning on no change.
    [[ "${target}" = "${new_name}" ]] && continue

    # rename target.
    mv -- "${target}" "${new_name}"
    
    if [[ ${?} != 0 ]]; then
      echo -e "${color_bred}${status}: Failed.${color_default}"
      ((failed++))
    fi
  done

  if [[ ${failed} != 0 ]]; then
    echo -e "${color_bred}Failed: ${failed}.${color_default}"
    false
  fi
}

# Change file prefixes.
# Usage: name_prefix <OLD> <NEW> [FILES]
name_prefix()
{
  local IFS=$'\n'
  local old="${1}"         # Old prefix.
  local new="${2}"         # New prefix.
  local targets=("${@:3}") # Target files.
  local count=0            # Total files processed.
  local total=$((${#}-2))  # Total files to process.
  local failed=0

  # All targets by default.
  if [[ "${targets}" = "" ]]; then
    targets=(${old}*)
    total=${#targets[@]}
  fi
  
  # Process.
  for target in "${targets[@]}"; do
    # Increment counter.
    ((count++))

    # Create new name.
    local new_name="${new}${target#$old}"

    # Prepare status.
    local status="[${count}/${total}] ${target} -> ${new_name}"
    echo -e "${status}"

    # Warning on no change.
    [[ "${target}" = "${new_name}" ]] && continue
    
    # Rename.
    mv -- "${target}" "${new_name}"
    
    if [[ ${?} != 0 ]]; then
      echo -e "${color_bred}${status}: Failed.${color_default}"
      ((failed++))
    fi
  done

  if [[ ${failed} != 0 ]]; then
    echo -e "${color_bred}Failed: ${failed}.${color_default}"
    false
  fi
}

# Change file postfix.
# Usage: name_postfix <OLD> <NEW> [FILES]
name_postfix()
{
  local IFS=$'\n'
  local old="${1}"         # Old postfix.
  local new="${2}"         # New postfix.
  local targets=("${@:3}") # Target files.
  local count=0            # Total files processed.
  local total=$((${#}-2))  # Total files to process.
  local failed=0

  # All targets by default.
  if [[ "${targets}" = "" ]]; then
    targets=(*${old})
    total=${#targets[@]}
  fi
  
  # Process.
  for target in "${targets[@]}"; do
    # Increment counter.
    ((count++))

    # Create new name.
    local new_name="${target%$old}${new}"

    # Prepare status.
    local status="[${count}/${total}] ${target} -> ${new_name}"
    echo -e "${status}"

    # Warning on no change.
    [[ "${target}" = "${new_name}" ]] && continue
    
    # Rename.
    mv -- "${target}" "${new_name}"
    
    if [[ ${?} != 0 ]]; then
      echo -e "${color_bred}${status}: Failed.${color_default}"
      ((failed++))
    fi
  done

  if [[ ${failed} != 0 ]]; then
    echo -e "${color_bred}Failed: ${failed}.${color_default}"
    false
  fi
}

# Replace part of the name.
name_replace()
{
  local IFS=$'\n'
  local old="${1}"         # Old postfix.
  local new="${2}"         # New postfix.
  local targets=("${@:3}") # Target files.
  local count=0            # Total files processed.
  local total=$((${#}-2))  # Total files to process.
  local failed=0

  # All targets by default.
  if [[ "${targets}" = "" ]]; then
    targets=(*${old}*)
    total=${#targets[@]}
  fi
  
  # Process.
  for target in "${targets[@]}"; do
    # Increment counter.
    ((count++))

    # Create new name.
    local new_name="${target//$old/$new}"

    # Prepare status.
    local status="[${count}/${total}] ${target} -> ${new_name}"
    echo -e "${status}"

    # Warning on no change.
    [[ "${target}" = "${new_name}" ]] && continue
    
    # Rename.
    mv -- "${target}" "${new_name}"
    
    if [[ ${?} != 0 ]]; then
      echo -e "${color_bred}${status}: Failed.${color_default}"
      ((failed++))
    fi
  done

  if [[ ${failed} != 0 ]]; then
    echo -e "${color_bred}Failed: ${failed}.${color_default}"
    false
  fi

}

# export for parallel.
export -f name name_hash name_hash_check name_ext