#!/usr/bin/env bash

# shellcheck shell=bash

# ``````````````````````````````````````````````````````````````````````````````
# Function name: InitTrace()
#
# Description:
#   Trace domain.
#
# Usage:
#   InitTrace domain
#
# Examples:
#   InitTrace example.com
#

function InitTrace() {

  # shellcheck disable=SC2034
  local _FUNCTION_ID="InitTrace"
  local _STATE=0

  local _host="$1"
  local _via_ip=""
  local _via_ip_tmp=""
  local _via_ip_status="0"

  local req_header_type_all_p=()

  # shellcheck disable=SC2001,SC2034,SC2154
  # Add '\' to before a special chars pattern.
  _host_p=$(sed 's/&/\\&/g' <<<"$_host")
  # shellcheck disable=SC2001,SC2034,SC2154
  _host_p=$(sed 's/?/\\?/g' <<<"$_host_p")

  # shellcheck disable=SC2154
  if [[ "$dump_headers_state" -eq 1 ]] ; then

    if [[ "$cache_bypass_state" -eq 0 ]] ; then

      _host_arg="$_host_p"

    else

      _host_arg="${_host_p}${cache_bypass_type}"

    fi

  else

    if [[ "$cache_bypass_state" -eq 0 ]] ; then

      _host_arg="$_host_p"

    else

      _host_arg="${_host_p}${cache_bypass_type}"

    fi

  fi

  # shellcheck disable=SC2154
  if [[ -z "${req_header_type_all[*]}" ]] ; then

    _ccmd=("${_curl_base[@]} $_host_arg")

  else

    for _head in "${req_header_type_all[@]}" ; do

      req_header_type_all_p+=("$_head")

    done

    _ccmd=("${_curl_base[@]} ${req_header_type_all_p[@]} $_host_arg")

  fi

  # shellcheck disable=SC2034,SC2154
  if [[ "$debug_conn_state" -eq 1 ]] ; then

    _ccmd+=("-v")

  fi

  # shellcheck disable=SC2034,SC2154
  _http_output=$(eval "${_ccmd[@]}" -ks -m "$_timeout" \
    -H "'User-Agent: $_user_agent'" \
    -w '%{time_total}:%{time_connect}:%{time_namelookup}:%{time_appconnect}:%{time_pretransfer}:%{time_starttransfer}:%{size_request}:%{size_download}:%{size_header}:%{local_ip}:%{local_port}:%{remote_ip}:%{remote_port}:%{http_version}:%{http_code}:%{scheme}:%{redirect_url}' \
    -D "${_tmp}/headers.dump" -o "${_tmp}/body.dump" --compressed 2>"${_tmp}/stderr.dump")

  if [[ "$debug_conn_state" -eq 1 ]] ; then

    # shellcheck disable=SC2034,SC2154
    _curl_conn_debug=$(echo "${_ccmd[@]}" -ks -m "$_timeout" \
      -H "'User-Agent: $_user_agent'" \
      -w '%{time_total}:%{time_connect}:%{time_namelookup}:%{time_appconnect}:%{time_pretransfer}:%{time_starttransfer}:%{size_request}:%{size_download}:%{size_header}:%{local_ip}:%{local_port}:%{remote_ip}:%{remote_port}:%{http_version}:%{http_code}:%{scheme}:%{redirect_url}' \
      -D "${_tmp}/headers.dump" -o "${_tmp}/body.dump" --compressed | sed 's/  */ /g')

  fi

  # _http_output=$(eval "${_ccmd[@]}" -ks -m "$_timeout" \
    # -H "'Connection: keep-alive'" \
    # -H "'Upgrade-Insecure-Requests: 1'" \
    # -H "'User-Agent: $_user_agent'" \
    # -H "'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'" \
    # -H "'Accept-Encoding: gzip, deflate, br'" \
    # -H "'Accept-Language: en-US,en;q=0.9,en-US;q=0.8,en;q=0.7'" \
    # -w '%{time_total}:%{time_connect}:%{local_ip}:%{local_port}:%{remote_ip}:%{remote_port}:%{http_version}:%{http_code}:%{scheme}:%{redirect_url}' \
    # -D "${_tmp}/headers.dump" -o "${_tmp}/body.dump" --compressed)

  if [[ -z "$_http_output" ]] ; then

    # shellcheck disable=SC2034
    _STATE="1"

    _logger "stop" \
      "${_FUNCTION_ID}()" \
      "get redirect error"

  else

    # lookup:
    #   The time, in seconds, it took from the start until the name resolving was completed.
    # connect:
    #   The time, in seconds, it took from the start until the TCP connect to the remote host (or proxy) was completed.
    # appconnect:
    #   The time, in seconds, it took from the start until the SSL/SSH/etc connect/handshake to the remote host was completed.
    # pretransfer:
    #   The time, in seconds, it took from the start until the file transfer was just about to begin.
    #   This includes all pre-transfer commands and negotiations that are specific to the particular protocol(s) involved.
    # starttransfer:
    #   The time, in seconds, it took from the start until the first byte was just about to be transferred.
    #   This includes time_pretransfer and also the time the server needed to calculate the result.
    # total:
    #   The total time, in seconds, that the full operation lasted. The time will be displayed with millisecond resolution.

    _time_total=$(echo "$_http_output" | cut -d ":" -f1)
    _time_connect=$(echo "$_http_output" | cut -d ":" -f2)
    _time_namelookup=$(echo "$_http_output" | cut -d ":" -f3)
    _time_appconnect=$(echo "$_http_output" | cut -d ":" -f4)
    _time_pretransfer=$(echo "$_http_output" | cut -d ":" -f5)
    _time_starttransfer=$(echo "$_http_output" | cut -d ":" -f6)
    _size_request=$(echo "$_http_output" | cut -d ":" -f7)
    _size_download=$(echo "$_http_output" | cut -d ":" -f8)
    _size_header=$(echo "$_http_output" | cut -d ":" -f9)
    _local_ip=$(echo "$_http_output" | cut -d ":" -f10)
    _local_port=$(echo "$_http_output" | cut -d ":" -f11)
    _remote_ip=$(echo "$_http_output" | cut -d ":" -f12)
    _remote_port=$(echo "$_http_output" | cut -d ":" -f13)
    _http_version=$(echo "$_http_output" | cut -d ":" -f14)
    _http_code=$(echo "$_http_output" | cut -d ":" -f15)
    _http_scheme=$(echo "$_http_output" | cut -d ":" -f16 | tr '[:upper:]' '[:lower:]')
    _http_redirect=$(echo "$_http_output" | cut -d ":" -f17-)

    if [[ -z "$_http_code" ]] || \
       [[ "$_http_code" == "000" ]] ; then

      _http_code=""

    fi

    if [[ -z "$_http_version" ]] || \
       [[ "$_http_version" == "0" ]] ; then

      _http_version=""

    fi

    # shellcheck disable=SC2154
    if [[ "$proxy_state" -eq 1 ]] ; then

      # Set local and remote sockets.
      _local_socket="${_local_ip}:${_local_port}"

      # shellcheck disable=SC2154
      DomainResolve "$_domain_name"

      # shellcheck disable=SC2154
      _remote_socket="${_host_ip}:???"
      _remote_socket_proxy="${_remote_ip}:${_remote_port}"

    else

      # Set local and remote sockets.
      _local_socket="${_local_ip}:${_local_port}"
      _remote_socket="${_remote_ip}:${_remote_port}"

    fi

    # Check if _remote_socket is private address - generate pattern.
    _class_pattern=('10.')        # 10.0.0.0/8
    _class_pattern+=('192.168.')  # 192.168.0.0/16
    _class_pattern+=('169.254.')  # not strictly private range, but link local

    for _ip in $(seq 16 31) ; do  # 172.16.0.0/12

      _class_pattern+=("172.${_ip}.")

    done

    # Get external IP (set https connection).
    # shellcheck disable=SC2154,SC2155
    if [[ "$resolve_state" -eq 1 ]] ; then

      if [[ "$resolve_type_ip" = "127."* ]] || \
         [[ "$resolve_type_ip" == "localhost" ]] ; then

        _via_ip_status="1"

      else

        _via_ip_status="0"

      fi

    fi

    if [[ "$_domain_name" = "127."* ]] || \
       [[ "$_domain_name" == "localhost" ]] ; then

      _via_ip_status="1"

    else

      _via_ip_status="0"

    fi

    if [[ "$_via_ip_status" -eq 0 ]] ; then

      # shellcheck disable=SC2154
      _via_ip_tmp=$($_curl_base_remote -ks -m "$_timeout" http://whatismyip.akamai.com/)

      if [[ ! "$_via_ip_tmp" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] ; then

        _via_ip="unknown"
        _via_ip_status="1"

      else

        for _ip in "${_class_pattern[@]}" ; do

          if [[ "$_remote_socket" = "$_ip"* ]] ; then

            _via_ip="<local_net>"
            _via_ip_status="1"

          fi

        done

      fi

    else

      _via_ip="<int_link>"
      _via_ip_status="1"

    fi

    if [[ "$_via_ip_status" -eq "0" ]] ; then

      _via_ip="$_via_ip_tmp"

    fi

    if [[ "$proxy_state" -eq 1 ]] ; then

      if [[ "$_via_ip_status" -eq "0" ]] ; then

        DomainGeo "$_host_ip"

      fi

    else

      if [[ "$_via_ip_status" -eq "0" ]] ; then

        DomainGeo "$_remote_ip"

      fi

    fi

    # Convert times.
    _time_namelookup=$(echo "$_time_namelookup" | tr ',' '.')
    _time_connect=$(echo "$_time_connect" | tr ',' '.')
    _time_appconnect=$(echo "$_time_appconnect" | tr ',' '.')
    _time_pretransfer=$(echo "$_time_pretransfer" | tr ',' '.')
    _time_starttransfer=$(echo "$_time_starttransfer" | tr ',' '.')
    _time_total=$(echo "$_time_total" | tr ',' '.')

    _dns_t="$_time_namelookup"
    _tcp_t=$(echo "$_time_connect" - "$_time_namelookup" | bc | awk '{printf "%f", $0}')
    _tls_t=$(echo "$_time_appconnect" - "$_time_connect" | bc | awk '{printf "%f", $0}')
    _http_t=$(echo "$_time_pretransfer" - "$_time_appconnect" | bc | awk '{printf "%f", $0}')

    _http_t_msg="TLS"
    if [[ "$_time_appconnect" == "0.000000" ]] ; then
      _http_t_msg="TCP"
      _tls_t="0.000000"
      _time_appconnect="$_time_connect"
      _http_t=$(echo "$_time_pretransfer" - "$_time_appconnect" | bc | awk '{printf "%f", $0}')
    fi

    _data_g=$(echo "$_time_starttransfer" - "$_time_pretransfer" | bc | awk '{printf "%f", $0}')
    _data_t=$(echo "$_time_total" - "$_time_starttransfer" | bc | awk '{printf "%f", $0}')

    # shellcheck disable=SC2034
    _ttfb="$_data_g"

    # Set properly total time.
    # shellcheck disable=SC2154
    _full_conn_time="$(bc <<<"$_full_conn_time + $_time_total" | awk '{printf "%f", $0}')"

    if [[ -z "$_remote_ip" ]] ; then

      # shellcheck disable=SC2086,SC2154
      printf '\n    \e['${trgb_3111}'m%s\e[m:\n\n\t%s\n\t%s\n\n' \
             "Connection error" \
             "- domain not resolved" \
             "- http or https not responding"

      # shellcheck disable=SC2034
      _STATE="1"

      _logger "stop" \
        "${_FUNCTION_ID}()" \
        "Connection error (domain not resolved?)"

    fi

    # Convert _http_version value.
    if [[ "$_http_version" == "2" ]] ; then _http_version="2.0" ; fi

    # shellcheck disable=SC2034
    _STATE="0"

    # shellcheck disable=SC2154
    if [[ "$dump_headers_state" -eq 1 ]] ; then

      PrintHeaders

    else

      if [[ "$_hcounter" -eq 1 ]] ; then

        PrintHeaders

        _hcounter=0

      fi

    fi

    # shellcheck disable=SC2154
    if [[ "$_hide_src_ip_state" -eq 1 ]] ; then

      # shellcheck disable=SC2034
      _local_socket_port=$(echo "$_local_socket" | cut -d ":" -f2)

      _local_socket_std="xxx.xxx.xxx.xxx:${_local_socket_port}"
      _via_ip_std="xxx.xxx.xxx.xxx"

    else

      _local_socket_std="$_local_socket"
      _via_ip_std="$_via_ip"

    fi

    # shellcheck disable=SC2086,SC2154
    printf ' \e['${trgb_3111}'m•\e[m \e['${trgb_3811}'m %-4.6s %-12.10s %-15.12s %-22.22s %-16.22s %-21.22s %-5.7s %-7.7s %-5.5s %-8.10s %s\e[m\n' \
           " $_req_num" \
           "$_full_conn_time" \
           "$_time_total" \
           "$_local_socket_std" \
           "$_via_ip_std" \
           "$_remote_socket" \
           "$_geo_ip" \
           "$_http_scheme" \
           "$_http_version" \
           "$_http_code" \
           "$_http_redirect"

    if [[ "$CURL_TIMERS" == "on" ]] ; then

      printf '
             Request Size         %s bytes
             Response Size        %s bytes
             Headers Size         %s bytes
         ————————————————————————————————————
         [0ms]
            › DNS Query
            ‹ DNS Response
              DNS Lookup          %sms
         [%sms]
            › TCP SYN
            › TCP ACK
              TCP Handshake (RTT) %sms
         [%sms]
            › TLS ClientHello
            ‹ TLS Finished
              TLS Handshake       %sms
         [%sms]
              %s › HTTP          %sms
         [%sms]
            › HTTP Request
            ‹ HTTP Response
              Waiting (TTFB)      %sms
         [%sms]
              Data Transfer       %sms
         ————————————————————————————————————
              Time Total          %sms

' \
      "$_size_request" \
      "$_size_download" \
      "$_size_header" \
      "$(echo "$_dns_t * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_time_namelookup * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_tcp_t * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_time_connect * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_tls_t * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_time_appconnect * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$_http_t_msg" \
      "$(echo "$_http_t * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_time_pretransfer * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_ttfb * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_time_starttransfer * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_data_t * 1000" | bc | awk '{printf "%.2f", $0}')" \
      "$(echo "$_time_total * 1000" | bc | awk '{printf "%.2f", $0}')"

    fi

    if [[ "$proxy_state" -eq 1 ]] ; then

      # shellcheck disable=SC2086,SC2154
      printf '  %-4.6s %-12.10s %-15.12s %-22.22s └\e['${trgb_3812}'m%-16.22s\e[m\n' \
             "       " \
             "       " \
             "       " \
             "       " \
             "$_remote_socket_proxy"

    fi

    if [[ "$_http_scheme" == "https" ]] ; then

      DomainStrip "$_host"

      DomainSslCheck "$_domain_name"

      # shellcheck disable=SC2154
      if [[ "$ssl_state" -eq 1 ]] ; then

        DomainCertCheck "$_domain_name"

        DomainChainCheck "$_domain_name"

        # shellcheck disable=SC2154
        if [[ "$_ssl_params_verify" == "OK" ]] || \
           [[ "$_ssl_params_verify" == *"0 (ok)"* ]] ; then

          _ssl_params_verify="ok"

          # shellcheck disable=SC2086,SC2154
          printf '         verification: \e['${trgb_3211}'m%s\e[m\n' "$_ssl_params_verify"

        elif [[ "$_ssl_params_verify" == "self signed certificate" ]] ; then

          # shellcheck disable=SC2086,SC2154
          printf '         verification: \e['${trgb_3300}'m%s\e[m\n' "$_ssl_params_verify"

        else

          # shellcheck disable=SC2086,SC2154
          printf '         verification: \e['${trgb_3100}'m%s\e[m\n' "$_ssl_params_verify"

        fi

      fi

      # shellcheck disable=SC2034
      _last_http_scheme="$_http_scheme"

    elif [[ "$_http_scheme" == "http" ]] && [[ "$ssl_state" -eq 1 ]] ; then

      # shellcheck disable=SC2086,SC2154
      printf '         ssl: \e['${trgb_3111}'m%s\e[m\n' "off"

    elif [[ "$_http_scheme" == "http" ]] && [[ "$_http_scheme" == "https" ]] ; then

      # shellcheck disable=SC2086,SC2154
      printf '         ssl: \e['${trgb_3111}'m%s\e[m\n' "off"

    fi

    # shellcheck disable=SC2154
    if [[ "$debug_conn_state" -eq 1 ]] ; then

      echo

      echo "$_curl_conn_debug" | sed -e 's/^[ \t]*/         › /' | \
      sed 's/\r//' | grep "› ." | fmt -w "$MAX_WIDTH" -s

      if [[ -e "${_tmp}/stderr.dump" ]] ; then

        if [[ -s "${_tmp}/stderr.dump" ]] ; then

          echo

        fi

        < "${_tmp}/stderr.dump" sed -e 's/^[ \t]*/         › /' | \
        sed 's/\r//' | grep "› ." | fmt -w "$MAX_WIDTH" -s

      fi

    fi

    # shellcheck disable=SC2154
    if [[ "$debug_ssl_state" -eq 1 ]] && [[ "$_http_scheme" == "https" ]] ; then

      echo

      echo "$_ssl_connect_debug" | sed -e 's/^[ \t]*/         › /' | \
      sed 's/\r//' | grep "› ." | fmt -w "$MAX_WIDTH" -s

    fi

    # shellcheck disable=SC2154
    if [[ "$dump_headers_state" -eq 1 ]] ; then

      if [[ -e "${_tmp}/headers.dump" ]] ; then

        if [[ -s "${_tmp}/headers.dump" ]] ; then

          echo

        fi

        < "${_tmp}/headers.dump" sed -e 's/^[ \t]*/         › /' | \
        sed 's/\r//' | grep "› ." | fmt -w "$MAX_WIDTH" -s

      fi

    fi

    # shellcheck disable=SC2154
    if [[ "$dump_body_state" -eq 1 ]] ; then

      if [[ -e "${_tmp}/body.dump" ]] ; then

        if [[ -s "${_tmp}/body.dump" ]] ; then

          echo

        fi

        < "${_tmp}/body.dump" sed -e 's/^[ \t]*/         › /' | \
        sed 's/\r//' | grep "› ." | fmt -w "$MAX_WIDTH" -s

      fi

    fi

    _logger "info" \
      "${_FUNCTION_ID}()" \
      "get redirect: $_http_output"

  fi

  return $_STATE

}
