import tkinter as tk
import collections
import time
import asyncio
import operator
import lib

# doesn't actually move. but looks like it does
class ScrollingUpdate(collections.UserList, metaclass=lib.SingletonType):

    def __init__(self, master=None, initlist=None, orient=tk.HORIZONTAL, reverse=False):
        super().__init__(initlist)
        self.offset = 0
        self.show = 5 if initlist is None else len(initlist)
        self.last_list_len = None
        self._reverse = reverse
        if reverse:
           self.skip = -1
        else:
            self.skip = 1

        if orient == tk.HORIZONTAL:
            master.bind_all("<KP_Left>", self.inc_offset if reverse else self.dec_offset)
            master.bind_all("<KP_Right>", self.dec_offset if reverse else self.inc_offset)

        elif orient == tk.VERTICAL:
            master.bind_all("<KP_Up>", self.inc_offset if reverse else self.dec_offset)
            master.bind_all("<KP_Down>", self.dec_offset if reverse else self.inc_offset)
        else:
            raise ValueError
        self.orient = orient
    
    def advance(self):
        if self._reverse:
            self.inc_offset()
        else:
            self.dec_offset()
    
    def get(self):
        if self._reverse:
            return self[-1].get()
        else:
            return self[0].get()

    def inc_offset(self, *event):
        self.offset += 1

    def dec_offset(self, *event):
        if self.offset - 1 >= 0:
            self.offset -= 1
    
    def update(self, lst):
        if self.last_list_len is None:
            self.last_list_len = len(lst)
        lst_len = len(lst)
        self_len = len(self)
        if not lst:
            self.offset = 0

        if lst_len < self.last_list_len and self.offset:
            diff = self.last_list_len - lst_len
            if self.offset - diff >= 0:
                self.offset -= diff
            else:
                self.offset = 0


        if self.offset + self.show > lst_len:
            self.offset -= 1
            
        if self_len > lst_len + self.offset:
            self.offset = 0
            for widget in self:
                widget.reset()

        for widget, obj in zip(self[::self.skip], lst[self.offset:]):
            widget.update(obj)
        
        self.last_list_len = lst_len
        
        if self._reverse:
            if lst_len:
                lo = self.offset / lst_len
                hi = (self.show / lst_len) + lo
                return 1 - hi, 1 -lo
            else:
                return 0, 1
        else:
            if lst_len:
                lo = self.offset / lst_len
                hi = (self.show / lst_len) + lo
                return lo, hi
            else:
                return 0,1


class DiscreteScrolledFrame(tk.Frame):

    def __init__(self, master, initwidget, nmemb, orient=tk.HORIZONTAL, reverse=False, **kwargs):
        assert callable(initwidget)
        super().__init__(master, **kwargs)
        self._interior = tk.Frame(self)
        self.scroller = ScrollingUpdate(self,
                initlist=[initwidget(master=self._interior) for i in range(nmemb)],
                orient=orient,
                reverse=reverse)
        self.scrollbar = tk.Scrollbar(self, orient=orient)
        if orient == tk.HORIZONTAL:
            self.grid_columnconfigure(0, weight=1)
            self._interior.grid(row=0, column=0, sticky="nswe")
            self.scrollbar.grid(row=1, column=0, sticky="nswe")
        elif orient == tk.VERTICAL:
            self.grid_rowconfigure(0, weight=1)
            self._interior.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
            self.scrollbar.pack(side=tk.LEFT, fill=tk.Y)
        self.orient = orient

    def update(self, synclist):
        self.data = synclist
        self.scrollbar.set(*self.scroller.update(synclist))

    def grid_inner(self, **kwargs):
        if self.orient == tk.HORIZONTAL:
            self._interior.grid_rowconfigure(0, weight=1)
            for i, widget in enumerate(self.scroller):
                self._interior.grid_columnconfigure(i, weight=1)
                widget.grid(row=0, column=i, **kwargs)
            if self.scroller._reverse:
                self._interior.grid_columnconfigure(len(self.scroller)-1, weight=0)
            else:
                self._interior.grid_columnconfigure(0, weight=0)
        elif self.orient == tk.VERTICAL:
            self._interior.grid_columnconfigure(0, weight=1)
            for i, widget in enumerate(self.scroller):
                self._interior.grid_rowconfigure(i, weight=2)
                widget.grid(row=i, column=0, **kwargs)
            # widgets that are first must fully expand
            if self.scroller._reverse:
                self._interior.grid_rowconfigure(len(self.scroller)-1, weight=0)
            else:
                self._interior.grid_rowconfigure(0, weight=0)