#!/usr/bin/env python3
import os
import sys
import time
import signal
import traceback
import usb1
from panda import Panda, PandaDFU
from multiprocessing import Pool

jungle = "JUNGLE" in os.environ
if jungle:
  from panda_jungle import PandaJungle  # pylint: disable=import-error

import cereal.messaging as messaging
from selfdrive.boardd.boardd import can_capnp_to_can_list

def initializer():
  """Ignore CTRL+C in the worker process.
  source: https://stackoverflow.com/a/44869451 """
  signal.signal(signal.SIGINT, signal.SIG_IGN)

def send_thread(sender_serial):
  global jungle
  while True:
    try:
      if jungle:
        sender = PandaJungle(sender_serial)
      else:
        sender = Panda(sender_serial)
        sender.set_safety_mode(Panda.SAFETY_ALLOUTPUT)

      sender.set_can_loopback(False)
      can_sock = messaging.sub_sock('can')

      while True:
        tsc = messaging.recv_one(can_sock)
        snd = can_capnp_to_can_list(tsc.can)
        snd = list(filter(lambda x: x[-1] <= 2, snd))

        try:
          sender.can_send_many(snd)
        except usb1.USBErrorTimeout:
          pass

        # Drain panda message buffer
        sender.can_recv()
    except Exception:
      traceback.print_exc()
      time.sleep(1)

if __name__ == "__main__":
  if jungle:
    serials = PandaJungle.list()
  else:
    serials = Panda.list()
  num_senders = len(serials)

  if num_senders == 0:
    print("No senders found. Exiting")
    sys.exit(1)
  else:
    print("%d senders found. Starting broadcast" % num_senders)

  if "FLASH" in os.environ:
    for s in PandaDFU.list():
      PandaDFU(s).recover()

    time.sleep(1)
    for s in serials:
      Panda(s).recover()
      Panda(s).flash()

  pool = Pool(num_senders, initializer=initializer)
  pool.map_async(send_thread, serials)

  while True:
    try:
      time.sleep(10)
    except KeyboardInterrupt:
      pool.terminate()
      pool.join()
      raise

