#!/usr/bin/env bash
###############################################################################
# Configuration
###############################################################################
MODE=$1
DEV=$2

if [ "$DEBUG" == "" ]; then
  DEBUG="false"
fi

if [ "$MBIM_INTERFACE" == "" ]; then
  MBIM_INTERFACE="/dev/cdc-wdm0"
fi

###############################################################################
# Global Variables
###############################################################################
previous_state="none"
state="none"
skip_line=0
ipv4_addresses=()
ipv4_gateway=""
ipv4_dns=()
ipv4_mtu=""
ipv6_addresses=()
ipv6_gateway=""
ipv6_dns=()
ipv6_mtu=""

export previous_state state skip_line \
  ipv4_addresses ipv4_gateway ipv4_dns ipv4_mtu \
  ipv6_addresses ipv6_gateway ipv6_dns ipv6_mtu

###############################################################################
# Function
###############################################################################

function print_debug {
  if [ "$DEBUG" != "false" ]; then
    echo "[State: $state] $1" >&2
  fi
}

function print_full_configuration {
  if [[ ${#ipv4_addresses[@]} > 0 ]]; then
    printf "IPv4: "
    printf '%s, ' "${ipv4_addresses[@]}"
    printf "\n"

    printf "GW: $ipv4_gateway\n"

    printf "DNS: "
    printf '%s, ' "${ipv4_dns[@]}"
    printf "\n"

    printf "MTU: $ipv4_mtu\n"
  fi

  if [[ ${#ipv6_addresses[@]} > 0 ]]; then
    echo
    printf "IPv6: "
    printf '%s, ' "${ipv6_addresses[@]}"
    printf "\n"

    printf "GW: $ipv6_gateway\n"

    printf "DNS: "
    printf '%s, ' "${ipv6_dns[@]}"
    printf "\n"

    printf "MTU: $ipv6_mtu\n"
  fi
}

function next_state {
  previous_state="$state"
  state="$1"
}

function parse_ip {
  #      IP [0]: '10.134.203.177/30'
  local line_re="IP \[([0-9]+)\]: '(.+)'"
  local input=$1
  if [[ $input =~ $line_re ]]; then
    local ip_cnt=${BASH_REMATCH[1]}
    local ip=${BASH_REMATCH[2]}
  fi
  echo "$ip"
}

function parse_dns {
  #      IP [0]: '10.134.203.177/30'
  local line_re="DNS \[([0-9]+)\]: '(.+)'"
  local input=$1
  if [[ $input =~ $line_re ]]; then
    local dns_cnt=${BASH_REMATCH[1]}
    local dns=${BASH_REMATCH[2]}
  fi
  echo "$dns"
}

function parse_gateway {
  #    Gateway: '10.134.203.178'
  local line_re="Gateway: '(.+)'"
  local input=$1
  if [[ $input =~ $line_re ]]; then
    local gw=${BASH_REMATCH[1]}
  fi
  echo "$gw"
}

function parse_mtu {
  #        MTU: '1500'
  local line_re="MTU: '([0-9]+)'"
  local input=$1
  if [[ $input =~ $line_re ]]; then
    local mtu=${BASH_REMATCH[1]}
  fi
  echo "$mtu"
}

function parse_input_state_machine {
  state="start"
  while true; do
    if [[ $skip_line == 0 ]]; then
      read line || break # TODO: Clean up
    else
      skip_line=0
    fi
    case "$state" in
    "start")
      read line || break # first line is empty, read a new one #TODO: This is not very clean...
      case "$line" in
      *"configuration available: 'none'"*)
        # Skip none state
        # TODO: This is a workaround of the original parser's shortcomming
        continue
        ;;
      *"IPv4 configuration available"*)
        next_state "ipv4_ip"
        continue
        ;;
      *"IPv6 configuration available"*)
        next_state "ipv6_ip"
        continue
        ;;
      *)
        next_state "exit"
        continue
        ;;
      esac
      ;;
    "error")
      echo "Error in pattern matchin of state $previous_state. Exiting." >&2
      exit 2
      ;;
    "exit")
      break
      ;;
    "ipv4_ip")
      ipv4=$(parse_ip "$line")
      if [ -z "$ipv4" ]; then
        if [[ ${#ipv4_addresses[@]} < 1 ]]; then
          next_state "error"
          continue
        else
          next_state "ipv4_gateway"
          skip_line=1
          continue
        fi
      fi
      print_debug "$ipv4"
      ipv4_addresses+=("$ipv4")
      ;;
    "ipv4_gateway")
      gw=$(parse_gateway "$line")
      if [ -z "$gw" ]; then
        next_state "error"
        continue
      fi
      print_debug "$gw"
      ipv4_gateway="$gw"
      next_state "ipv4_dns"
      ;;
    "ipv4_dns")
      ipv4=$(parse_dns "$line")
      if [ -z "$ipv4" ]; then
        if [[ ${#ipv4_dns[@]} < 1 ]]; then
          next_state "error"
          continue
        else
          next_state "ipv4_mtu"
          skip_line=1
          continue
        fi
      fi
      print_debug "$ipv4"
      ipv4_dns+=("$ipv4")
      ;;
    "ipv4_mtu")
      mtu=$(parse_mtu "$line")
      if [ -z "$mtu" ]; then
        next_state "error"
        continue
      fi
      print_debug "$mtu"
      ipv4_mtu="$mtu"
      next_state "start"
      ;;
    "ipv6_ip")
      ipv6=$(parse_ip "$line")
      if [ -z "$ipv6" ]; then
        if [[ ${#ipv6_addresses[@]} < 1 ]]; then
          next_state "error"
          continue
        else
          next_state "ipv6_gateway"
          skip_line=1
          continue
        fi
      fi
      print_debug "$ipv6"
      ipv6_addresses+=("$ipv6")
      ;;
    "ipv6_gateway")
      gw=$(parse_gateway "$line")
      if [ -z "$gw" ]; then
        next_state "error"
        continue
      fi
      print_debug "$gw"
      ipv6_gateway="$gw"
      next_state "ipv6_dns"
      ;;
    "ipv6_dns")
      ipv6=$(parse_dns "$line")
      if [ -z "$ipv6" ]; then
        if [[ ${#ipv6_dns[@]} < 1 ]]; then
          next_state "error"
          continue
        else
          next_state "ipv6_mtu"
          skip_line=1
          continue
        fi
      fi
      print_debug "$ipv6"
      ipv6_dns+=("$ipv6")
      ;;
    "ipv6_mtu")
      mtu=$(parse_mtu "$line")
      if [ -z "$mtu" ]; then
        next_state "error"
        continue
      fi
      print_debug "$mtu"
      ipv6_mtu="$mtu"
      next_state "start"
      ;;
    *)
      print_debug "Invalid state (came from $previous_state). Exiting."
      exit 0
      ;;
    esac
  done
}

interface_stop() {
  ip addr flush dev $DEV
  ip route flush dev $DEV

  ip -6 addr flush dev $DEV
  ip -6 route flush dev $DEV

  #TODO: Nameserver?
}

interface_start() {
  ip link set $DEV up

  if [[ ${#ipv4_addresses[@]} > 0 ]]; then
    ip addr add ${ipv4_addresses[@]} dev $DEV broadcast + #TODO: Works for multiple addresses?
    ip link set $DEV mtu $ipv4_mtu
    ip route add default via $ipv4_gateway dev $DEV
    #TODO: nameserver ${ipv4_dns[@]}
  else
    echo "No IPv4 address, skipping v4 configuration..."
  fi

  if [[ ${#ipv6_addresses[@]} > 0 ]]; then
    ip -6 addr add ${ipv6_addresses[@]} dev $DEV #TODO: Works for multiple addresses?
    ip -6 route add default via $ipv6_gateway dev $DEV
    ip -6 link set $DEV mtu $ipv6_mtu
    #TODO: nameserver ${ipv6_dns[@]}"
  else
    echo "No IPv6 address, skipping v6 configuration..."
  fi
}

###############################################################################
# Execution
###############################################################################
set -x
set -e
echo "NOTE: This script does not yet support nameserver configuration."

case "$MODE" in
"start")
  mbim-network $MBIM_INTERFACE start
  sleep 1
  mbimcli -d $MBIM_INTERFACE -p --query-ip-configuration=0 | {
    parse_input_state_machine
    print_full_configuration
    interface_stop
    interface_start
  }
  ;;
"stop")
  mbim-network $MBIM_INTERFACE stop
  interface_stop
  ;;
*)
  echo "USAGE: $0 start|stop INTERFACE" >&2
  echo "You can set an env variable DEBUG to gather debugging output." >&2
  exit 1
  ;;
esac