#!/bin/bash
# shellcheck disable=SC2155
if ! type "include" > /dev/null 2>&1; then
  include() {
  . "$(cd "$(dirname "$1")" && pwd)/${2}";
  };
fi
include "${BASH_SOURCE[0]}" ../pragma_once.bash
if ! pragma_once ${BASH_SOURCE[0]}; then return 0; fi
include "${BASH_SOURCE[0]}" ./global_rely/export.bash
# shellcheck source=./global_rely/export.bash




mkdir_with_symlink() {
  local dest_dir="$1"     # 要处理的目标目录
  local install_root="$2" # 安装根目录
  is_absolute_directory "$dest_dir" || {
    echo "mkdir_with_rlink dest_dir=${dest_dir}"
    exit 1
  }
  is_absolute_directory "$install_root" || {
    echo "install_root=${install_root}"
    exit 1
  }
  local target="${install_root}${dest_dir}"
  if is_absolute_directory "$target"; then
    #echo "$target 文件夹已存在，取消mkdir_symlink！"
    return 0
  fi
  local current_path=""
  local dir_parts=()
  IFS='/' read -ra dir_parts <<<"$dest_dir"
  dir_parts=("${dir_parts[@]:1}") #（因为根目录是空字符串）
  local dir_name
  for dir_name in "${dir_parts[@]}"; do
    current_path="$current_path/$dir_name"
    local target_dir_path="${install_root}${current_path}"
    if [ -L "$current_path" ]; then
      local extern_rlink_target_path=$(readlink -f "$current_path")
      local rlink_path="${target_dir_path}"
      local rlink_dir_path="$(dirname "${rlink_path}")"
      local rlink_target_path="$install_root$extern_rlink_target_path"
      if [ ! -L "$rlink_path" ] || [ "$(readlink -f "$rlink_path")" != "$rlink_path" ]; then
        mkdir_with_symlink "$extern_rlink_target_path" "$install_root"
        get_relative_path "$rlink_dir_path" "$rlink_target_path"
        ln -sf "$_ret_" "$rlink_path"
        [ -n "$_cur_log_path_" ] && echo "=======dir symlink=======  ln -sf $_ret_ $rlink_path    " >>"$_cur_log_path_"
      fi
    elif [ ! -e "$target_dir_path" ]; then
      mkdir -p "$target_dir_path"
    fi
  done
}

cp_with_symlink() {
  local dest_file="$1" # 源文件路径
  local install_root="$2"
  is_absolute_file "$dest_file" || {
    echo "[cp_with_symlink error] dest_file=${dest_file} is not absolute file！"
    exit 1
  }
  is_absolute_directory "$install_root" || {
    echo "[cp_with_symlink error] install_root=${install_root} is not absolute directory！"
    exit 1
  }
  local target="${install_root}${dest_file}"
  if is_absolute_file "$target"; then
    echo "$target 文件已存在，取消cp_symlink！"
    return 0
  fi
  local dest_dir
  dest_dir=$(dirname "$dest_file")
  mkdir_with_symlink "${dest_dir}" "$install_root"

  local ret
  ret=$target
  if [ -L "$dest_file" ]; then
    local extern_rlink_path="$dest_file"
    local extern_rlink_target_path=$(readlink -f "$dest_file")
    cp_with_symlink "${extern_rlink_target_path}" "$install_root"
    ret=$_ret_
    local rlink_target_path="${install_root}${extern_rlink_target_path}"
    local rlink_path="${install_root}${extern_rlink_path}"
    local rlink_path_dir="$(dirname ${rlink_target_path})"
    get_relative_path "$rlink_path_dir" "$rlink_target_path"
    ln -sf "$_ret_" "$rlink_path"
    [ -n "$_cur_log_path_" ] && echo "=file_symlink= ln -sf $_ret_ $rlink_path " >>"$_cur_log_path_"
  else
    cp "${dest_file}" "${target}"
    echo "cp ${dest_file} ==> ${target}"
  fi

  _ret_=$ret
  return 0
}

# $1 传入一个数组，用于拷贝他们的依赖
# $2 拷贝依赖的根目录
copy_dependencies() {
  #local abs_exe_path=$(realpath "$1")
  local install_root="$2"
  declare -A path_seen
  declare -A error_line_skipped
  declare -A rely_dir
  local ldd_log="${install_root}/deploy_ldd.txt"
  local run_path_log="${install_root}/deploy_run_path.txt"
  local illegal_line="${install_root}/illegal_line.txt"
  extract_absolute_paths() {
    rely_dir=()
    local abs_exe_path="$1"
    local lines
    mapfile -t lines < <(ldd "$abs_exe_path")
    local result=()
    echo "abs_exe_path == $abs_exe_path" >>"${ldd_log}"
    for line in "${lines[@]}"; do
      echo "line == $line" >>"${ldd_log}"
      if [[ -z "$line" ]]; then
        continue
      fi
      if [[ "$line" =~ ^[[:space:]]*/lib64/ld-linux-x86-64 ]]; then
        continue
      fi
      if [[ "$line" =~ ^[[:space:]]*linux-vdso.so ]]; then
        continue
      fi
      if [[ "$line" =~ ^[[:space:]]*statically\ linked ]]; then
        continue
      fi
      if [[ -n "${error_line_skipped[$line]}" ]]; then
        continue
      fi
      local fields
      IFS=' ' read -r -a fields <<<"$line"
      local num_fields=${#fields[@]}
      if [ "${num_fields}" != 4 ]; then
        echo "${abs_exe_path} == ${line} 判断属性数量错误！ num_fields == ${num_fields}" >>"$illegal_line"
        error_line_skipped["$line"]=1
        continue
      fi
      if [[ "${fields[2]}" == "not" && "${fields[3]}" == "found" ]]; then
        echo "严重错误! ${abs_exe_path} 找不到$abs_exe_path的依赖: ${line}" | tee -a "$illegal_line"
        #read -n 1  # 等待用户按下一个键
        #exit 1
      fi
      if [ "${fields[1]}" != "=>" ]; then
        echo "${abs_exe_path} == 判断是否第二个是=> ${fields[1]} != '>'" >>"$illegal_line"
        error_line_skipped["$line"]=1 # 记录已跳过的行
        continue
      fi
      if [[ "${fields[2]}" =~ ^/ ]]; then
        local path_with_rlink="${fields[2]}"
        local dir_path_with_rlink=$(dirname "${path_with_rlink}")
        if [[ -z "${rely_dir["$dir_path_with_rlink"]}" ]]; then
          rely_dir["$dir_path_with_rlink"]=1
        fi
        if [[ -z "${path_seen[$path_with_rlink]}" ]]; then
          result+=("${path_with_rlink}")
          path_seen["$path_with_rlink"]=1
        fi
      fi
    done
    local rely_dir_path
    local runpath_list=""
    declare -A seen_paths
    for rely_dir_path in "${!rely_dir[@]}"; do
      get_relative_origin_path "$(dirname "${abs_exe_path}")" "${rely_dir_path}"
      path="${_ret_}"
      if [[ -n "$path" && -z "${seen_paths[$path]}" ]]; then
        seen_paths[$path]=1
        runpath_list+="${path}:"
      fi
    done
    runpath_list="${runpath_list%:}"
    if [[ "${runpath_list}" ]]; then
      patchelf --set-rpath "${runpath_list}" "${install_root}${abs_exe_path}"
    fi
    echo "${abs_exe_path} == ${runpath_list} " >>"${run_path_log}"
    _ret_="$(
      IFS=:
      echo "${result[*]}"
    )"
  }
  _cp_rely() {
    local abs_exe_path="$1"
    local install_root="$2"
    mkdir -p "${install_root}"
    chmod 777 "${install_root}"
    cp_with_symlink "${abs_exe_path}" "${install_root}"
    extract_absolute_paths "${abs_exe_path}"
    local tobe_install_list
    local tobe_install
    IFS=':' read -ra tobe_install_list <<<"$_ret_"
    for tobe_install in "${tobe_install_list[@]}"; do
      if [ -e "$tobe_install" ]; then
        _cp_rely "$tobe_install" "$install_root"
      fi
    done
  }

  _cur_log_path_="cp_reply_log.txt"

  paths=$1
  IFS=':' read -r -a path_array <<<"$paths"
  for path in "${path_array[@]}"; do
    local abs_exe_path
    if [ -d "$path" ]; then
      local executable_files=$(find "$path" -type f -executable)
      while IFS= read -r abs_exe_path; do
        _cp_rely "$abs_exe_path" "$install_root"
      done <<<"$executable_files"
    elif [ -f "$path" ]; then
      abs_exe_path=$(realpath "$path")
      _cp_rely "$abs_exe_path" "$install_root"
    else
      echo "ERROR WARING _cp_rely 路径无效或不存在: $path" >>"${_cur_log_path_}"
      echo "ERROR WARING _cp_rely 路径无效或不存在: $path"
      exit 1
    fi
  done
}

# 获取一个名称为了不重复  让目录尾数递加
get_no_repeat_dir() {
  local install_root="$1"
  local original_install_root="$install_root"
  local counter=1
  while [ -e "$install_root" ]; do
    install_root="${original_install_root}_${counter}" # 在原目录名后加上数字
    ((counter++))                                      # 增加计数器
  done
  _ret_="$install_root"
}

copy_qt_plugin() {
  local target_dir="$1"
  local qt_plugin_dir="$2"

  # 设置默认的 Qt 插件目录
  local default_qt_plugin_dirs=(
    "/home/env/Qt5.14.2/5.14.2/gcc_64/plugins"
    "/usr/lib/x86_64-linux-gnu/qt5/plugins/"
    "/usr/lib/qt/plugins/"
    "/usr/local/Qt-*/plugins/"
  )

  # 如果没有提供 qt_plugin_dir，尝试使用默认目录
  if [ -z "$qt_plugin_dir" ]; then
    for dir in "${default_qt_plugin_dirs[@]}"; do
      # 使用通配符来查找可能的目录
      for plugin_dir in $dir; do
        if [ -d "$plugin_dir" ]; then
          qt_plugin_dir="$plugin_dir"
          break 2 # 找到一个有效的目录，退出循环
        fi
      done
    done
  fi

  # 检查最终选择的源目录是否存在
  if [ ! -d "$qt_plugin_dir" ]; then
    echo "错误: 源目录 ${qt_plugin_dir} 不存在。"
    return 1
  fi

  # 创建目标目录并复制插件
  mkdir -p "$target_dir"
  cp -r "$qt_plugin_dir" "$target_dir/"

  echo "成功复制 Qt 插件到 ${target_dir}。"
  return 0
}

# 第一个参数可执行文件或库绝对路径
# 发布的目录绝对路径  （不一定是真实的 同名会把名+1）
deploy() {
  local executable="$1"
  get_no_repeat_dir "$2"
  local install_root=${_ret_}
  echo "deploy "
  echo "executable $1"
  echo "no_repeat install_root $install_root"
  echo "install_root $2"
  mkdir -p "$install_root"
  chmod 777 "$install_root"
  local extern_exe_path=$(realpath "${executable}")
  local extern_exe_dir=$(dirname "${extern_exe_path}")
  local exe_path="${install_root}${extern_exe_path}"
  local exe_dir="${install_root}${extern_exe_dir}"
  copy_dependencies "${extern_exe_path}" "${install_root}"
  ln -sf ".$extern_exe_path" "$install_root/$(basename "$extern_exe_path")"
  compress_directory_tar_gz "$install_root" "$(dirname "$2")" "$(basename "$2").tar.gz"
}

# ln -sf /home/user/myapp/target_dir /home/user/myapp/my_link
# 思路把需要的库拷贝到 qt lib底下  然后建立软连接
# /home/env/Qt5.14.2/5.14.2/gcc_64
# bin  doc  include  lib  mkspecs  phrasebooks  plugins  qml  translations
deploy_qt() {
  local executable="$1"
  get_no_repeat_dir "$2"
  local install_root=${_ret_}
  local extern_qt_dir="$3"
  local qt_plugin_dir="$4"
  echo "deploy_qt "
  echo "executable $executable"
  echo "install_root $install_root"
  echo "extern_qt_dir $extern_qt_dir"
  echo "qt_plugin_dir $qt_plugin_dir"
  mkdir -p "$install_root"
  chmod 777 "$install_root"
  local extern_exe_path=$(realpath "${executable}")
  local extern_exe_dir=$(dirname "${extern_exe_path}")
  local extern_qt_platform_dir="${qt_plugin_dir}/platforms"
  local exe_path="${install_root}${extern_exe_path}"
  local exe_dir="${install_root}${extern_exe_dir}"
  local qt_dir="${install_root}${extern_qt_dir}"
  local qt_platform_dir="${install_root}${extern_qt_platform_dir}"

  copy_dependencies "${executable}:${extern_qt_platform_dir}" "${install_root}"
  ln -sf ".$extern_exe_path" "$install_root/$(basename "$extern_exe_path")"
  get_relative_path "${extern_exe_dir}" "${extern_qt_platform_dir}"
  ln -sf "${_ret_}" "${exe_dir}/platforms"



  local linker_path=$(which ldd)

  if [ -z "$linker_path" ]; then
    echo "链接器路径未找到。"
    exit 1
  else
    echo "链接器路径 ${linker_path}"
  fi

  # 使用 cp 拷贝链接器到目标目录
  cp_with_symlink "$linker_path" "${install_root}"


  ln -sf ".$linker_path" "$install_root/$(basename "$linker_path")"
  echo "链接器已拷贝到 ${install_root} 1111 ${_ret_}"


  compress_directory_tar_gz "$install_root" "$(dirname "$2")" "$(basename "$2").tar.gz"
}

# 此函数获取系统链接器配置文件里写的路径
get_etc_ld_so_config_paths() {
  local ld_paths=()
  while read -r line; do
    # 排除注释行
    if [[ -n "$line" && "$line" != \#* && -d "$line" ]]; then
      ld_paths+=("$line")
    fi
  done < <(grep -v '^#' /etc/ld.so.conf)
  local conf_file
  for conf_file in /etc/ld.so.conf.d/*; do
    while read -r line; do
      if [[ -n "$line" && "$line" != \#* && -d "$line" ]]; then
        ld_paths+=("$line")
      fi
    done < <(grep -v '^#' "$conf_file")
  done
  local combined_paths=""
  for path in "${ld_paths[@]}"; do
    combined_paths+="${path}:"
  done
  combined_paths="${combined_paths%:}"

  _ret_="$combined_paths"
  echo "系统配置文件找库路径：$combined_paths" >>"${_cur_log_path_}"
}

set_all_runpath() {
  local dir="$1" # 根目录
  get_etc_ld_so_config_paths
  list_append_element "$_ret_" "/lib64:/usr/lib64"
  if [ -n "$LD_LIBRARY_PATH" ]; then
    list_prepend_element "$_ret_" "$LD_LIBRARY_PATH"
  fi
  local base_path_list="$_ret_"
  printf "set_all_runpath base_path_list==  \n%s\n" "$base_path_list" | tr ':' '\n'
  _set_all_runpath() {
    local dir="$1"    # 根目录
    local prefix="$2" # 当前深度
    prefix=${prefix:-"\$ORIGIN"}
    list_prepend_all_element "$base_path_list" "$prefix"
    local item
    for item in "$dir"/*; do
      if [ -d "$item" ]; then
        _set_all_runpath "$item" "${prefix}/.."
      elif [[ "$item" == *.so* ]]; then
        $(echo "")
        #patchelf --set-rpath "$_ret_" "$item"
      fi
    done
  }
  _set_all_runpath "$dir"
}

return 0