import math
import tkinter as tk
from tkinter import messagebox, ttk

LENGTHS = (2, 3, 6)
SIZES = (
    (25, 100), (25, 150), (25, 200),
    (40, 100), (40, 150), (40, 200),
    (50, 100), (50, 150), (50, 200),
)
SIZE_COLUMNS = 3
SCAN_INTERVAL_MS = 1000
MIN_CUBES = 1.0


class BoardsInCubeApp:

    def __init__(self, root):
        self.root = root
        self.cubes = MIN_CUBES
        self.invalid_text = None
        self.length_var = tk.IntVar(master=root, value=LENGTHS[0])
        self.size_var = tk.StringVar(master=root, value=self.size_id(*SIZES[0]))
        self.cubes_var = tk.StringVar(master=root, value=self.format_value(self.cubes))
        self.result_var = tk.StringVar(master=root)

        root.title("Сколько досок в кубе")
        root.resizable(False, False)
        root.report_callback_exception = self.show_exception

        self.build_ui()
        self.refresh()
        root.after(SCAN_INTERVAL_MS, self.scan)

    @staticmethod
    def size_id(thickness, width):
        return "{}x{}".format(thickness, width)

    @staticmethod
    def format_value(value):
        if math.isclose(value, round(value), rel_tol=0.0, abs_tol=1e-9):
            return str(int(round(value)))
        text = "{:.6f}".format(value).rstrip("0").rstrip(".")
        return text if text else "0"

    def build_ui(self):
        container = ttk.Frame(self.root, padding=12)
        container.grid(row=0, column=0, sticky="nsew")

        ttk.Label(container, text="Количество кубов:").grid(row=0, column=0, sticky="w")
        self.entry = ttk.Entry(container, textvariable=self.cubes_var, width=14, justify="right")
        self.entry.grid(row=0, column=1, padx=8, sticky="we")
        self.plus_button = ttk.Button(container, text="+", width=3, command=self.on_plus)
        self.plus_button.grid(row=0, column=2, sticky="w")
        self.minus_button = ttk.Button(container, text="\u2212", width=3, command=self.on_minus)
        self.minus_button.grid(row=0, column=3, padx=(6, 0), sticky="w")

        length_frame = ttk.LabelFrame(container, text="Длина доски, м", padding=10)
        length_frame.grid(row=1, column=0, columnspan=4, sticky="we", pady=(12, 0))
        for index, length in enumerate(LENGTHS):
            ttk.Radiobutton(
                length_frame,
                text=str(length),
                value=length,
                variable=self.length_var,
                command=self.on_option_change,
            ).grid(row=0, column=index, sticky="w", padx=(0, 24))

        size_frame = ttk.LabelFrame(container, text="Толщина × ширина, мм", padding=10)
        size_frame.grid(row=2, column=0, columnspan=4, sticky="we", pady=(12, 0))
        for index, (thickness, width) in enumerate(SIZES):
            ttk.Radiobutton(
                size_frame,
                text="{} × {}".format(thickness, width),
                value=self.size_id(thickness, width),
                variable=self.size_var,
                command=self.on_option_change,
            ).grid(row=index // SIZE_COLUMNS, column=index % SIZE_COLUMNS, sticky="w", padx=(0, 24), pady=2)

        result_frame = ttk.Frame(container)
        result_frame.grid(row=3, column=0, columnspan=4, sticky="we", pady=(16, 0))
        ttk.Label(result_frame, text="Досок в кубе:").grid(row=0, column=0, sticky="w")
        ttk.Label(
            result_frame,
            textvariable=self.result_var,
            font=("Segoe UI", 18, "bold"),
        ).grid(row=0, column=1, sticky="w", padx=(10, 0))

        self.entry.focus_set()
        self.entry.selection_range(0, tk.END)

    def on_plus(self):
        self.read_entry()
        self.set_cubes(self.cubes + 1.0)

    def on_minus(self):
        self.read_entry()
        if self.cubes > MIN_CUBES:
            self.set_cubes(max(MIN_CUBES, self.cubes - 1.0))

    def on_option_change(self):
        self.read_entry()
        self.refresh()

    def scan(self):
        self.read_entry()
        self.root.after(SCAN_INTERVAL_MS, self.scan)

    def read_entry(self):
        value = self.parse_value(self.cubes_var.get())
        if value is None:
            return
        self.cubes = value
        self.refresh()

    def set_cubes(self, value):
        self.cubes = value
        self.cubes_var.set(self.format_value(value))
        self.invalid_text = None
        self.refresh()

    def refresh(self):
        thickness, width = self.get_size()
        volume = thickness / 1000.0 * (width / 1000.0) * self.length_var.get()
        boards = self.cubes / volume
        rounded = round(boards)
        if math.isclose(boards, rounded, rel_tol=1e-9, abs_tol=1e-9):
            self.result_var.set(str(int(rounded)))
        else:
            self.result_var.set("~" + "{:.1f}".format(boards))
        state = "normal" if self.cubes > MIN_CUBES else "disabled"
        self.minus_button.configure(state=state)

    def get_size(self):
        thickness, width = self.size_var.get().split("x")
        return int(thickness), int(width)

    def parse_value(self, text):
        cleaned = text.strip().replace(",", ".")
        if not cleaned or not cleaned.strip("+-."):
            return None
        try:
            value = float(cleaned)
        except ValueError:
            self.report_invalid(text)
            return None
        if not math.isfinite(value) or value < 0.0:
            self.report_invalid(text)
            return None
        self.invalid_text = None
        return value

    def report_invalid(self, text):
        if text == self.invalid_text:
            return
        self.invalid_text = text
        messagebox.showerror(
            "Ошибка ввода",
            "Количество кубов должно быть числом, например: 3,34 или 3.34",
            parent=self.root,
        )

    def show_exception(self, exc_type, exc_value, exc_traceback):
        messagebox.showerror(
            "Ошибка",
            "{}: {}".format(exc_type.__name__, exc_value),
            parent=self.root,
        )


def main():
    root = tk.Tk()
    BoardsInCubeApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()