from threading import Event
from time import sleep
from unittest.mock import MagicMock
from guizero import Text, Picture

SET_FONT = "Times New Roman"
TEST_FONTS = ["Times New Roman", "Liberation Serif"]

def schedule_after_test(app, widget):
    callback_event = Event()
    def callback():
        callback_event.set()
    assert not callback_event.is_set()
    widget.after(0, callback)
    # call tk to update the app
    app.tk.update()
    assert callback_event.is_set()
    #widget.cancel(callback)

def schedule_repeat_test(app, widget):
    callback_event = Event()
    def callback():
        callback_event.set()
        widget.cancel(callback)
    assert not callback_event.is_set()
    widget.repeat(0, callback)
    # call tk to update the app
    app.tk.update()
    assert callback_event.is_set()

def destroy_test(widget):
    assert widget.tk.winfo_exists()
    widget.destroy()
    assert not widget.tk.winfo_exists()
    
def enable_test(widget):
    assert widget.enabled
    widget.enabled = False
    assert not widget.enabled
    widget.enabled = True
    assert widget.enabled
    widget.disable()
    assert not widget.enabled
    widget.enable()
    assert widget.enabled

# doesn't work under pytest, app always returns None from focus_get() run 
# direct from Python it works fine.
def focus_test(app, widget):
    app.focus()    
    app.tk.update()
    assert app.tk.focus_get() == app.tk
    
    widget.focus()
    app.tk.update()
    assert app.tk.focus_get() == widget.tk

def display_test(widget):
    assert widget.visible
    widget.visible = False
    assert not widget.visible
    widget.visible = True
    assert widget.visible
    widget.hide()
    assert not widget.visible
    widget.show()
    assert widget.visible
    
def color_test(widget):
    default = widget.bg
    widget.bg = "red"
    assert widget.bg == "red"
    widget.bg = "#ff0000"
    assert widget.bg == "#ff0000"
    widget.bg = (255, 0, 0)
    assert widget.bg == "#ff0000"
    widget.bg = None
    assert widget.bg == default
    
def size_pixel_test(widget):
    default = widget.width
    widget.width = 666
    assert widget.width == 666
    widget.width = None
    assert widget.width == default
    
    default = widget.height
    widget.height = 666
    assert widget.height == 666
    widget.height = None
    assert widget.height == default

def size_text_test(widget):
    default = widget.width
    widget.width = 30
    assert widget.width == 30
    widget.width = None
    assert widget.width == default

    default = widget.height
    widget.height = 10
    assert widget.height == 10
    widget.height = None
    assert widget.height == default
    
def text_test(widget):
    default = widget.font
    widget.font = SET_FONT
    assert widget.font in TEST_FONTS
    widget.font = None
    assert widget.font == default
    
    default = widget.text_color
    widget.text_color = "red"
    assert widget.text_color == "red"
    widget.text_color = "#ff0000"
    assert widget.text_color == "#ff0000"
    widget.text_color = (255, 0, 0)
    assert widget.text_color == "#ff0000"
    widget.text_color = None
    assert widget.text_color == default

    default = widget.text_size
    widget.text_size = 16
    assert widget.text_size == 16
    widget.text_size = None
    assert widget.text_size == default
    
def events_test(widget):

    events_to_test = (
        ("when_clicked", "<when_clicked>"),
        ("when_left_button_pressed", "<when_left_button_pressed>"),
        ("when_left_button_released", "<when_left_button_released>"),
        ("when_right_button_pressed", "<when_right_button_pressed>"),
        ("when_right_button_released", "<when_right_button_released>"),
        ("when_key_pressed", "<when_key_pressed>"),
        ("when_key_released", "<when_key_released>"),
        ("when_mouse_enters", "<when_mouse_enters>"),
        ("when_mouse_leaves", "<when_mouse_leaves>"),
        ("when_mouse_dragged", "<when_mouse_dragged>"),
    )

    callback_event = Event()
    def callback():
        callback_event.set()

    callback_with_param_event = Event()
    def callback_with_param(e):
        assert e.widget == widget
        assert e.key == "A"
        assert e.x == 1
        assert e.y == 2
        assert e.display_x == 3
        assert e.display_y == 4
        callback_with_param_event.set()

    for event_to_test in events_to_test:
        # set the when_attribute to the callback
        setattr(widget, event_to_test[0], callback)
        assert not callback_event.is_set()
        # mock the event
        mock_event(widget, event_to_test[1], "A", 1, 2, 3, 4)
        assert callback_event.is_set()

        callback_with_param_event.clear()
        # set the when_attribute to the callback with a parameter
        setattr(widget, event_to_test[0], callback_with_param)
        assert not callback_with_param_event.is_set()
        # mock the event
        mock_event(widget, event_to_test[1], "A", 1, 2, 3, 4)
        assert callback_with_param_event.is_set()

        callback_event.clear()
        callback_with_param_event.clear()
        # set the when_attribute to None
        setattr(widget, event_to_test[0], None)
        mock_event(widget, event_to_test[1], "A", 1, 2, 3, 4)
        # make sure its not called
        assert not callback_event.is_set()
        assert not callback_with_param_event.is_set()

def mock_event(widget, ref, key, x, y, display_x, display_y):
    # you cant invoke a tk event so we will mock it 
    # create a mock event

    # get the event callback
    event_callback = widget.events._refs[ref]
    
    # mock a tk event
    tk_event = MagicMock()
    tk_event.char = key
    tk_event.x = x
    tk_event.y = y
    tk_event.x_root = display_x
    tk_event.y_root = display_y
    
    # call the event callback
    event_callback._event_callback(tk_event)

def cascaded_properties_test(container, widget, text):
    container.bg = "red"
    container.enabled = False
    assert widget.bg == "red"
    assert not widget.enabled 

    if text:
        container.text_color = "purple"
        assert widget.text_color == "purple"
        container.text_size = 16
        assert widget.text_size == 16

def inherited_properties_test(container, widget_create, text):
    container.bg = "red"
    container.enabled = False
    if text:
        container.text_color = "purple"
        container.text_size = 16
    
    w = widget_create()

    assert w.bg == "red"
    assert not container.enabled
    if text:
        assert w.text_color == "purple"
        assert w.text_size == 16

def cascading_enable_test(container):

    def check_children(container, test_value):
        for child in container.children:
            assert child.enabled == test_value

    assert container.enabled
    check_children(container, True)

    container.enabled = False
    assert not container.enabled
    check_children(container, False)
    
    container.enabled = True
    assert container.enabled
    check_children(container, True)
    
    container.disable()
    assert not container.enabled
    check_children(container, False)
    
    container.enable()
    assert container.enabled
    check_children(container, True)

def cascading_properties_test(container):
    t = Text(container)
    p = Picture(container)

    container.bg = "red"
    container.text_color = "purple"
    container.text_size = 16
    container.font = SET_FONT
    container.enabled = False

    assert t.bg == "red"
    assert t.text_color == "purple"
    assert t.text_size == 16
    assert t.font in TEST_FONTS
    assert t.enabled == False
    assert p.bg == "red"
    assert p.enabled == False
    
    # test that destroying widgets removes them as children
    p.destroy()
    container.bg = "green"
    assert t.bg == "green"

def inheriting_properties_test(container):
    container.bg = "red"
    container.text_color = "purple"
    container.text_size = 16
    container.font = SET_FONT
    container.enabled = False

    t = Text(container)
    assert t.bg == "red"
    assert t.text_color == "purple"
    assert t.text_size == 16
    assert t.font in TEST_FONTS
    assert not t.enabled

    p = Picture(container)
    assert p.bg == "red"
    assert not p.enabled