import math
import os.path
import re
from datetime import datetime

from config import PROP_WINDOW_SCALE, PROP_SCREEN_WIDTH, PROP_SCREEN_HEIGHT
from num_utils import get_screen_centre, get_ip, get_font_path_or_default


class IWidgetMeta(type):
    """
    Metaclass for the IWidget interface
    """
    def __instancecheck__(cls, instance):
        return cls.__subclasscheck__(type(instance))

    def __subclasscheck__(cls, subclass):
        return hasattr(subclass, 'draw') and callable(subclass.draw)

class IWidget(metaclass=IWidgetMeta):
    """
    Interface for a widget which can be drawn on the screen
    """
    def __init__(self, config: dict):
        pass
    def draw(self, screen):
        pass

class DigitalClockWidget(IWidget):
    """
    A simple clock widget which displays the current time
    """
    def __init__(self, config: dict):
        # Call the parent constructor
        super().__init__(config)

        # Get the pygame module from the config
        self.pygame = config['pygame']

        # Get the font path
        self.font_path = get_font_path_or_default(self.pygame, config.get("font_name", None), config.get("font_bold", False), config.get("font_italic", False))

        # Load the font
        self.font = self.pygame.font.Font(self.font_path, config.get("font_size", 36 * PROP_WINDOW_SCALE))
        self.font.bold = config.get("font_bold", False)
        self.font.italic = config.get("font_italic", False)

        # Set the colours
        self.color = config.get("font_color", "white")
        self.background_color = config.get("background_color", "black")

        # Set background options
        self.draw_background = config.get("draw_background", False)
        self.background_type = config.get("background_type", "color")
        if self.background_type == "image" and config.get("background_image", "") != "":
            self.bg_image = self.pygame.image.load(config.get("background_image", ""))
            self.bg_image = self.pygame.transform.scale(self.bg_image, (PROP_SCREEN_WIDTH * PROP_WINDOW_SCALE, PROP_SCREEN_HEIGHT * PROP_WINDOW_SCALE))

        # Set the clock format
        self.clock_format = config.get("clock_format", "%H:%M:%S")

        # Set the starting text
        self.text = ""

    def draw(self, screen):
        # Update the text
        self.text = datetime.now().strftime(self.clock_format)

        # Fill the background
        if self.draw_background:
            if self.background_type == "color":
                screen.fill(self.background_color)
            elif self.background_type == "image":
                screen.blit(self.bg_image, (0, 0))

        # Render the text
        text_surface = self.font.render(self.text, True, self.color)

        # Blit the text to the screen
        text_rect = text_surface.get_rect(center=get_screen_centre())
        screen.blit(text_surface, text_rect)

class TestWidget(IWidget):
    def __init__(self, config: dict):
        super().__init__(config)
        self.pygame = config['pygame']
        self.color = config.get("color", "white")
        self.bg_color = config.get("background_color", "black")
        self.text = config.get("text", "Hello, world!")
        self.font = self.pygame.font.Font(self.pygame.font.get_default_font(), 36 * PROP_WINDOW_SCALE)

    def draw(self, screen):
        screen.fill(self.bg_color)
        text_surface = self.font.render(self.text, True, self.color)
        text_rect = text_surface.get_rect(center=get_screen_centre())
        screen.blit(text_surface, text_rect)

class AnalogClockWidget(IWidget):
    def __init__(self, config: dict):
        super().__init__(config)
        self.pygame = config['pygame']

        # Set the colours
        self.background_color = config.get("background_color", "black")
        self.marking_color = config.get("marking_color", "white")
        self.clock_background_color = config.get("clock_background_color", "black")

        self.hour_hand_color = config.get("hour_hand_color", "white")
        self.minute_hand_color = config.get("minute_hand_color", "white")
        self.second_hand_color = config.get("second_hand_color", "red")

        # Set background options
        self.draw_background = config.get("draw_background", False)
        self.draw_clock_background = config.get("draw_clock_background", True)
        self.background_type = config.get("background_type", "color")
        if self.background_type == "image" and config.get("background_image", "") != "":
            self.bg_image = self.pygame.image.load(config.get("background_image", ""))
            self.bg_image = self.pygame.transform.scale(self.bg_image, (
                PROP_SCREEN_WIDTH * PROP_WINDOW_SCALE,
                PROP_SCREEN_HEIGHT * PROP_WINDOW_SCALE
            ))

        # Set the clock format
        self.clock_radius = config.get("clock_outer_radius", 100 * PROP_WINDOW_SCALE)
        self.clock_inner_radius = config.get("clock_inner_radius", 80 * PROP_WINDOW_SCALE)
        self.marking_length = config.get("marking_length", 10 * PROP_WINDOW_SCALE)
        self.marking_width = config.get("marking_width", 2 * PROP_WINDOW_SCALE)

        self.hour_hand_length = config.get("hour_hand_length", 40 * PROP_WINDOW_SCALE)
        self.minute_hand_length = config.get("minute_hand_length", 50 * PROP_WINDOW_SCALE)
        self.second_hand_length = config.get("second_hand_length", 60 * PROP_WINDOW_SCALE)

        self.hour_hand_thickness = config.get("hour_hand_thickness", 2 * PROP_WINDOW_SCALE)
        self.minute_hand_thickness = config.get("minute_hand_thickness", 2 * PROP_WINDOW_SCALE)
        self.second_hand_thickness = config.get("second_hand_thickness", 2 * PROP_WINDOW_SCALE)

        self.hands_rounded_edges = config.get("hands_rounded_edges", True)
        self.hands_smooth_motion = config.get("hands_smooth_motion", True)

    def draw_line_round_corners_polygon(self, surf, p1, p2, c, w):
        p1v = self.pygame.math.Vector2(p1)
        p2v = self.pygame.math.Vector2(p2)
        lv = (p2v - p1v).normalize()
        lnv = self.pygame.math.Vector2(-lv.y, lv.x) * w // 2
        pts = [p1v + lnv, p2v + lnv, p2v - lnv, p1v - lnv]
        self.pygame.draw.polygon(surf, c, pts)
        self.pygame.draw.circle(surf, c, p1, round(w / 2))
        self.pygame.draw.circle(surf, c, p2, round(w / 2))

    def clock_hand(self, screen, centre, radius, angle, thickness, color):
        x = centre[0] + radius * math.cos(math.radians(angle - 90))
        y = centre[1] + radius * math.sin(math.radians(angle - 90))
        self.pygame.draw.line(screen, color, centre, (int(x), int(y)), thickness)
        if self.hands_rounded_edges:
            # self.pygame.draw.circle(screen, color, (int(x), int(y)), thickness / 2)
            self.draw_line_round_corners_polygon(screen, centre, (int(x), int(y)), color, thickness)

    def draw_clock(self, screen):
        screen_centre = get_screen_centre()
        self.pygame.draw.circle(screen, self.clock_background_color, screen_centre, self.clock_radius)

    def draw_markings(self, screen):
        screen_centre = get_screen_centre()
        for i in range(0, 360, 30):
            angle_radians = math.radians(i)
            cos = math.cos(angle_radians)
            sin = math.sin(angle_radians)
            x1 = screen_centre[0] + self.clock_inner_radius * cos
            y1 = screen_centre[1] + self.clock_inner_radius * sin
            x2 = x1 + self.marking_length * cos
            y2 = y1 + self.marking_length * sin
            self.pygame.draw.line(screen, self.marking_color, (x1, y1), (x2, y2), self.marking_width)

    def draw(self, screen):
        # Fill the background
        if self.draw_background:
            if self.background_type == "color":
                screen.fill(self.background_color)
            elif self.background_type == "image":
                screen.blit(self.bg_image, (0, 0))

        if self.draw_clock_background:
            self.draw_clock(screen)

        self.draw_markings(screen)

        curr_time = datetime.now()
        if self.hands_smooth_motion:
            second = curr_time.second + curr_time.microsecond / 1000000
            minute = curr_time.minute + second / 60
            hour = curr_time.hour + minute / 60
        else:
            hour = curr_time.hour
            minute = curr_time.minute
            second = curr_time.second
        self.clock_hand(screen, get_screen_centre(), self.second_hand_length, second * 6, self.second_hand_thickness, self.second_hand_color)
        self.clock_hand(screen, get_screen_centre(), self.minute_hand_length, minute * 6, self.minute_hand_thickness, self.minute_hand_color)
        self.clock_hand(screen, get_screen_centre(), self.hour_hand_length, hour * 30, self.hour_hand_thickness, self.hour_hand_color)

class IPShowWidget(IWidget):
    def __init__(self, config: dict):
        super().__init__(config)
        self.pygame = config['pygame']

        # Backgrounds
        self.background_color = config.get("background_color", "black")
        self.draw_background = config.get("draw_background", False)
        self.draw_clock_background = config.get("draw_clock_background", True)
        self.background_type = config.get("background_type", "color")
        if self.background_type == "image" and config.get("background_image", "") != "":
            self.bg_image = self.pygame.image.load(config.get("background_image", ""))
            self.bg_image = self.pygame.transform.scale(self.bg_image, (
                PROP_SCREEN_WIDTH * PROP_WINDOW_SCALE,
                PROP_SCREEN_HEIGHT * PROP_WINDOW_SCALE
            ))


        # Get the font path
        self.font_path = get_font_path_or_default(self.pygame, config.get("font_name", None), config.get("font_bold", False), config.get("font_italic", False))

        # Load the font
        self.font = self.pygame.font.Font(self.font_path, config.get("font_size", 15 * PROP_WINDOW_SCALE))
        self.font.bold = config.get("font_bold", False)
        self.font.italic = config.get("font_italic", False)

        # Set the colours
        self.color = config.get("font_color", "white")

    def draw(self, screen):
        if self.draw_background:
            if self.background_type == "color":
                screen.fill(self.background_color)
            elif self.background_type == "image":
                screen.blit(self.bg_image, (0, 0))

        text_surface = self.font.render(f"IP Address:", True, self.color)
        text_surface_2 = self.font.render(get_ip(), True, self.color)
        text_rect = text_surface.get_rect(center=get_screen_centre())
        y = get_screen_centre()[1] + self.font.size("IP Address:")[1] + 10
        text_rect_2 = text_surface_2.get_rect(center=(get_screen_centre()[0], y))
        screen.blit(text_surface, text_rect)
        screen.blit(text_surface_2, text_rect_2)

