import tkinter as tk
from tkinter import simpledialog, messagebox

class Person:
    def __init__(self, name="Person"):
        self.name = name
        self.spouse = None
        self.parents = []  # up to 2
        self.children = []

class FamilyTreeApp(tk.Tk):
    BOX_W = 80
    BOX_H = 40
    V_GAP = 50
    H_GAP = 40

    def __init__(self):
        super().__init__()
        self.title("Family Tree")
        self.canvas = tk.Canvas(self, width=1000, height=700, bg="white")
        self.canvas.pack(fill="both", expand=True)

        self.root_person = Person()
        self.box_items = {}

        self.canvas.bind("<Button-1>", self.on_click)
        self.draw_tree()

    def edit_name(self, person):
        new_name = simpledialog.askstring("Edit Name", "Enter name:", initialvalue=person.name)
        if new_name:
            person.name = new_name
            self.draw_tree()

    def add_spouse(self, person):
        if person.spouse:
            messagebox.showinfo("Info", "This person already has a spouse.")
            return
        person.spouse = Person("Spouse")
        person.spouse.spouse = person
        self.draw_tree()

    def add_child(self, person):
        child = Person("Child")
        child.parents = [person]
        if person.spouse:
            child.parents.append(person.spouse)
        for p in child.parents:
            p.children.append(child)
        self.draw_tree()

    def add_parent(self, person):
        if len(person.parents) >= 2:
            messagebox.showinfo("Info", "This person already has 2 parents.")
            return
        parent = Person("Parent")
        parent.children.append(person)
        person.parents.append(parent)
        self.draw_tree()

    def delete_person(self, person):
        if person.spouse:
            person.spouse.spouse = None
        for p in person.parents:
            if person in p.children:
                p.children.remove(person)
        for ch in person.children:
            if person in ch.parents:
                ch.parents.remove(person)
        if person is self.root_person:
            self.root_person = Person()
        self.draw_tree()

    def draw_tree(self):
        self.canvas.delete("all")
        self.box_items.clear()
        self.layout_positions = {}

        # center the tree in the canvas
        self.update_idletasks()
        canvas_mid = self.canvas.winfo_width() // 2

        self._assign_positions(self.root_person, 0, canvas_mid)
        self._draw_connections(self.root_person)
        self._draw_boxes(self.root_person)

    def _assign_positions(self, person, level, offset):
        if person in self.layout_positions:
            return self.layout_positions[person][0]

        if person.children:
            child_xs = []
            for ch in person.children:
                child_xs.append(self._assign_positions(ch, level + 1, offset))
                offset = child_xs[-1] + self.BOX_W + self.H_GAP
            center_x = (child_xs[0] + child_xs[-1]) / 2
        else:
            center_x = offset
            offset += self.BOX_W + self.H_GAP

        self.layout_positions[person] = (center_x, level)
        if person.spouse:
            spouse_x = center_x + self.BOX_W + 10
            self.layout_positions[person.spouse] = (spouse_x, level)
        return center_x

    def _draw_connections(self, person, drawn=set()):
        if person.spouse and (person, person.spouse) not in drawn:
            x, y = self._get_box_center(person)
            sx, sy = self._get_box_center(person.spouse)
            self.canvas.create_line(x, y, sx, sy)
            drawn.add((person, person.spouse))
            drawn.add((person.spouse, person))
        for ch in person.children:
            cx, cy = self._get_box_center(ch)
            px, py = self._get_box_center(person)
            self.canvas.create_line(px, py + self.BOX_H//2, cx, cy - self.BOX_H//2)
            if person.spouse:
                sx, sy = self._get_box_center(person.spouse)
                self.canvas.create_line(sx, sy + self.BOX_H//2, cx, cy - self.BOX_H//2)
            self._draw_connections(ch, drawn)

    def _draw_boxes(self, person, drawn=set()):
        if person in drawn:
            return
        drawn.add(person)
        x, level = self.layout_positions[person]
        y = level * (self.BOX_H + self.V_GAP)
        box = self.canvas.create_rectangle(
            x, y, x + self.BOX_W, y + self.BOX_H, outline="black", fill="lightyellow"
        )
        text = self.canvas.create_text(
            x + self.BOX_W/2, y + self.BOX_H/2, text=person.name
        )
        self.box_items[box] = person
        self.box_items[text] = person
        if person.spouse:
            self._draw_boxes(person.spouse, drawn)
        for ch in person.children:
            self._draw_boxes(ch, drawn)

    def _get_box_center(self, person):
        x, level = self.layout_positions[person]
        y = level * (self.BOX_H + self.V_GAP)
        return (x + self.BOX_W/2, y + self.BOX_H/2)

    def on_click(self, event):
        items = self.canvas.find_closest(event.x, event.y)
        if not items:
            return
        person = self.box_items.get(items[0])
        if person:
            self.popup_menu(person, event.x_root, event.y_root)

    def popup_menu(self, person, x, y):
        menu = tk.Menu(self, tearoff=0)
        menu.add_command(label="Edit name", command=lambda: self.edit_name(person))
        menu.add_command(label="Add spouse", command=lambda: self.add_spouse(person))
        menu.add_command(label="Add child", command=lambda: self.add_child(person))
        menu.add_command(label="Add parent", command=lambda: self.add_parent(person))
        menu.add_command(label="Delete", command=lambda: self.delete_person(person))
        menu.tk_popup(x, y)

if __name__ == "__main__":
    app = FamilyTreeApp()
    app.mainloop()
