import json
import os
import tkinter as tk
from tkinter import messagebox, ttk, filedialog
import matplotlib
matplotlib.use("TkAgg") # TkinterにMatplotlibを埋め込むための設定
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import networkx as nx
from collections import defaultdict

# --- [データ管理・UIクラス] ---
class FamilyApp:
    def __init__(self, root):
        self.root = root
        self.root.title("家系図データ入力システム")
        self.root.geometry("450x580")

        self.file_path = "family_data.json"
        self.family_data = self.load_data()

        self.create_widgets()
        self.update_json_preview()

    def load_data(self):
        try:
            with open(self.file_path, "r", encoding="utf-8") as f:
                return json.load(f)
        except FileNotFoundError:
            return {}

    def save_data(self):
        with open(self.file_path, "w", encoding="utf-8") as f:
            json.dump(self.family_data, f, ensure_ascii=False, indent=4)

    def create_widgets(self):
        frame = ttk.LabelFrame(self.root, text=" ① フォームから人物を追加 ", padding=10)
        frame.pack(padx=10, pady=5, fill="x")
        frame.columnconfigure(1, weight=1)

        ttk.Label(frame, text="名前 (例: 祖父):").grid(row=0, column=0, sticky="w", pady=2)
        self.entry_name = ttk.Entry(frame)
        self.entry_name.grid(row=0, column=1, sticky="ew", pady=2)

        ttk.Label(frame, text="世代 (曽祖父:-3, 祖父:-2...):").grid(row=1, column=0, sticky="w", pady=2)
        self.entry_gen = ttk.Entry(frame)
        self.entry_gen.grid(row=1, column=1, sticky="ew", pady=2)

        ttk.Label(frame, text="年齢順 (右から並ぶ順: 1, 2...):").grid(row=2, column=0, sticky="w", pady=2)
        self.entry_order = ttk.Entry(frame)
        self.entry_order.grid(row=2, column=1, sticky="ew", pady=2)

        ttk.Label(frame, text="配偶者の名前:").grid(row=3, column=0, sticky="w", pady=2)
        self.entry_spouse = ttk.Entry(frame)
        self.entry_spouse.grid(row=3, column=1, sticky="ew", pady=2)

        ttk.Label(frame, text="子供の名前 (複数ならカンマ区切り):").grid(row=4, column=0, sticky="w", pady=2)
        self.entry_children = ttk.Entry(frame)
        self.entry_children.grid(row=4, column=1, sticky="ew", pady=2)

        btn_frame1 = ttk.Frame(self.root)
        btn_frame1.pack(padx=10, pady=2, fill="x")
        self.btn_add = ttk.Button(btn_frame1, text="フォームからデータを追加/更新", command=self.add_person)
        self.btn_add.pack(fill="x")

        preview_frame = ttk.LabelFrame(self.root, text=" ② 現在保存されているデータ (ここを直接書き換え可能) ", padding=10)
        preview_frame.pack(padx=10, pady=5, fill="both", expand=True)

        self.text_preview = tk.Text(preview_frame, height=12, font=("Courier", 10))
        self.text_preview.pack(fill="both", expand=True)

        btn_frame2 = ttk.Frame(self.root)
        btn_frame2.pack(padx=10, pady=5, fill="x")

        self.btn_save_direct = ttk.Button(btn_frame2, text="上の文字（JSON）の変更を直接保存する", command=self.save_data_direct)
        self.btn_save_direct.pack(side="left", padx=5, expand=True, fill="x")

        self.btn_draw = ttk.Button(btn_frame2, text="系図を描画する", command=self.open_chart_window)
        self.btn_draw.pack(side="right", padx=5, expand=True, fill="x")

    def add_person(self):
        name = self.entry_name.get().strip()
        if not name:
            messagebox.showerror("エラー", "名前は必須入力です。")
            return
        try:
            gen = int(self.entry_gen.get().strip())
            order = int(self.entry_order.get().strip())
        except ValueError:
            messagebox.showerror("エラー", "世代と年齢順には数値を入力してください。")
            return

        spouse = self.entry_spouse.get().strip()
        spouse = spouse if spouse else None
        children_raw = self.entry_children.get().strip()
        children = [c.strip() for c in children_raw.split(",")] if children_raw else []

        self.family_data[name] = {
            "gen": gen,
            "order": order,
            "spouse": spouse,
            "children": children
        }
        self.save_data()
        self.update_json_preview()
        messagebox.showinfo("成功", f"{name} さんのデータを保存しました！")
        for entry in [self.entry_name, self.entry_gen, self.entry_order, self.entry_spouse, self.entry_children]:
            entry.delete(0, tk.END)

    def update_json_preview(self):
        self.text_preview.delete("1.0", tk.END)
        self.text_preview.insert("1.0", json.dumps(self.family_data, ensure_ascii=False, indent=4))

    def save_data_direct(self):
        raw_text = self.text_preview.get("1.0", tk.END).strip()
        try:
            parsed_json = json.loads(raw_text)
            self.family_data = parsed_json
            self.save_data()
            self.update_json_preview()
            messagebox.showinfo("成功", "直接編集したデータを保存しました！")
        except json.JSONDecodeError as e:
            messagebox.showerror("エラー", f"JSONの形式が正しくありません。\n（エラー詳細: {e}）")

    def open_chart_window(self):
        if not self.family_data:
            messagebox.showwarning("警告", "データが空です。")
            return

        chart_win = tk.Toplevel(self.root)
        chart_win.title("系統図ビューアー（スクロール・保存対応）")
        chart_win.geometry("900x700")

        toolbar = ttk.Frame(chart_win, padding=5)
        toolbar.pack(side="top", fill="x")

        fig, ax = process_and_draw_tree(self.family_data)

        btn_save_img = ttk.Button(toolbar, text="💾 この系図を画像(PNG)として保存",
                                  command=lambda: save_chart_image(fig))
        btn_save_img.pack(side="left", padx=5)

        container = ttk.Frame(chart_win)
        container.pack(fill="both", expand=True)

        canvas = tk.Canvas(container, bg="white")
        h_scrollbar = ttk.Scrollbar(container, orient="horizontal", command=canvas.xview)
        v_scrollbar = ttk.Scrollbar(container, orient="vertical", command=canvas.yview)

        scrollable_frame = ttk.Frame(canvas)
        scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
        )

        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(xscrollcommand=h_scrollbar.set, yscrollcommand=v_scrollbar.set)

        v_scrollbar.pack(side="right", fill="y")
        h_scrollbar.pack(side="bottom", fill="x")
        canvas.pack(side="left", fill="both", expand=True)

        plot_canvas = FigureCanvasTkAgg(fig, master=scrollable_frame)
        plot_canvas.draw()
        plot_canvas.get_tk_widget().pack(fill="both", expand=True)

def save_chart_image(fig):
    file_path = filedialog.asksaveasfilename(
        defaultextension=".png",
        filetypes=[("PNG Image", "*.png"), ("All Files", "*.*")],
        title="系図画像の保存"
    )
    if file_path:
        fig.savefig(file_path, dpi=150, bbox_inches="tight")
        messagebox.showinfo("成功", "画像を保存しました！")

# --- [中核：データ補完 ＆ 自動サイズ・色分け描画] ---
def process_and_draw_tree(raw_data):
    family_data = json.loads(json.dumps(raw_data))

    # 不足データの自動補完
    extended_data = {}
    for name, info in family_data.items():
        if name not in extended_data:
            extended_data[name] = info
        spouse_name = info.get("spouse")
        if spouse_name and spouse_name not in family_data:
            extended_data[spouse_name] = {
                "gen": info["gen"], "order": info["order"] + 1, "spouse": name, "children": info.get("children", [])
            }
        for child_name in info.get("children", []):
            if child_name not in family_data and child_name not in extended_data:
                extended_data[child_name] = {
                    "gen": info["gen"] + 1, "order": len(extended_data) + 1, "spouse": None, "children": []
                }

    pos = {}
    gen_people = defaultdict(list)
    for name, info in extended_data.items():
        gen_people[info["gen"]].append(name)

    processed_couples = set()
    marriage_centers = {}

    # 自動サイズ調整
    max_people_in_gen = max(len(p) for p in gen_people.values()) if gen_people else 1
    total_generations = len(gen_people) if gen_people else 1

    fig_width = max(10, max_people_in_gen * 2.8)
    fig_height = max(8, total_generations * 2.5)

    for gen in sorted(gen_people.keys()):
        people = gen_people[gen]
        sorted_people_names = sorted(people, key=lambda x: extended_data[x].get("order", 99))

        x_current = max_people_in_gen * 0.4
        x_gap = 0.5
        y_coord = -gen * 2.0

        for name in sorted_people_names:
            if name in pos:
                continue
            info = extended_data[name]
            spouse_id = info["spouse"]

            if spouse_id and spouse_id in extended_data:
                pair = tuple(sorted([name, spouse_id]))
                if pair not in processed_couples:
                    pos[name] = (x_current, y_coord)
                    pos[spouse_id] = (x_current - x_gap, y_coord)
                    marriage_centers[pair] = (x_current - (x_gap / 2), y_coord)
                    processed_couples.add(pair)
                    x_current -= x_gap * 2.2
            else:
                pos[name] = (x_current, y_coord)
                x_current -= x_gap

    fig, ax = plt.subplots(figsize=(fig_width, fig_height))
    ax.set_title("伝統スタイル・二重婚姻線による自動系統図", fontsize=14, fontweight='bold', pad=20, fontname="MS Gothic")

    # 1. カギ型親子線
    for pair, (m_x, m_y) in marriage_centers.items():
        name1, name2 = pair
        children = extended_data[name1]["children"] if extended_data[name1]["children"] else extended_data[name2]["children"]
        if not children:
            continue
        mid_y = m_y - 1.0
        ax.plot([m_x, m_x], [m_y, mid_y], color="#2e7d32", linewidth=3)
        for child in children:
            if child in pos:
                c_x, c_y = pos[child]

                c_x, c_y = pos[child]
                ax.plot([m_x, c_x], [mid_y, mid_y], color="#2e7d32", linewidth=3)
                ax.plot([c_x, c_x], [mid_y, c_y], color="#2e7d32", linewidth=3)

    # 2. 伝統的な二重婚姻線
    for pair, (m_x, m_y) in marriage_centers.items():
        name1, name2 = pair
        if name1 in pos and name2 in pos:
            p1_x, p1_y = pos[name1]
            p2_x, p2_y = pos[name2]
            ax.plot([p1_x, p2_x], [m_y + 0.04, m_y + 0.04], color="#1565c0", linestyle="-", linewidth=1.5)
            ax.plot([p1_x, p2_x], [m_y - 0.04, m_y - 0.04], color="#1565c0", linestyle="-", linewidth=1.5)

    # 3. 人物のボックス（1回で同時に色を塗る安全方式）
    G = nx.Graph()
    node_colors = []
    edge_colors_list = []
    node_order_list = []

    for name in extended_data.keys():
        G.add_node(name)
        node_order_list.append(name)
        n_str = str(name)

        if any(w in n_str for w in ["父", "曽", "曾", "祖父", "伯父", "叔父", "男", "弟", "兄"]):
            node_colors.append("#e3f2fd")
            edge_colors_list.append("#1e88e5")
        elif any(w in n_str for w in ["母", "祖母", "伯母", "叔母", "女", "妹", "姉"]):
            node_colors.append("#fce4ec")
            edge_colors_list.append("#ec407a")
        else:
            node_colors.append("#f5f5f5")
            edge_colors_list.append("#9e9e9e")

    valid_pos = {n: p for n, p in pos.items() if n in G.nodes}

    nx.draw_networkx_nodes(G, valid_pos, ax=ax, nodelist=node_order_list,
                           node_size=2800, node_color=node_colors,
                           edgecolors=edge_colors_list, node_shape="s")

    labels = {name: name for name in extended_data.keys()}
    nx.draw_networkx_labels(G, valid_pos, labels=labels, font_size=10, font_family="MS Gothic", ax=ax)

    ax.axis("off")

    all_x = [p[0] for p in pos.values()]
    all_y = [p[1] for p in pos.values()]
    if all_x and all_y:
        ax.set_xlim(min(all_x) - 0.6, max(all_x) + 0.6)
        ax.set_ylim(min(all_y) - 1.2, max(all_y) + 1.2)

    plt.tight_layout()
    return fig, ax

# --- [メイン処理の起動] ---
if __name__ == "__main__":
    root = tk.Tk()
    app = FamilyApp(root)
    root.mainloop()
