"""
link configuration
"""
import tkinter as tk
from tkinter import ttk
from typing import TYPE_CHECKING, Union

from core.api.grpc import core_pb2
from core.gui.dialogs.colorpicker import ColorPickerDialog
from core.gui.dialogs.dialog import Dialog
from core.gui.themes import PADX, PADY

if TYPE_CHECKING:
    from core.gui.app import Application
    from core.gui.graph.graph import CanvasGraph, CanvasEdge


def get_int(var: tk.StringVar) -> Union[int, None]:
    value = var.get()
    if value != "":
        return int(value)
    else:
        return None


def get_float(var: tk.StringVar) -> Union[float, None]:
    value = var.get()
    if value != "":
        return float(value)
    else:
        return None


class LinkConfigurationDialog(Dialog):
    def __init__(self, master: "CanvasGraph", app: "Application", edge: "CanvasEdge"):
        super().__init__(master, app, "Link Configuration", modal=True)
        self.app = app
        self.edge = edge
        self.is_symmetric = edge.link.options.unidirectional is False
        if self.is_symmetric:
            self.symmetry_var = tk.StringVar(value=">>")
        else:
            self.symmetry_var = tk.StringVar(value="<<")

        self.bandwidth = tk.StringVar()
        self.delay = tk.StringVar()
        self.jitter = tk.StringVar()
        self.loss = tk.StringVar()
        self.duplicate = tk.StringVar()

        self.down_bandwidth = tk.StringVar()
        self.down_delay = tk.StringVar()
        self.down_jitter = tk.StringVar()
        self.down_loss = tk.StringVar()
        self.down_duplicate = tk.StringVar()

        self.color = tk.StringVar(value="#000000")
        self.color_button = None
        self.width = tk.DoubleVar()

        self.load_link_config()
        self.symmetric_frame = None
        self.asymmetric_frame = None

        self.draw()

    def draw(self):
        self.top.columnconfigure(0, weight=1)
        source_name = self.app.canvas.nodes[self.edge.src].core_node.name
        dest_name = self.app.canvas.nodes[self.edge.dst].core_node.name
        label = ttk.Label(
            self.top, text=f"Link from {source_name} to {dest_name}", anchor=tk.CENTER
        )
        label.grid(row=0, column=0, sticky="ew", pady=PADY)

        frame = ttk.Frame(self.top)
        frame.columnconfigure(0, weight=1)
        frame.columnconfigure(1, weight=1)
        frame.grid(row=1, column=0, sticky="ew", pady=PADY)
        button = ttk.Button(frame, text="Unlimited")
        button.grid(row=0, column=0, sticky="ew", padx=PADX)
        if self.is_symmetric:
            button = ttk.Button(
                frame, textvariable=self.symmetry_var, command=self.change_symmetry
            )
        else:
            button = ttk.Button(
                frame, textvariable=self.symmetry_var, command=self.change_symmetry
            )
        button.grid(row=0, column=1, sticky="ew")

        if self.is_symmetric:
            self.symmetric_frame = self.get_frame()
            self.symmetric_frame.grid(row=2, column=0, sticky="ew", pady=PADY)
        else:
            self.asymmetric_frame = self.get_frame()
            self.asymmetric_frame.grid(row=2, column=0, sticky="ew", pady=PADY)

        self.draw_spacer(row=3)

        frame = ttk.Frame(self.top)
        frame.columnconfigure(0, weight=1)
        frame.columnconfigure(1, weight=1)
        frame.grid(row=4, column=0, sticky="ew")
        button = ttk.Button(frame, text="Apply", command=self.click_apply)
        button.grid(row=0, column=0, sticky="ew", padx=PADX)
        button = ttk.Button(frame, text="Cancel", command=self.destroy)
        button.grid(row=0, column=1, sticky="ew")

    def get_frame(self) -> ttk.Frame:
        frame = ttk.Frame(self.top)
        frame.columnconfigure(1, weight=1)
        if self.is_symmetric:
            label_name = "Symmetric Link Effects"
        else:
            label_name = "Asymmetric Effects: Downstream / Upstream "
        row = 0
        label = ttk.Label(frame, text=label_name, anchor=tk.CENTER)
        label.grid(row=row, column=0, columnspan=2, sticky="ew", pady=PADY)
        row = row + 1

        label = ttk.Label(frame, text="Bandwidth (bps)")
        label.grid(row=row, column=0, sticky="ew")
        entry = ttk.Entry(
            frame,
            textvariable=self.bandwidth,
            validate="key",
            validatecommand=(self.app.validation.positive_int, "%P"),
        )
        entry.grid(row=row, column=1, sticky="ew", pady=PADY)
        if not self.is_symmetric:
            entry = ttk.Entry(
                frame,
                textvariable=self.down_bandwidth,
                validate="key",
                validatecommand=(self.app.validation.positive_int, "%P"),
            )
            entry.grid(row=row, column=2, sticky="ew", pady=PADY)
        row = row + 1

        label = ttk.Label(frame, text="Delay (us)")
        label.grid(row=row, column=0, sticky="ew")
        entry = ttk.Entry(
            frame,
            textvariable=self.delay,
            validate="key",
            validatecommand=(self.app.validation.positive_int, "%P"),
        )
        entry.grid(row=row, column=1, sticky="ew", pady=PADY)
        if not self.is_symmetric:
            entry = ttk.Entry(
                frame,
                textvariable=self.down_delay,
                validate="key",
                validatecommand=(self.app.validation.positive_int, "%P"),
            )
            entry.grid(row=row, column=2, sticky="ew", pady=PADY)
        row = row + 1

        label = ttk.Label(frame, text="Jitter (us)")
        label.grid(row=row, column=0, sticky="ew")
        entry = ttk.Entry(
            frame,
            textvariable=self.jitter,
            validate="key",
            validatecommand=(self.app.validation.positive_int, "%P"),
        )
        entry.grid(row=row, column=1, sticky="ew", pady=PADY)
        if not self.is_symmetric:
            entry = ttk.Entry(
                frame,
                textvariable=self.down_jitter,
                validate="key",
                validatecommand=(self.app.validation.positive_int, "%P"),
            )
            entry.grid(row=row, column=2, sticky="ew", pady=PADY)
        row = row + 1

        label = ttk.Label(frame, text="Loss (%)")
        label.grid(row=row, column=0, sticky="ew")
        entry = ttk.Entry(
            frame,
            textvariable=self.loss,
            validate="key",
            validatecommand=(self.app.validation.positive_float, "%P"),
        )
        entry.grid(row=row, column=1, sticky="ew", pady=PADY)
        if not self.is_symmetric:
            entry = ttk.Entry(
                frame,
                textvariable=self.down_loss,
                validate="key",
                validatecommand=(self.app.validation.positive_float, "%P"),
            )
            entry.grid(row=row, column=2, sticky="ew", pady=PADY)
        row = row + 1

        label = ttk.Label(frame, text="Duplicate (%)")
        label.grid(row=row, column=0, sticky="ew")
        entry = ttk.Entry(
            frame,
            textvariable=self.duplicate,
            validate="key",
            validatecommand=(self.app.validation.positive_int, "%P"),
        )
        entry.grid(row=row, column=1, sticky="ew", pady=PADY)
        if not self.is_symmetric:
            entry = ttk.Entry(
                frame,
                textvariable=self.down_duplicate,
                validate="key",
                validatecommand=(self.app.validation.positive_int, "%P"),
            )
            entry.grid(row=row, column=2, sticky="ew", pady=PADY)
        row = row + 1

        label = ttk.Label(frame, text="Color")
        label.grid(row=row, column=0, sticky="ew")
        self.color_button = tk.Button(
            frame,
            textvariable=self.color,
            background=self.color.get(),
            bd=0,
            relief=tk.FLAT,
            highlightthickness=0,
            command=self.click_color,
        )
        self.color_button.grid(row=row, column=1, sticky="ew", pady=PADY)
        row = row + 1

        label = ttk.Label(frame, text="Width")
        label.grid(row=row, column=0, sticky="ew")
        entry = ttk.Entry(
            frame,
            textvariable=self.width,
            validate="key",
            validatecommand=(self.app.validation.positive_float, "%P"),
        )
        entry.grid(row=row, column=1, sticky="ew", pady=PADY)

        return frame

    def click_color(self):
        dialog = ColorPickerDialog(self, self.app, self.color.get())
        color = dialog.askcolor()
        self.color.set(color)
        self.color_button.config(background=color)

    def click_apply(self):
        self.app.canvas.itemconfigure(self.edge.id, width=self.width.get())
        self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get())
        link = self.edge.link
        bandwidth = get_int(self.bandwidth)
        jitter = get_int(self.jitter)
        delay = get_int(self.delay)
        duplicate = get_int(self.duplicate)
        loss = get_float(self.loss)
        options = core_pb2.LinkOptions(
            bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, per=loss
        )
        link.options.CopyFrom(options)

        interface_one = None
        if link.HasField("interface_one"):
            interface_one = link.interface_one.id
        interface_two = None
        if link.HasField("interface_two"):
            interface_two = link.interface_two.id

        if not self.is_symmetric:
            link.options.unidirectional = True
            asym_interface_one = None
            if interface_one:
                asym_interface_one = core_pb2.Interface(id=interface_one)
            asym_interface_two = None
            if interface_two:
                asym_interface_two = core_pb2.Interface(id=interface_two)
            down_bandwidth = get_int(self.down_bandwidth)
            down_jitter = get_int(self.down_jitter)
            down_delay = get_int(self.down_delay)
            down_duplicate = get_int(self.down_duplicate)
            down_loss = get_float(self.down_loss)
            options = core_pb2.LinkOptions(
                bandwidth=down_bandwidth,
                jitter=down_jitter,
                delay=down_delay,
                dup=down_duplicate,
                per=down_loss,
                unidirectional=True,
            )
            self.edge.asymmetric_link = core_pb2.Link(
                node_one_id=link.node_two_id,
                node_two_id=link.node_one_id,
                interface_one=asym_interface_one,
                interface_two=asym_interface_two,
                options=options,
            )
        else:
            link.options.unidirectional = False
            self.edge.asymmetric_link = None

        if self.app.core.is_runtime() and link.HasField("options"):
            session_id = self.app.core.session_id
            self.app.core.client.edit_link(
                session_id,
                link.node_one_id,
                link.node_two_id,
                link.options,
                interface_one,
                interface_two,
            )
            if self.edge.asymmetric_link:
                self.app.core.client.edit_link(
                    session_id,
                    link.node_two_id,
                    link.node_one_id,
                    self.edge.asymmetric_link.options,
                    interface_one,
                    interface_two,
                )

        self.destroy()

    def change_symmetry(self):
        if self.is_symmetric:
            self.is_symmetric = False
            self.symmetry_var.set("<<")
            if not self.asymmetric_frame:
                self.asymmetric_frame = self.get_frame()
            self.symmetric_frame.grid_forget()
            self.asymmetric_frame.grid(row=2, column=0)
        else:
            self.is_symmetric = True
            self.symmetry_var.set(">>")
            if not self.symmetric_frame:
                self.symmetric_frame = self.get_frame()
            self.asymmetric_frame.grid_forget()
            self.symmetric_frame.grid(row=2, column=0)

    def load_link_config(self):
        """
        populate link config to the table
        """
        width = self.app.canvas.itemcget(self.edge.id, "width")
        self.width.set(width)
        color = self.app.canvas.itemcget(self.edge.id, "fill")
        self.color.set(color)
        link = self.edge.link
        if link.HasField("options"):
            self.bandwidth.set(str(link.options.bandwidth))
            self.jitter.set(str(link.options.jitter))
            self.duplicate.set(str(link.options.dup))
            self.loss.set(str(link.options.per))
            self.delay.set(str(link.options.delay))
        if not self.is_symmetric:
            asym_link = self.edge.asymmetric_link
            self.down_bandwidth.set(str(asym_link.options.bandwidth))
            self.down_jitter.set(str(asym_link.options.jitter))
            self.down_duplicate.set(str(asym_link.options.dup))
            self.down_loss.set(str(asym_link.options.per))
            self.down_delay.set(str(asym_link.options.delay))
