#!/usr/bin/python

# call2mac is an example program for encoding amateur radio callsigns into
# mac addresses.
#
# usage: call2mac [arg1 [arg2 ..]]
#        without argument, call2mac reads from stdin.
#        call2mac autodetermines if the arg is a mac address or a callsign -
#        which makes the program very easy to use.
# see
#   http://db0fhn.efi.fh-nuernberg.de/doku.php?id=projects:wlan:proposal
# for reference of this proposed standard.
#
# (c) 2009-03-26 Thomas Osterried  DL9SAU
# License: GPL (http://www.fsf.org/)

import sys

#-----------------------------------------------------------------------------

def call2mac(buf):
  FLAG_LA=1 # 0: global unique 1: local assignment
  FLAG_BC=0 # 0: unicast 1: group / multicast
  mac = []
  ssid = 0
  for c in buf:
    if c == '-':
      break
    if len(mac) > 5:
      return None
    mac.append(ord(c)-32 << 2)
  while len(mac) < 6:
    mac.append(0x00)
  sep = buf.find('-')
  if sep > -1:
    try:
      ssid = int(buf[sep+1:])
    except:
      return None
  if ssid < 0 or ssid > 0xff:
    return None
  for i in range(0, 4):
    # fill from right (byte 6 to byte 4)
    mac[5-i] = mac[5-i] | (ssid & 0x03)
    ssid = ssid >> 2
    # mac byte 2 bits 0 and 1 is reserved for further use
    # mac byte 1 bit 0: if set, it indicates a broadcast / group address
    # mac byte 1 bit 1: if set, it indicates that this mac is localy assigned
    mac[0] |= ((FLAG_LA & 0x01) << 1 | (FLAG_BC & 0x01))
  s = None
  for m in mac:
    if not s:
      s = "%2.2X" % ( m )
    else:
      s = "%s:%2.2X" % ( s, m )
  return s

#-----------------------------------------------------------------------------

def mac2call(buf):
  hextable =   {'0':  0,  '1':  1,  '2':  2,  '3':  3, \
		'4':  4,  '5':  5,  '6':  6,  '7':  7, \
		'8':  8,  '9':  9,  'A': 10,  'B': 11, \
		'C': 12,  'D': 13,  'E': 14,  'F': 15 }
  mac = buf.split(':')
  call = ""
  ssid = 0
  if not len(mac) == 6:
    return None
  for m in mac:
    if not len(m) == 2:
      return None
    l = 0
    for c in m:
      if not ( ( c >= 'A' and c <= 'F' ) or ( c >= '0' and c <= '9' ) ):
        return ''
      l = (l << 4) | hextable[c]
    call = call + "%c" % ((l >> 2) + 32)
    ssid = ssid << 2 | (l & 0x03)
  return call, (ssid & 0xff), ((ssid >> 8) & 0x0f)
  
#-----------------------------------------------------------------------------

def do_convert(buf):
  FLAG_NAMES= [ [ "Unicast", "Multicast" ], \
              [ "GlobalUnique", "LocallyAdministered" ] ]
  ssid = 0
  flags = 0
  buf = buf.strip().upper()

  if buf.find(':') == -1:
    mac = call2mac(buf)
    if not mac:
      print("invalid call")
      return
    # ..and now recompute call from mac, for convenience in the output code ;)
    buf = mac
  mac = buf
  try:
    call, ssid, flags = mac2call(mac)
  except:
    call = None
  if not call:
    print("invalid MAC address")
  else:
    print("MAC: %s CALL: >%s<  SSID: %d" % (mac, call, ssid))
    print("%s. %s. AMPR \'Reserved\' flags in Byte2: Bit1 %d, Bit2 %d" % \
      ( FLAG_NAMES[0][(flags >> 2) & 0x01], FLAG_NAMES[1][(flags >> 3) & 0x01], \
        flags & 0x01, (flags >> 1) & 0x01 ))

#-----------------------------------------------------------------------------
# main()

if len(sys.argv) > 1:
  for arg in sys.argv[1:]:
    do_convert(arg)
else:
  while True:
    line = sys.stdin.readline()
    if line == '':
      break
    do_convert(line)
