#!/usr/bin/env bash

OLDIFS=$IFS
IFS=$'\n'

path_src=""
path_dst=""

#функция перевода
i18n() {
    local key="$1"; shift
    printf "$(gettext -s "$key")" "$@"
}

#инициализация
_init_(){
    #Общие переменные для порядковы номеров в текством файле
    iter_str=1
    iter_dst=1

    path_root=""

    export TEXTDOMAIN=${0##*/}
    export TEXTDOMAINDIR="${path_root}/usr/share/locale"

    #создание папки tmp, для хранения информации вычесленного хэша
    path_ubhash_tmp="/tmp/ubhash"
    [[ ! -d "${path_ubhash_tmp}" ]] && install -m 777 -d ${path_ubhash_tmp}

    src_files="${path_ubhash_tmp}/ubhash_src.txt"
    dst_files="${path_ubhash_tmp}/ubhash_dst.txt"
    
    ([[ -f "${src_files}" ]] || [[ -f "${src_files}" ]]) && (rm -r "${src_files}" && rm -r "${dst_files}") && (touch "${src_files}" && touch "${dst_files}") || (touch "${src_files}" && touch "${dst_files}")
    chmod 666 "${src_files}" && chmod 666 "${dst_files}"

    declare -a COMPARE_HASHES #массив, где хранится данные о сравнении файлов
    declare -a PATH_NAME_ARRAY_SRC #массив с именами файлов из основной директории
    declare -a PATH_NAME_ARRAY_DST #массив с именами файлов из сравниваемой директории

    LC_ALL="ru_RU.UTF-8"
    LANG="ru_RU.UTF-8"
    LANGUAGE="en_US:en:ru_RU:ru"
    LC_PAPER="en_US.UTF-8"
    LESSCHARSET="utf-8"
    MM_CHARSET="utf-8"
}

msg_help="UBHash console application for compare hash

    Usage:    %s [-h][-H hashing algorithm] (file or dir 1) (file or dir 2) [-a][-n][-d]

    -h | --help
        Show a help message
    -a | --all
        Work with subdirectory
    -n | --no-comp
        Do not compare files and hashes
    -d | --hidden_files
        Also compare hidden files
    -H
        Choose hashing algorithm
            sha256, sha512, md4, md5
            gost-12-256, gost-12-512"


#вывод файла с вычесленным хэшем в stdout
view_file_info(){
    echo -e "\n###$(i18n "Information about") ${2}###"
    cat "${1}" | column -t -s '|'
}

#функция получения имен файлов из директории
#принимает путь основной директори
find_all_files_dir_src(){
    if [ -z "${arg_hddn_fls}" ]; then
        while read -r line; do
            PATH_NAME_ARRAY_SRC+=("$line")
        done < <(find "$1" -depth -type f -not -path '*/\.*' | sort )
    else
        while read -r line; do
            PATH_NAME_ARRAY_SRC+=("$line")
        done < <(find "$1" -depth -type f | sort )
    fi
}

#функция получения имен файлов из директории
#принимает путь сравниваемой директори
find_all_files_dir_dst(){
    if [ -z "${arg_hddn_fls}" ]; then
        while read -r line; do
            PATH_NAME_ARRAY_DST+=("$line")
        done < <(find "$1" -depth -type f -not -path '*/\.*' | sort )
    else
        while read -r line; do
            PATH_NAME_ARRAY_DST+=("$line")
        done < <(find "$1" -depth -type f | sort )
    fi
}

#нахождение хэша файла
#принимает путь до файла из основной директории
find_files_src_hash(){
    src_file="$1"
    local alg_hash="$2"
    src_hash="$(openssl dgst -engine gost -"${alg_hash}" "${src_file}" 2>/dev/null | sed 's/[^=]*\= //' )" #основная функция - вычисление хэша по gost12
    write_file_info "${iter_str}" $(echo "${src_file}" | sed 's/ /_/g') "${src_hash}" "${src_files}"
    ((iter_str++))
}

#нахождение хэша файла
#принимает путь до файла из сравниваемой директории
find_files_dst_hash(){
    dst_file="$1"
    local alg_hash="$2"
    dst_hash="$(openssl dgst -engine gost -"${alg_hash}" "${dst_file}" 2>/dev/null | sed 's/[^=]*\= //' )"
    write_file_info "${iter_dst}" $(echo "${dst_file}" | sed 's/ /_/g') "${dst_hash}" "${dst_files}"
    ((iter_dst++))
}

#запись данных, о найденном хэше в файл 
#принимает порядковый номер, путь до файла, хэш и файл для записи
write_file_info(){
    local file_info="$1 | $2 | $3"
    local path="$4"
    echo "${file_info}" >> ${path} 
}

#функция сравнения хэша
compare_file_hashes(){
    local count_str_src=$(echo $(wc ${src_files}) | awk '{print $1}') #кол-во строк в основном файле
    local count_str_dst=$(echo $(wc ${dst_files}) | awk '{print $1}') #кол-во строк в сравниваемом файле
    local check_compare=1 #переменная для отображения номера сравнимой записи, а также для определения, если похожих файлов нет

    #рекурсивный метод сравнения двух файлов по их имени, а затем их хэшей
    #если имена совпадают, то сравнивает их хэш, а затем выводит результат
    for (( i=0; i < ${count_str_src}; i++ ))
    do
        local file_name_src=$(awk -v iter_name_src=$i '{if ($1 == iter_name_src+1) print $3}' "${src_files}") #имя файла по порядковому номеру из основного файла
        local hash_src=$(awk -v iter_hash_src=$i '{if ($1 == iter_hash_src+1) print $5}' "${src_files}") #хэш по порядковому номеру из основного файла
        for (( j=0; j < ${count_str_dst}; j++ ))
        do
            local file_name_dst=$(awk -v iter_name_dst=$j '{if ($1 == iter_name_dst+1) print $3}' "${dst_files}") #имя файла по порядковому номеру из сравниваемого файла
            local hash_dst=$(awk -v iter_hash_dst=$j '{if ($1 == iter_hash_dst+1) print $5}' "${dst_files}") #хэш по порядковому номеру из сравниваемого файла
            if [ "${hash_src}" == "${hash_dst}" ]; then
                echo "${check_compare} $(dirname "${file_name_src}")/${file_name_src##*/} ${hash_src} = $(dirname "${file_name_dst}")/${file_name_dst##*/} ${hash_dst}"
                ((check_compare++))
            # else
            #     echo -e "${check_compare} $(dirname "${file_name_src}")/${file_name_src##*/} ${hash_src} ≠ $(dirname "${file_name_dst}")/${file_name_dst##*/} ${hash_dst}"
            #     ((check_compare++))
            fi
        done
    done

    [[ "${check_compare}" == "1" ]] && echo "255" #если никаких записей не произашло (нет одинаковых имен), записать соотв. строку
}

#считывание ключей
[[ -z "$1" ]] && set - "--help"
while [[ -n "$1" ]]; do
    case "$1" in
        "-gf" | "--get_src_file")
            arg_get_src_file="-gf"
        ;;
        "-t" | "--term_ui")
            arg_term_ui="-t"
        ;;
        "-h" | "--help")
            arg_help="-h"
        ;;
        "-H")
            arg_hash="-H"
        ;;
        "-a" | "--all")
            arg_all_dir="-a"
        ;;
        "-n" | "--no_comp")
            arg_no_comp="-n"
        ;;
        "-d" | "--hidden_files")
            arg_hddn_fls="-d"
        ;;
        *)
            if [ -z "${path_src}" ]; then
                [[ -f "$1" ]] && path_src="$1"
                [[ -d "$1" ]] && path_src="$1"

            else
                [[ -f "$1" ]] && path_dst="$1"
                [[ -d "$1" ]] && path_dst="$1"
            fi
            if [[ -n ${arg_hash} ]]; then
                case "$1" in
                    "sha256")
                        arg_alg_hash="sha256"
                    ;;
                    "sha512")
                        arg_alg_hash="sha512"
                    ;;
                    "md4")
                        arg_alg_hash="md4"
                    ;;
                    "md5")
                        arg_alg_hash="md5"
                    ;;
                    "gost94")
                        arg_alg_hash="md_gost94"
                    ;;
                    "gost-12-256")
                        arg_alg_hash="md_gost12_256"
                    ;;
                    "gost-12-512")
                        arg_alg_hash="md_gost12_512"
                    ;;
                esac
            else
                arg_alg_hash="md_gost12_512"
            fi
        ;;
    esac
	shift
done

#главная функция начала вычисления и сравнения хэша
main(){
    local path_src_file="$1"
    local path_dst_file="$2"
    #проверка, если пути являются файлами, то работать как с файлами
    if [ -f "${path_src_file}" ] && [ -f "${path_dst_file}" ]; then
        find_files_src_hash "${path_src_file}" ${arg_alg_hash}
        find_files_dst_hash "${path_dst_file}" ${arg_alg_hash}
        [[ -n "${arg_no_comp}" ]] && return
        compare_file_hashes
    #проверка, если пути являются директориями с заходом в подпапки
    elif [ -d "${path_src_file}" ] && [ -d "${path_dst_file}" ] && [ -z "${arg_all_dir}" ]; then
        if [ -z "${arg_hddn_fls}" ]; then
            #Цикл нахождения всех файлов в папке и вычисления их хэша
            for entry_src in "${path_src_file}"/*; do
                [[ -f "$entry_src" ]] && find_files_src_hash "$entry_src" ${arg_alg_hash}
            done
            for entry_dst in "${path_dst_file}"/*; do
                [[ -f "$entry_dst" ]] && find_files_dst_hash "$entry_dst" ${arg_alg_hash}
            done
        else
            #Цикл нахождения всех файлов в папке и вычисления их хэша
            while read -r src_path_line; do
                [[ -f "${src_path_line}" ]] && find_files_src_hash "${src_path_line}" ${arg_alg_hash}
            done < <(find "${path_src_file}" -maxdepth 1 -type f)

            while read -r dst_path_line; do
                [[ -f "${dst_path_line}" ]] && find_files_dst_hash "${dst_path_line}" ${arg_alg_hash}
            done < <(find "${path_dst_file}" -maxdepth 1 -type f)
        fi
        [[ -n "${arg_no_comp}" ]] && return
        compare_file_hashes
    #проверка, если пути являются директориями без захода в подпапки
    elif [ -d "${path_src_file}" ] && [ -d "${path_dst_file}" ] && [ -n "${arg_all_dir}" ]; then
        find_all_files_dir_src "${path_src_file}"
        find_all_files_dir_dst "${path_dst_file}"
        for arr in ${PATH_NAME_ARRAY_SRC[@]}; do
            find_files_src_hash "$arr" ${arg_alg_hash}
        done
        for arr in ${PATH_NAME_ARRAY_DST[@]}; do
            find_files_dst_hash "$arr" ${arg_alg_hash}
        done
        [[ -n "${arg_no_comp}" ]] && return
        compare_file_hashes
    fi

}

#функция для вывода в терминал информации
term(){
    local path_src_file=$1
    local path_dst_file=$2
    [[ -n "${arg_help}" ]] && echo "$(i18n "${msg_help}" "$(basename $0)")" && return
    echo "$(i18n "Culculating...")"
    while read -r line; do
    COMPARE_HASHES+=("$line")
    done < <(main "${path_src}" "${path_dst}")
    clear
    sed -i -e "1 s/^/$(i18n "No. | File | Hash")\n/;" "${src_files}" && sed -i -e 1" s/^/$(i18n "No. | File | Hash")\n/;" "${dst_files}"
    view_file_info "${src_files}" "${path_src_file}" && view_file_info "${dst_files}" "${path_dst_file}"
    [[ -n  "${arg_no_comp}" ]] && return
    echo -e "\n######### $(i18n "Compare fles") ##########"
    for i in ${!COMPARE_HASHES[@]}; do
    echo -e "$(i18n "${COMPARE_HASHES[i]}")"
    done
}



exec 3>&1 2>&1
_init_

[[ -n ${arg_get_src_file} ]] && echo "${src_files} ${dst_files}" 1>&3 && exit 0
[[ -n ${arg_term_ui} ]] && main "${path_src}" "${path_dst}" 1>&3 && exit 0
[[ -z ${arg_term_ui} ]] && term "${path_src}" "${path_dst}" && exit 0


IFS=$OLDIFS

exec 3>&-

exit 0
