import tkinter as tk
import time
import threading

try:
    import pyperclip
except ImportError:
    pyperclip = None

try:
    import keyboard
except ImportError:
    keyboard = None

class ClipboardBasicApp:
    def __init__(self, root):
        self.root = root
        self.root.title("クリップボード履歴 (基本版)")
        self.root.geometry("400x300")
        self.root.attributes("-topmost", True)  # 常に最前面

        self.history = []
        self.last_text = ""
        self.last_ctrl_time = 0
        self.is_paused = False  # 自己コピー時の監視一時停止フラグ

        # フローティングボックス用の変数
        self.float_win = None
        self.current_hover_index = -1  # 現在マウスが乗っている行のインデックス

        # 説明ラベル
        self.label = tk.Label(root, text="マウスオーバーで中身表示 / クリックでコピー＆最小化", font=("Meiryo", 9))
        self.label.pack(pady=5)

        # リストボックス
        self.listbox = tk.Listbox(
            root,
            font=("Meiryo", 10),
            selectbackground="#31c795",
            selectforeground="white",
            activestyle="none"
        )
        self.listbox.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        # 【変更】マウス移動、およびリストボックスから離れたときのイベントをバインド
        self.listbox.bind("<Motion>", self.on_mouse_move)
        self.listbox.bind("<Leave>", self.on_mouse_leave)

        # クリックされた時は元の「コピー＆自動最小化」を行う
        self.listbox.bind("<<ListboxSelect>>", self.on_select)

        # ウィンドウ移動時の追従
        self.root.bind("<Configure>", self.update_float_position)

        # クリップボードの常時監視を開始
        self.check_clipboard()

        # グローバルキー監視の開始（Ctrl2回連打での復帰用）
        if keyboard:
            self.monitor_thread = threading.Thread(target=self.start_global_key_monitor, daemon=True)
            self.monitor_thread.start()

    def start_global_key_monitor(self):
        """他画面の操作中でもCtrl2回連打を監視して復帰"""
        try:
            keyboard.on_press_key('ctrl', self.on_ctrl_press)
            keyboard.wait()
        except Exception:
            pass

    def on_ctrl_press(self, event):
        current_time = time.time()
        if 0.08 < (current_time - self.last_ctrl_time) < 0.4:
            self.root.after(0, self.show_window)
            self.last_ctrl_time = 0
        else:
            self.last_ctrl_time = current_time

    def show_window(self):
        """最小化から復帰して最前面に表示"""
        self.root.deiconify()
        self.root.attributes("-topmost", True)
        self.root.focus_force()

    def check_clipboard(self):
        """クリップボードの常時監視"""
        if self.is_paused:
            self.is_paused = False
            self.root.after(1000, self.check_clipboard)
            return

        if pyperclip:
            try:
                current_text = pyperclip.paste()
                if current_text and current_text != self.last_text:
                    self.last_text = current_text
                    if current_text in self.history:
                        self.history.remove(current_text)
                    self.history.insert(0, current_text)
                    self.update_display()
            except Exception:
                pass
        self.root.after(1000, self.check_clipboard)

    def update_display(self):
        """リストボックスの表示更新"""
        self.listbox.delete(0, tk.END)
        for i, text in enumerate(self.history):
            num_display = (i + 1) if (i < 9) else (0 if i == 9 else "-")
            short_text = text.replace("\n", " ")
            if len(short_text) > 40:
                short_text = short_text[:40] + "..."

            self.listbox.insert(tk.END, f"[{num_display}]  {short_text}")

    def on_mouse_move(self, event):
        """マウスが動いたときに、指している行のテキストを判定してフローティング表示"""
        # マウス座標からリストボックスの行番号（インデックス）を取得
        index = self.listbox.nearest(event.y)

        # 実際に項目が存在する領域かチェック
        if index < 0 or index >= len(self.history):
            self.hide_floating_box()
            self.current_hover_index = -1
            return

        # リストボックスの各行のY座標の範囲を取得し、枠外の誤検知を防ぐ
        bbox = self.listbox.bbox(index)
        if bbox is None or not (bbox[1] <= event.y <= bbox[1] + bbox[3]):
            self.hide_floating_box()
            self.current_hover_index = -1
            return

        # 前回と同じ行の上を動いている場合は何もしない（チラつき防止）
        if index == self.current_hover_index:
            return

        self.current_hover_index = index
        text = self.history[index]
        self.show_floating_box(text)

    def on_mouse_leave(self, event):
        """マウスがリストボックスの外に出たらフローティングを隠す"""
        self.hide_floating_box()
        self.current_hover_index = -1

    def show_floating_box(self, text):
        """詳細を表示するフローティングボックスを作成・更新"""
        self.hide_floating_box()

        if self.root.state() != "normal":
            return

        self.float_win = tk.Toplevel(self.root)
        self.float_win.wm_overrideredirect(True)
        self.float_win.attributes("-topmost", True)

        # 外枠のデザイン（背景を水色 "#d2e9ff"、枠線（highlightbackground）を鮮やかな青 "#0078d7" に変更）
        frame = tk.Frame(
            self.float_win,
            bg="#e6fcf8",
            bd=1,
            relief=tk.SOLID,
            highlightbackground="#24ffd3",
            highlightthickness=1
        )
        frame.pack(fill=tk.BOTH, expand=True)

        # テキスト表示部（背景を同じ水色 "#d2e9ff" に統一、文字は読みやすい濃い藍色のまま）
        text_widget = tk.Text(
            frame,
            font=("Meiryo", 9),
            bg="#e6fcf8",
            fg="#0d4a3e",
            wrap=tk.WORD,
            bd=0,
            padx=5,
            pady=5,
            width=30,
            height=8
        )

        text_widget.insert(tk.END, text)
        text_widget.config(state=tk.DISABLED)
        text_widget.pack(fill=tk.BOTH, expand=True)

        self.update_float_position()

    def hide_floating_box(self):
        """フローティングボックスを閉じる"""
        if self.float_win:
            self.float_win.destroy()
            self.float_win = None

    def update_float_position(self, event=None):
        """メインウィンドウの右側にフローティングボックスを追従させる"""
        if not self.float_win or not self.float_win.winfo_exists():
            return

        root_x = self.root.winfo_x()
        root_y = self.root.winfo_y()
        root_w = self.root.winfo_width()

        float_x = root_x + root_w + 5
        float_y = root_y + 30

        self.float_win.geometry(f"+{float_x}+{float_y}")

    def on_select(self, event):
        """クリックされた時の処理（選択・並び替え・コピー・最小化）"""
        selection = self.listbox.curselection()
        if not selection:
            return

        line_num = selection[0]
        if line_num < len(self.history):
            text = self.history[line_num]

            # フローティングボックスを閉じる
            self.hide_floating_box()

            # 1. 選択された履歴を一番上に移動
            self.history.remove(text)
            self.history.insert(0, text)

            # 2. クリップボードに格納
            if pyperclip:
                self.is_paused = True
                pyperclip.copy(text)
                self.last_text = text

            # 3. 画面の並び順を更新し、一番上を選択状態にする
            self.update_display()
            self.listbox.selection_set(0)

            # 4. ツールを最小化して隠す
            self.root.attributes("-topmost", False)
            self.root.iconify()

if __name__ == "__main__":
    root = tk.Tk()
    app = ClipboardBasicApp(root)
    root.mainloop()
