#
# Cellular configuration for Komashi.
#
# vim: et:ts=2:sw=2:
#
{
  pkgs,
  deprekages,
  config,
  ...
}:
{
  config = {

    #
    # Let GPSD capture GPS over our modem.
    #
    services.gpsd = {
      enable = true;
      devices = [ "/dev/wwan0at0" ];
    };

    #
    # Set up ModemManager to FCC unlock this device.
    #
    environment.etc."ModemManager/fcc-unlock.d/8086:7560" = {
      text = ''
        #!${pkgs.bash}/bin/bash

        # SPDX-License-Identifier: CC0-1.0
        # 2024 Stoica Floris <floris@nusunt.eu>
        #
        # Lenovo-shipped XMM7560 (8086:7560) FCC unlock

        # Ensure we can touch coreutils.
        PATH=$PATH:${pkgs.coreutils}/bin

        if [[ "$FCC_UNLOCK_DEBUG_LOG" == '1' ]]; then
          exec 3>&1 4>&2
          trap 'exec 2>&4 1>&3' 0 1 2 3
          exec 1>>/var/log/mm-xmm7560-fcc.log 2>&1
        fi

        # require program name and at least 2 arguments
        [ $# -lt 2 ] && exit 1

        # first argument is DBus path, not needed here
        shift

        # second and next arguments are control port names
        for PORT in "$@"; do
          # match port type in Linux 5.14 and newer
          ${pkgs.gnugrep}/bin/grep -q AT "/sys/class/wwan/$PORT/type" 2>/dev/null && {
            AT_PORT=$PORT
            break
          }
          # match port name in Linux 5.13
          echo "$PORT" | ${pkgs.gnugrep}/bin/grep -q AT && {
            AT_PORT=$PORT
            break
          }
        done

        # fail if no AT port exposed
        [ -n "$AT_PORT" ] || exit 2

        DEVICE=/dev/''${AT_PORT}

        at_command() {
          exec 99<>"$DEVICE"
          echo -e "$1\r" >&99
          read answer <&99
          read answer <&99
          echo "$answer"
          exec 99>&-
        }

        log() {
          echo "$1"
        }

        error() {
          echo "$1" >&2
        }

        reverseWithLittleEndian() {
          num="$1"
          printf "%x" $(("0x''${num:6:2}''${num:4:2}''${num:2:2}''${num:0:2}"))
        }

        VENDOR_ID_HASH="3df8c719"

        for i in {1..9}; do
          sleep 2
          log "Requesting challenge from WWAN modem (attempt #''${i})"
          RAW_CHALLENGE=$(at_command "at+gtfcclockgen")
          CHALLENGE=$(echo "$RAW_CHALLENGE" | ${pkgs.gnugrep}/bin/grep -o '0x[0-9a-fA-F]\+' | ${pkgs.gawk}/bin/awk '{print $1}')

          if [ -n "$CHALLENGE" ]; then
            log "Got challenge from modem: $CHALLENGE"
            HEX_CHALLENGE=$(printf "%08x" "$CHALLENGE")
            REVERSE_HEX_CHALLENGE=$(reverseWithLittleEndian "''${HEX_CHALLENGE}")
            COMBINED_CHALLENGE="''${REVERSE_HEX_CHALLENGE}''${VENDOR_ID_HASH}"
            RESPONSE_HASH=$(printf "%s" "$COMBINED_CHALLENGE" | ${pkgs.xxd}/bin/xxd -r -p | sha256sum | cut -d ' ' -f 1)
            TRUNCATED_RESPONSE=$(printf "%.8s" "''${RESPONSE_HASH}")
            REVERSED_RESPONSE=$(reverseWithLittleEndian "$TRUNCATED_RESPONSE")
            RESPONSE=$(printf "%d" "0x''${REVERSED_RESPONSE}")

            log "Sending hash modem: $RESPONSE"
            UNLOCK_RESPONSE=$(at_command "at+gtfcclockver=$RESPONSE")
            log "at+gtfcclockver response = $UNLOCK_RESPONSE"
            UNLOCK_RESPONSE=$(at_command "at+gtfcclockmodeunlock")
            log "at+gtfcclockmodeunlock response = $UNLOCK_RESPONSE"
            UNLOCK_RESPONSE=$(at_command "at+cfun=1")
            log "at+cfun response = $UNLOCK_RESPONSE"
            UNLOCK_RESPONSE=$(at_command "at+gtfcclockstate")
            log "at+gtfcclockstate response = $UNLOCK_RESPONSE"
            UNLOCK_RESPONSE=$(echo "$UNLOCK_RESPONSE" | tr -d '\r')

            at_command "at+xdns=0,1"
            sleep 1

            if [ "$UNLOCK_RESPONSE" = "1" ]; then
              log "FCC was unlocked previously"
              exit 0
            fi

            if [ "$UNLOCK_RESPONSE" = "OK" ]; then
              log "FCC unlock success"
              exit 0
            else
              error "Unlock failed. Got response: $UNLOCK_RESPONSE"
            fi
          else
            error "Failed to obtain FCC challenge. Got: ''${RAW_CHALLENGE}"
          fi

          sleep 0.5
        done

        exit 2
      '';

      # Let anyone execute this.
      mode = "555";
    };

    # Work properly with KPN's 4-over-6 IP-wonk.
    services.clatd = {
      enable = true;
      settings = {
        ip6tables-enable = true;
        v4-conncheck-delay = 1;
      };
    };
    networking.networkmanager.dispatcherScripts = [{
      source = pkgs.writeShellScript "magic" ''
        if [[ "$DEVICE_IFACE" == wwan* ]]; then
          if [[ "$NM_DISPATCHER_ACTION" == "up" ]]; then
            printf '[Service]\nExecStart=\nExecStart=${config.systemd.services.clatd.serviceConfig.ExecStart} dns64-servers=%s' "$(printf "%s" "$IP6_NAMESERVERS" | ${pkgs.coreutils}/bin/tr ' ' ',')" | ${pkgs.systemd}/bin/systemctl edit --stdin clatd.service --runtime
            ${pkgs.systemd}/bin/systemctl restart clatd
          fi
          if [[ "$NM_DISPATCHER_ACTION" == "down" ]]; then
            ${pkgs.systemd}/bin/systemctl restart clatd
          fi
        fi
      '';
    }];
  };

}
