import interrupt
import collections
import sys_ble
import time
import vibra
import display
import color
import buttons
import leds
import config

MODE_OFF = 0
MODE_ON_FIRST = 1
MODE_ON_RX = 2
MODE_BOTH = MODE_ON_FIRST + MODE_ON_RX

DEVICES = {
    "00:00:00:00:00:00": "DEMO0",
    "11:11:11:11:11:11": "DEMO1",
}
DEV_ANY = "ANY"

LEDS_TOTAL=12
LED_MAX_VALUE=255
MIN_SIGNAL=-100
MAX_SIGNAL=-30

colors = [(0,0,255),] * LEDS_TOTAL
seen = collections.defaultdict(lambda :0)
vib_mode = MODE_ON_FIRST
led_mode = MODE_BOTH
current_dev = DEV_ANY


def scale(rssi):
    level = (rssi - MIN_SIGNAL) / (MAX_SIGNAL - MIN_SIGNAL)
    level = max(min(level, 1),0)
    return level


def mix(new_colors):
    global colors
    for led in range(LEDS_TOTAL):
        new_color = [0,0,0]
        for comp in range(3):
            new_color[comp] = max(colors[led][comp], new_colors[led][comp])
        colors[led] = tuple(new_color)


def fade(factor=0.8, mono=(255,0,255)):
    for led in range(LEDS_TOTAL):
        if mono:
            value = max(colors[led])
            new_color = (
                int(mono[0] * value / LED_MAX_VALUE * factor),
                int(mono[1] * value / LED_MAX_VALUE * factor), 
                int(mono[2] * value / LED_MAX_VALUE * factor))
        else:
            new_color = [0,0,0]
            for comp in range(3):
                new_color[comp] = int(colors[led][comp] * factor)
        colors[led] = tuple(new_color)
    leds.set_all(colors)


def led_colorbar(level, fg=(255,0,255), bg=(0,0,0), hg=(255,255,255)):
    num_leds = int(level * LEDS_TOTAL)
    other_leds = LEDS_TOTAL - num_leds - 1
    new_colors = [fg,] * num_leds + [hg,] + [bg,] * other_leds
    mix(new_colors)
    leds.set_all(colors)


def bytes2hex(bin, sep=":"):
    return sep.join(["%02x" % x for x in bin]).upper()


def notify_seen(mac):
    if vib_mode & MODE_ON_FIRST:
        vibra.vibrate(500)
    if led_mode & MODE_ON_FIRST:
        leds.flash_rocket(0, 31, 500)


def process_bt_data(mac, rssi):
    global vib_mode, seen

    mac_s = bytes2hex(mac)
    targeting = (current_dev == DEV_ANY) or (mac_s == current_dev)

    print(mac_s, current_dev, seen[current_dev], rssi)
    # first hit
    if targeting and seen[current_dev] == 0:
        notify_seen(mac)

    # counting
    seen[DEV_ANY] += 1
    if mac_s in DEVICES:
        seen[mac_s] += 1

    # visualization
    if not targeting:
        #print("unknown MAC")
        if led_mode % MODE_ON_RX:
            leds.flash_rocket(1, 31, 10)
        return    

    signal_level = scale(rssi)
    led_colorbar(signal_level)
    #print("MATCH!", signal_level)

    if vib_mode & MODE_ON_RX:
        vibra.vibrate(10)

    if led_mode & MODE_ON_RX:
        leds.flash_rocket(0, 31, 20)


def process_scan_report(scan_report):
    mac = scan_report[4]
    mac = bytes([mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]])
    rssi = scan_report[1]

    # print(bytes2hex(mac, ':'), rssi, bytes2hex(scan_report[0]), ads)
    # According to spec there is no other service announced and the
    # service is always listed in the complete list of 16 bit services
    process_bt_data(mac, rssi)


def ble_callback(_):
    while True:
        event = sys_ble.get_event()
        if event == sys_ble.EVENT_NONE:
            break
        if event == sys_ble.EVENT_SCAN_REPORT:
            while True:
                scan_report = sys_ble.get_scan_report()
                if scan_report == None:
                    break
                process_scan_report(scan_report)


disp = display.open()
v_old = 0
pause = 1

interrupt.set_callback(interrupt.BLE, ble_callback)
interrupt.enable_callback(interrupt.BLE)

try:
    vib_mode = int(config.get_string("btf_vib_mode"))
except:
    pass

try:
    led_mode = int(config.get_string("btf_led_mode"))
except:
    pass
    
try:
    current_dev = config.get_string("btf_dev")
except:
    pass

disp.clear()
disp.print("BT finder  ", posy=0, fg=color.WHITE)
disp.print("    RST#  >", posy=20, fg=color.GREEN)
disp.print("<   dev   >", posy=40, fg=color.GREEN)
disp.update()

time.sleep(3)

t0 = time.time()
sys_ble.scan_start()


while True:
    fade()
    pause -= 1
    v_new = buttons.read()
    v = ~v_old & v_new
    v_old = v_new
    dev_changed = False

    devices = list(DEVICES.keys()) + [DEV_ANY]
    current_dev_idx = (devices.index(current_dev))

    if v & buttons.BOTTOM_LEFT:
        dev_changed = True
        current_dev_idx -= 1
    if v & buttons.BOTTOM_RIGHT:
        dev_changed = True
        current_dev_idx += 1
    if v & buttons.TOP_RIGHT:
        seen[current_dev] = 0
    if dev_changed:
        if current_dev_idx >= len(devices):
            current_dev_idx = 0
        if current_dev_idx < 0:
            current_dev_idx = -1
        current_dev = devices[current_dev_idx]
        config.set_string("btf_dev", current_dev)


    if pause <= 0 or dev_changed:
        disp.clear()
        disp.print("Filter:", posy=0, fg=color.YELLOW)
        disp.print(DEVICES.get(current_dev, current_dev), posy=20, fg=color.BLUE)
        disp.print(str(seen[current_dev]), posy=40, fg=color.YELLOW)
        disp.update()
        #leds.set_all(((0,0,0),) * LEDS_TOTAL)
        pause = 20 if dev_changed else 10
    
    time.sleep(0.1)
