import tkinter as tk
from tkinter import colorchooser, filedialog, messagebox
from PIL import Image


class IcoEditor:

    def __init__(self, root):
        self.root = root
        self.root.title("ICOファイルエディタ (開く・編集対応)")
        self.root.geometry("640x520")

        # アイコンの基本設定（32x32ピクセル固定）
        self.icon_size = 32
        self.pixel_size = 12
        self.canvas_dim = self.icon_size * self.pixel_size

        # パレットとツールの状態
        self.current_color = "#000000"
        self.current_tool = "pen"

        # 画像データの初期化（初期状態は透明：RGBA）
        self.grid_data = [
            [(0, 0, 0, 0) for _ in range(self.icon_size)]
            for _ in range(self.icon_size)
        ]

        # UIの構築
        self.create_menu()
        self.create_toolbar()
        self.create_canvas()

        # マウスイベントのバインド
        self.canvas.bind("<Button-1>", self.on_canvas_click)
        self.canvas.bind("<B1-Motion>", self.on_canvas_drag)

    def create_menu(self):
        """メニューバーの作成（「開く」を追加）"""
        menubar = tk.Menu(self.root)
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(label="新規作成", command=self.clear_canvas)
        filemenu.add_command(label="ICOファイルを開く", command=self.open_ico)
        filemenu.add_command(label="ICOとして保存", command=self.save_ico)
        filemenu.add_separator()
        filemenu.add_command(label="終了", command=self.root.quit)
        menubar.add_cascade(label="ファイル", menu=filemenu)
        self.root.config(menu=menubar)

    def create_toolbar(self):
        """ツールバーの作成"""
        toolbar = tk.Frame(self.root, bd=1, relief=tk.RAISED, padx=5, pady=5)
        toolbar.pack(side=tk.LEFT, fill=tk.Y)

        tk.Label(toolbar, text="ツール", font=("Arial", 10, "bold")).pack(anchor=tk.W, pady=5)

        self.btn_pen = tk.Button(toolbar, text="ペン (1x1)", width=12, relief=tk.SUNKEN, command=lambda: self.set_tool("pen"))
        self.btn_pen.pack(pady=2)

        self.btn_brush = tk.Button(toolbar, text="ブラシ (2x2)", width=12, relief=tk.RAISED, command=lambda: self.set_tool("brush"))
        self.btn_brush.pack(pady=2)

        self.btn_eraser = tk.Button(toolbar, text="消しゴム", width=12, relief=tk.RAISED, command=lambda: self.set_tool("eraser"))
        self.btn_eraser.pack(pady=2)

        self.btn_bucket = tk.Button(toolbar, text="バケツ", width=12, relief=tk.RAISED, command=lambda: self.set_tool("bucket"))
        self.btn_bucket.pack(pady=2)

        self.btn_picker = tk.Button(toolbar, text="スポイト", width=12, relief=tk.RAISED, command=lambda: self.set_tool("picker"))
        self.btn_picker.pack(pady=2)

        tk.Label(toolbar, text="カラー", font=("Arial", 10, "bold")).pack(anchor=tk.W, pady=15)

        self.color_preview = tk.Frame(toolbar, width=40, height=40, bg=self.current_color, bd=2, relief=tk.SOLID)
        self.color_preview.pack(pady=5)

        btn_color = tk.Button(toolbar, text="色を選択", command=self.choose_color)
        btn_color.pack(pady=2)

    def create_canvas(self):
        """描画キャンバスの作成"""
        self.canvas = tk.Canvas(self.root, width=self.canvas_dim, height=self.canvas_dim, bg="#E0E0E0")
        self.canvas.pack(side=tk.RIGHT, expand=True, padx=10, pady=10)
        self.draw_grid()

    def draw_grid(self):
        """グリッドの描画"""
        for i in range(self.icon_size + 1):
            pos = i * self.pixel_size
            self.canvas.create_line(pos, 0, pos, self.canvas_dim, fill="#CCCCCC")
            self.canvas.create_line(0, pos, self.canvas_dim, pos, fill="#CCCCCC")

    def set_tool(self, tool):
        """ツールの切り替え"""
        self.current_tool = tool
        self.btn_pen.config(relief=tk.SUNKEN if tool == "pen" else tk.RAISED)
        self.btn_brush.config(relief=tk.SUNKEN if tool == "brush" else tk.RAISED)
        self.btn_eraser.config(relief=tk.SUNKEN if tool == "eraser" else tk.RAISED)
        self.btn_bucket.config(relief=tk.SUNKEN if tool == "bucket" else tk.RAISED)
        self.btn_picker.config(relief=tk.SUNKEN if tool == "picker" else tk.RAISED)

    def choose_color(self):
        """カラーピッカー"""
        color = colorchooser.askcolor(title="色を選択")
        if color[1]:
            self.current_color = color[1]
            self.color_preview.config(bg=self.current_color)
            if self.current_tool == "eraser":
                self.set_tool("pen")

    def rgb_to_hex(self, rgb):
        """(R, G, B) を #RRGGBB 形式に変換"""
        return f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}"

    def hex_to_rgb(self, hex_str):
        """#RRGGBB を (R, G, B) に変換"""
        hex_str = hex_str.lstrip("#")
        return tuple(int(hex_str[i : i + 2], 16) for i in (0, 2, 4))

    def paint_pixel(self, x, y):
        """指定座標に描画、または消去を実行"""
        if 0 <= x < self.icon_size and 0 <= y < self.icon_size:
            pixels_to_paint = []

            # 【新規追加】スポイトツールの処理
            if self.current_tool == "picker":
                r, g, b, a = self.grid_data[y][x]
                if a > 0: # 透明でなければ色を取得
                    self.current_color = self.rgb_to_hex((r, g, b))
                    self.color_preview.config(bg=self.current_color)
                return

            # 【新規追加】バケツツールの処理（4方向の塗りつぶし）
            if self.current_tool == "bucket":
                target_color = self.grid_data[y][x]
                new_rgb = self.hex_to_rgb(self.current_color)
                new_color = (new_rgb[0], new_rgb[1], new_rgb[2], 255)
                if target_color == new_color:
                    return

                queue = [(x, y)]
                while queue:
                    cx, cy = queue.pop(0)
                    if self.grid_data[cy][cx] == target_color:
                        self.grid_data[cy][cx] = new_color
                        tag = f"p_{cx}_{cy}"
                        self.canvas.delete(tag)
                        self.canvas.create_rectangle(
                            cx * self.pixel_size + 1, cy * self.pixel_size + 1,
                            (cx + 1) * self.pixel_size, (cy + 1) * self.pixel_size,
                            fill=self.current_color, outline="", tags=tag
                        )
                        for dx, dy in ((-1,0), (1,0), (0,-1), (0,1)):
                            nx, ny = cx + dx, cy + dy
                            if 0 <= nx < self.icon_size and 0 <= ny < self.icon_size:
                                queue.append((nx, ny))
                return

            if self.current_tool == "pen":
                pixels_to_paint.append((x, y))
            elif self.current_tool == "brush":
                for dx in (0, 1):
                    for dy in (0, 1):
                        nx, ny = x + dx, y + dy
                        if 0 <= nx < self.icon_size and 0 <= ny < self.icon_size:
                            pixels_to_paint.append((nx, ny))
            elif self.current_tool == "eraser":
                pixels_to_paint.append((x, y))

            for px, py in pixels_to_paint:
                if self.current_tool == "eraser":
                    self.grid_data[py][px] = (0, 0, 0, 0)  # 完全透明
                    fill_color = "#E0E0E0"  # キャンバス背景色
                else:
                    rgb = self.hex_to_rgb(self.current_color)
                    self.grid_data[py][px] = (rgb[0], rgb[1], rgb[2], 255)  # 不透明
                    fill_color = self.current_color

                tag = f"p_{px}_{py}"
                self.canvas.delete(tag)

                x0 = px * self.pixel_size
                y0 = py * self.pixel_size
                x1 = x0 + self.pixel_size
                y1 = y0 + self.pixel_size

                self.canvas.create_rectangle(x0 + 1, y0 + 1, x1, y1, fill=fill_color, outline="", tags=tag)

    def on_canvas_click(self, event):
        x = event.x // self.pixel_size
        y = event.y // self.pixel_size
        self.paint_pixel(x, y)

    def on_canvas_drag(self, event):
        x = event.x // self.pixel_size
        y = event.y // self.pixel_size
        self.paint_pixel(x, y)

    def clear_canvas(self):
        """キャンバスのリセット"""
        if messagebox.askyesno("確認", "キャンバスをクリアしますか？"):
            self.canvas.delete("all")
            self.draw_grid()
            self.grid_data = [[(0, 0, 0, 0) for _ in range(self.icon_size)] for _ in range(self.icon_size)]

    def open_ico(self):
        """既存のICOファイルを開いてキャンバスにインポートする機能"""
        file_path = filedialog.askopenfilename(
            filetypes=[("Icon Files", "*.ico")],
            title="ICOファイルを開く"
        )
        if not file_path:
            return

        try:
            # ICOファイルを開く
            with Image.open(file_path) as img:
                # 32x32の画像サイズが含まれているかチェックし、無ければ一番近いサイズにリサイズ
                img_rgba = img.convert("RGBA")
                if img_rgba.size != (self.icon_size, self.icon_size):
                    img_rgba = img_rgba.resize((self.icon_size, self.icon_size), Image.Resampling.NEAREST)

                # キャンバスを一旦クリア
                self.canvas.delete("all")
                self.draw_grid()

                # 各ピクセルの色を読み込んで配列とキャンバスに反映
                for y in range(self.icon_size):
                    for x in range(self.icon_size):
                        r, g, b, a = img_rgba.getpixel((x, y))
                        self.grid_data[y][x] = (r, g, b, a)

                        # 透明ではない（アルファ値が0より大きい）場合のみキャンバスに描画
                        if a > 0:
                            hex_color = self.rgb_to_hex((r, g, b))
                            x0 = x * self.pixel_size
                            y0 = y * self.pixel_size
                            x1 = x0 + self.pixel_size
                            y1 = y0 + self.pixel_size

                            tag = f"p_{x}_{y}"
                            self.canvas.create_rectangle(
                                x0 + 1, y0 + 1, x1, y1,
                                fill=hex_color, outline="", tags=tag
                            )
            messagebox.showinfo("成功", "ICOファイルを読み込みました！")
        except Exception as e:
            messagebox.showerror("エラー", f"ファイルを開けませんでした:\n{e}")

    def save_ico(self):
        """描画データをICOファイルとして書き出し（透明度を完全維持）"""
        file_path = filedialog.asksaveasfilename(
            defaultextension=".ico",
            filetypes=[("Icon Files", "*.ico")],
            title="ICOファイルとして保存",
        )
        if not file_path:
            return

        try:
            img = Image.new("RGBA", (self.icon_size, self.icon_size))

            for y in range(self.icon_size):
                for x in range(self.icon_size):
                    r, g, b, a = self.grid_data[y][x]
                    # 透明な箇所はWindowsのバグ防止のためにRGBを白マスク(255,255,255,0)にする
                    if a == 0:
                        img.putpixel((x, y), (255, 255, 255, 0))
                    else:
                        img.putpixel((x, y), (r, g, b, a))

            img.save(file_path, format="ICO", sizes=[(self.icon_size, self.icon_size)])
            messagebox.showinfo("成功", "完全透過のICOファイルを保存しました！")
        except Exception as e:
            messagebox.showerror("エラー", f"保存中にエラーが発生しました:\n{e}")


if __name__ == "__main__":
    root = tk.Tk()
    app = IcoEditor(root)
    root.mainloop()
