import gi
import threading
import time
import datetime
import collections
import usb.core
import usb.util
import requests
import json

gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gio, Gdk
from requests.adapters import HTTPAdapter, Retry
from usbcardreader import UsbCardReader


with open('config.json', 'r') as f:
    config = json.load(f)


activeCards = []

TIME = config['countdown_seconds']

USB_IF      = 0 # Interface
USB_TIMEOUT = 5 # Timeout in ms
USB_VENDOR  = 0xffff # Vendor-ID:
USB_PRODUCT = 0x0035 # Product-ID

db = {}

queue_to_send = []


session = requests.Session()


retries = Retry(total=10,
                backoff_factor=1,
                status_forcelist=[ 500, 502, 503, 504 ])

session.mount('https://', HTTPAdapter(max_retries=retries))


session.post(config['host'] + '/api/login', {'email':config['login'], 'password': config['password']})

def refreshDb():
    global db
    resp = session.get(config['host'] + '/api/racers')
    if resp.status_code == 200:
        json = resp.json()
        db = {}
        for racer in json:
            if racer["card_id"]:
                db[racer["card_id"]] = racer


def refreshDbEvery3mins():
    while True:
        refreshDb()
        time.sleep(3 * 60)


threading.Thread(target=refreshDbEvery3mins).start()


def sendQueueToSend():
    global queue_to_send
    while True:
        if len(queue_to_send) > 0:
            to_send = queue_to_send.pop()

            response = session.post(config['host'] + '/api/station/register',
                json=to_send
            )
            if response.status_code != 200:
                if response.status_code == 400:
                    continue
                queue_to_send.append(to_send) #try again later
                time.sleep(1)
        else:
            time.sleep(1)


threading.Thread(target=sendQueueToSend).start()


def callbackOnCard(card_id):
    global win, activeCards, db
    if card_id not in db:
        refreshDb()
    if card_id in db:
        if card_id not in activeCards:
            activeCards.append(card_id)
            win.arb(db[card_id])
            print('Card is active now')
        else:
            print('Card is already active')
    else:
        print(f'Card {card_id} not found in db')


UsbCardReader(callbackOnCard)


class GridRow():
    def __init__(self, parent, racer, index):
        global queue_to_send
        self.id = racer['starting_number']
        self.parent = parent

        self.grid = Gtk.Grid()

        if index % 2 == 0:
            self.grid.set_name("lightgrid")
        else:
            self.grid.set_name("darkgrid")

        self.runid = Gtk.Label(label=str(racer["starting_number"]), name="gridlabel")
        self.runid.set_justify(2)
        self.runid.set_width_chars(10)

        self.runb = Gtk.Label(label='00:00', name="gridlabel")
        self.runb.set_hexpand(1)

        self.grid.attach(self.runid, 0, 0, 1, 1)
        self.grid.attach(self.runb, 1, 0, 1, 1)

        parent.grid.attach(self.grid, 0, index, 2, 1)

        countdown=threading.Thread(target=self.updateTime)
        countdown.start()

        queue_to_send.append({
            'card_id': racer['card_id'],
            'station_id': config['station_id'],
            'time': datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S.%f')
        })

    def printIndex(self, button):
        print(self.index)

    def updateTime(self):
        t = TIME
        while t >= -10:
            if t < 0:
                mins, secs = divmod((t * -1), 60)
                timer = '-{:02d}:{:02d}'.format(mins, secs)
            else:
                mins, secs = divmod(t, 60)
                timer = '{:02d}:{:02d}'.format(mins, secs)
            self.runb.set_label(timer)
            time.sleep(1)
            t -= 1
            if t == 10:
                if self.grid.get_name() == "lightgrid":
                    self.grid.set_name("lredgrid")
                else:
                    self.grid.set_name("dredgrid")

        GridWindow.remRow(self.parent)

class GridWindow(Gtk.ApplicationWindow):
    def __init__(self, **kargs):
        super().__init__(**kargs, title='Alkator Clock')

        css_provider = Gtk.CssProvider()
        css_provider.load_from_file(Gio.File.new_for_path("style.css"))
        Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

        runlabel = Gtk.Label(label="ID", name="gridlabel")
        runlabel.set_justify(2)
        cntlabel = Gtk.Label(label="Countdown", name="gridlabel")
        cntlabel.set_justify(2)

        self.runners = []

        self.grid = Gtk.Grid()
        self.grid.attach(runlabel, 0, 0, 1, 1)
        self.grid.attach(cntlabel, 1, 0, 2, 1)

        self.set_child(self.grid)

    def arb(self, racer):
        self.runners.append(GridRow(self, racer, len(activeCards)))

    def rrb(self, button):
        self.remRow()

    def addRow(self, runner, index):
        runid = Gtk.Label(label=str(runner))
        runb = Gtk.Button(label='time_placeholder')

        self.grid.attach(runid, 0, index, 1, 1)
        self.grid.attach(runb, 1, index, 1, 1)

    def remRow(self):
        global activeCards
        if len(activeCards) > 0:
            self.grid.remove_row(1)
            del activeCards[0]


def on_activate(app):
    # Create window
    global win
    win = GridWindow(application=app)
    win.present()

UsbCardReader()
app = Gtk.Application(application_id='com.example.App')
app.connect('activate', on_activate)

app.run(None)
