"""
input validation
"""
import re
import tkinter as tk
from typing import TYPE_CHECKING

import netaddr
from netaddr import IPNetwork

if TYPE_CHECKING:
    from core.gui.app import Application

SMALLEST_SCALE = 0.5
LARGEST_SCALE = 5.0


class InputValidation:
    def __init__(self, app: "Application"):
        self.master = app.master
        self.positive_int = None
        self.positive_float = None
        self.app_scale = None
        self.name = None
        self.ip4 = None
        self.rgb = None
        self.hex = None
        self.register()

    def register(self):
        self.positive_int = self.master.register(self.check_positive_int)
        self.positive_float = self.master.register(self.check_positive_float)
        self.app_scale = self.master.register(self.check_scale_value)
        self.name = self.master.register(self.check_node_name)
        self.ip4 = self.master.register(self.check_ip4)
        self.rgb = self.master.register(self.check_rbg)
        self.hex = self.master.register(self.check_hex)

    @classmethod
    def ip_focus_out(cls, event: tk.Event):
        value = event.widget.get()
        try:
            IPNetwork(value)
        except netaddr.core.AddrFormatError:
            event.widget.delete(0, tk.END)
            event.widget.insert(tk.END, "invalid")

    @classmethod
    def focus_out(cls, event: tk.Event, default: str):
        value = event.widget.get()
        if value == "":
            event.widget.insert(tk.END, default)

    @classmethod
    def check_positive_int(cls, s: str) -> bool:
        if len(s) == 0:
            return True
        try:
            int_value = int(s)
            if int_value >= 0:
                return True
            return False
        except ValueError:
            return False

    @classmethod
    def check_positive_float(cls, s: str) -> bool:
        if len(s) == 0:
            return True
        try:
            float_value = float(s)
            if float_value >= 0.0:
                return True
            return False
        except ValueError:
            return False

    @classmethod
    def check_node_name(cls, s: str) -> bool:
        if len(s) < 0:
            return False
        if len(s) == 0:
            return True
        for char in s:
            if not char.isalnum() and char != "_":
                return False
        return True

    @classmethod
    def check_canvas_int(cls, s: str) -> bool:
        if len(s) == 0:
            return True
        try:
            int_value = int(s)
            if int_value >= 0:
                return True
            return False
        except ValueError:
            return False

    @classmethod
    def check_canvas_float(cls, s: str) -> bool:
        if not s:
            return True
        try:
            float_value = float(s)
            if float_value >= 0.0:
                return True
            return False
        except ValueError:
            return False

    @classmethod
    def check_scale_value(cls, s: str) -> bool:
        if not s:
            return True
        try:
            float_value = float(s)
            if SMALLEST_SCALE <= float_value <= LARGEST_SCALE or float_value == 0:
                return True
            return False
        except ValueError:
            return False

    @classmethod
    def check_ip4(cls, s: str) -> bool:
        if not s:
            return True
        pat = re.compile("^([0-9]+[.])*[0-9]*$")
        if pat.match(s) is not None:
            _32bits = s.split(".")
            if len(_32bits) > 4:
                return False
            for _8bits in _32bits:
                if (
                    (_8bits and int(_8bits) > 255)
                    or len(_8bits) > 3
                    or (_8bits.startswith("0") and len(_8bits) > 1)
                ):
                    return False
            return True
        else:
            return False

    @classmethod
    def check_rbg(cls, s: str) -> bool:
        if not s:
            return True
        if s.startswith("0") and len(s) >= 2:
            return False
        try:
            value = int(s)
            if 0 <= value <= 255:
                return True
            else:
                return False
        except ValueError:
            return False

    @classmethod
    def check_hex(cls, s: str) -> bool:
        if not s:
            return True
        pat = re.compile("^([#]([0-9]|[a-f])+)$|^[#]$")
        if pat.match(s):
            if 0 <= len(s) <= 7:
                return True
            else:
                return False
        else:
            return False
