"""
悬浮虚拟键盘
- 美式 QWERTY 键盘排列（字母 + 数字 + 常用符号）
- Shift / Caps Lock / 退格 / 空格 / 回车 / 关闭
- 可拖拽悬浮，自动跟随焦点输入框
- 暗色主题，大按钮适合触屏
"""

from PyQt6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
    QApplication, QLineEdit, QTextEdit, QGridLayout
)
from PyQt6.QtCore import Qt, QEvent, QPoint, QTimer, pyqtSignal
from PyQt6.QtGui import QMouseEvent, QTextCursor


# ─── 长按重复配置 ─────────────────────────────────────────
LONG_PRESS_DELAY = 400    # 按住多少毫秒后开始重复
LONG_PRESS_REPEAT = 80    # 开始重复后每多少毫秒触发一次


# ─── 键盘布局定义 ──────────────────────────────────────────────

KEY_ROWS = {
    "normal": [
        ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
        ["a", "s", "d", "f", "g", "h", "j", "k", "l"],
        ["z", "x", "c", "v", "b", "n", "m"],
    ],
    "shift": [
        ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
        ["A", "S", "D", "F", "G", "H", "J", "K", "L"],
        ["Z", "X", "C", "V", "B", "N", "M"],
    ],
}

NUMBER_ROW = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]

# 符号键：按 ?!(. 切换时替换数字行
SYMBOL_ROW = ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"]
# 显示用（Qt 中 & 需要写 && 才能显示一个 &）
SYMBOL_ROW_DISPLAY = ["!", "@", "#", "$", "%", "^", "&&", "*", "(", ")"]

# 额外符号：切换到符号键盘后显示在字母区
EXTRA_SYMBOLS = [
    ["-", "=", "[", "]", "\\", ";", "'", ",", ".", "/"],
    ["_", "+", "{", "}", "|", ":", "\"", "<", ">", "?"],
]
# 常见后缀（放在符号键盘多余的字母位置上）
COMMON_SUFFIXES = [".com", ".cn", ".org", ".net", ".edu", ".co"]

KEY_STYLE = """
QPushButton {
    min-width: 52px;
    min-height: 52px;
    font-size: 22px;
    font-weight: 600;
    color: #f2f2f2;
    background-color: #4a4a4a;
    border: 2px solid #646464;
    border-radius: 8px;
    padding: 4px 4px;
}
QPushButton:hover {
    background-color: #5a5a5a;
    border-color: #888888;
}
QPushButton:pressed {
    background-color: #2f6f91;
    border-color: #5a9fcf;
}
"""

CTRL_KEY_STYLE = """
QPushButton {
    min-width: 52px;
    min-height: 52px;
    font-size: 18px;
    font-weight: 600;
    color: #f2f2f2;
    background-color: #555555;
    border: 2px solid #707070;
    border-radius: 8px;
    padding: 4px 8px;
}
QPushButton:hover {
    background-color: #666666;
    border-color: #909090;
}
QPushButton:pressed {
    background-color: #2f6f91;
    border-color: #5a9fcf;
}
"""

ACTIVE_CTRL_STYLE = """
QPushButton {
    min-width: 52px;
    min-height: 52px;
    font-size: 18px;
    font-weight: 600;
    color: #ffffff;
    background-color: #2f6f91;
    border: 2px solid #5a9fcf;
    border-radius: 8px;
    padding: 4px 8px;
}
QPushButton:hover {
    background-color: #3a85b3;
    border-color: #6fb8dd;
}
"""


class FloatingKeyboard(QWidget):
    """可悬浮拖拽的虚拟键盘，自动跟随焦点输入框"""

    key_pressed = pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowFlags(
            Qt.WindowType.FramelessWindowHint
            | Qt.WindowType.WindowStaysOnTopHint
            | Qt.WindowType.Tool
        )
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
        # 防止键盘窗口/按钮抢走输入框焦点
        self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating)
        self.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self.setStyleSheet("background: transparent;")

        self._drag_pos = None
        self._shift_on = False
        self._caps_on = False
        self._symbol_mode = False

        self._target_widget = None

        self.init_ui()

    # ── UI 构建 ──────────────────────────────────────────────

    def init_ui(self):
        outer = QWidget()
        outer.setStyleSheet(
            "background-color: #333333; border: 2px solid #555555; border-radius: 12px;"
        )
        outer_layout = QVBoxLayout(outer)
        outer_layout.setContentsMargins(8, 8, 8, 8)
        outer_layout.setSpacing(4)

        # ── 顶部拖拽条 ──
        title_bar = QWidget()
        title_bar.setFixedHeight(28)
        title_bar.setStyleSheet("background: transparent;")
        title_bar.mousePressEvent = self._title_mouse_press
        title_bar.mouseMoveEvent = self._title_mouse_move
        title_layout = QHBoxLayout(title_bar)
        title_layout.setContentsMargins(8, 0, 8, 0)

        drag_label = QPushButton("≡ 键盘")
        drag_label.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        drag_label.setStyleSheet("""
            QPushButton {
                background: transparent;
                color: #aaaaaa;
                font-size: 16px;
                border: none;
                text-align: left;
            }
        """)
        title_layout.addWidget(drag_label)
        title_layout.addStretch()

        close_btn = QPushButton("✕")
        close_btn.setFixedSize(28, 28)
        close_btn.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        close_btn.setStyleSheet("""
            QPushButton {
                background: transparent;
                color: #aaaaaa;
                font-size: 18px;
                border: none;
                border-radius: 14px;
            }
            QPushButton:hover {
                background-color: #555555;
                color: #ffffff;
            }
        """)
        close_btn.clicked.connect(self.hide)
        title_layout.addWidget(close_btn)
        outer_layout.addWidget(title_bar)

        # ── 内容区：左侧 4 行按键 + 右侧功能键 ──
        content = QHBoxLayout()
        content.setSpacing(6)

        # ====== 左侧：QGridLayout 4行×10列，等宽列 ======
        grid = QGridLayout()
        grid.setSpacing(4)
        for col in range(10):
            grid.setColumnStretch(col, 1)

        # Row 0: 数字行
        self.num_buttons = []
        for col, ch in enumerate(NUMBER_ROW):
            btn = self._make_key(ch, ch)
            self.num_buttons.append(btn)
            grid.addWidget(btn, 0, col)

        # Row 1: 字母行 q ~ p
        self._letter_buttons = []
        for col, ch in enumerate(KEY_ROWS["normal"][0]):
            btn = self._make_key(ch, ch)
            self._letter_buttons.append(btn)
            grid.addWidget(btn, 1, col)

        # Row 2: 字母行 a ~ l (9个，第10列空位留给符号模式)
        for col, ch in enumerate(KEY_ROWS["normal"][1]):
            btn = self._make_key(ch, ch)
            self._letter_buttons.append(btn)
            grid.addWidget(btn, 2, col)

        # Row 3: [?!(.] + 字母 z ~ m + [⇧] + [Caps]
        self.sym_btn = self._make_ctrl_btn("?!(.", self._toggle_symbol)
        grid.addWidget(self.sym_btn, 3, 0)

        for i, ch in enumerate(KEY_ROWS["normal"][2]):
            btn = self._make_key(ch, ch)
            self._letter_buttons.append(btn)
            grid.addWidget(btn, 3, i + 1)

        self.shift_btn = self._make_ctrl_btn("⇧", self._toggle_shift)
        grid.addWidget(self.shift_btn, 3, 8)

        self.caps_btn = self._make_ctrl_btn("Caps", self._toggle_caps)
        grid.addWidget(self.caps_btn, 3, 9)

        # 各行等高分占布局
        for row in range(4):
            grid.setRowStretch(row, 1)

        content.addLayout(grid)

        # ====== 右侧：4个竖向功能键，拉伸填满高度 ======
        right_col = QVBoxLayout()
        right_col.setSpacing(4)

        RIGHT_BTN_STYLE = """
        QPushButton {
            min-width: 80px;
            min-height: 52px;
            font-size: 18px;
            font-weight: 600;
            color: #f2f2f2;
            background-color: #555555;
            border: 2px solid #707070;
            border-radius: 8px;
            padding: 4px 8px;
        }
        QPushButton:hover {
            background-color: #666666;
            border-color: #909090;
        }
        QPushButton:pressed {
            background-color: #2f6f91;
            border-color: #5a9fcf;
        }
        """

        backspace_btn = QPushButton("⌫ 退格")
        backspace_btn.setStyleSheet(RIGHT_BTN_STYLE)
        backspace_btn.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        backspace_btn.clicked.connect(lambda: self._send_key("\b"))
        self._enable_long_press(backspace_btn, lambda: self._send_key("\b"))
        right_col.addWidget(backspace_btn, stretch=1)

        del_btn = QPushButton("Del")
        del_btn.setStyleSheet(RIGHT_BTN_STYLE)
        del_btn.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        del_btn.clicked.connect(lambda: self._send_key("\x7f"))
        self._enable_long_press(del_btn, lambda: self._send_key("\x7f"))
        right_col.addWidget(del_btn, stretch=1)

        enter_btn = QPushButton("↵ 回车")
        enter_btn.setStyleSheet(RIGHT_BTN_STYLE)
        enter_btn.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        enter_btn.clicked.connect(lambda: self._send_key("\n"))
        self._enable_long_press(enter_btn, lambda: self._send_key("\n"))
        right_col.addWidget(enter_btn, stretch=1)

        self.space_btn = QPushButton("　空格")
        self.space_btn.setStyleSheet(RIGHT_BTN_STYLE)
        self.space_btn.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self.space_btn.clicked.connect(lambda: self._send_key(" "))
        self._enable_long_press(self.space_btn, lambda: self._send_key(" "))
        right_col.addWidget(self.space_btn, stretch=1)

        content.addLayout(right_col)
        outer_layout.addLayout(content)

        outer_container = QVBoxLayout(self)
        outer_container.setContentsMargins(0, 0, 0, 0)
        outer_container.addWidget(outer)

        self.setFixedWidth(840)

    # ── 按键工厂 ──

    def _make_key(self, text, send_text=None):
        """创建按键，send_text 为实际发送字符（如 & 按钮显示 && 但发送 &）"""
        btn = QPushButton(text)
        btn.setStyleSheet(KEY_STYLE)
        btn.setFocusPolicy(Qt.FocusPolicy.NoFocus)  # 不抢输入框焦点
        btn._real_text = send_text if send_text is not None else text
        btn.clicked.connect(lambda: self._send_key(btn._real_text))
        # 普通按键自动加长按
        self._enable_long_press(btn, lambda: self._send_key(btn._real_text))
        return btn

    @staticmethod
    def _make_ctrl_btn(text, callback):
        """创建控制类按钮（?!(./⇧/Caps等）"""
        btn = QPushButton(text)
        btn.setStyleSheet(CTRL_KEY_STYLE)
        btn.setFocusPolicy(Qt.FocusPolicy.NoFocus)  # 不抢输入框焦点
        btn.clicked.connect(callback)
        return btn

    def _enable_long_press(self, btn, callback):
        """为按键添加长按连续触发功能（按住 LONG_PRESS_DELAY ms 后每 LONG_PRESS_REPEAT ms 重复）
        callback 为点击/重复时执行的 callable（无参数）
        """
        timer = QTimer(btn)
        timer.setSingleShot(True)
        btn._repeat_timer = timer

        def on_pressed():
            timer.setSingleShot(True)
            timer.setInterval(LONG_PRESS_DELAY)
            timer.start()

        def on_released():
            timer.stop()

        def on_timer():
            # 首次超时：进入快速重复模式
            if timer.isSingleShot():
                timer.setSingleShot(False)
                timer.setInterval(LONG_PRESS_REPEAT)
                timer.start()
            callback()

        btn.pressed.connect(on_pressed)
        btn.released.connect(on_released)
        timer.timeout.connect(on_timer)

    # ── 按键发送 ──

    def _send_key(self, text):
        """发送按键到目标控件"""
        if text == "\b":
            self._backspace()
            return
        if text == "\x7f":
            self._delete_forward()
            return
        if self._symbol_mode:
            self._toggle_symbol()

        widget = self._get_target()
        if widget is None:
            return

        if isinstance(widget, QLineEdit):
            cursor = widget.cursorPosition()
            current = widget.text()
            new_text = current[:cursor] + text + current[cursor:]
            widget.setText(new_text)
            widget.setCursorPosition(cursor + len(text))
        elif isinstance(widget, QTextEdit):
            tc = widget.textCursor()
            tc.insertText(text)
            widget.setTextCursor(tc)

        # 上档键：激活一次后自动取消
        if self._shift_on:
            self._toggle_shift()

    def _backspace(self):
        widget = self._get_target()
        if widget is None:
            return

        if isinstance(widget, QLineEdit):
            cursor = widget.cursorPosition()
            current = widget.text()
            if cursor > 0:
                new_text = current[:cursor - 1] + current[cursor:]
                widget.setText(new_text)
                widget.setCursorPosition(cursor - 1)
        elif isinstance(widget, QTextEdit):
            tc = widget.textCursor()
            if not tc.hasSelection():
                pos = tc.position()
                if pos > 0:
                    tc.setPosition(pos - 1)
                    tc.movePosition(QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor)
            tc.removeSelectedText()

    def _delete_forward(self):
        """向前删除（删除光标后的字符）"""
        widget = self._get_target()
        if widget is None:
            return

        if isinstance(widget, QLineEdit):
            cursor = widget.cursorPosition()
            current = widget.text()
            if cursor < len(current):
                new_text = current[:cursor] + current[cursor + 1:]
                widget.setText(new_text)
                widget.setCursorPosition(cursor)
        elif isinstance(widget, QTextEdit):
            tc = widget.textCursor()
            if not tc.hasSelection():
                tc.movePosition(QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor)
            tc.removeSelectedText()

    def _get_target(self):
        """获取当前有效的目标输入控件"""
        if self._target_widget and self._target_widget.hasFocus():
            return self._target_widget
        w = QApplication.focusWidget()
        if isinstance(w, (QLineEdit, QTextEdit)):
            return w
        return None

    # ── Shift / Caps / Symbol 切换 ──

    def _toggle_shift(self):
        self._shift_on = not self._shift_on
        self._apply_shift_caps()
        self.shift_btn.setStyleSheet(ACTIVE_CTRL_STYLE if self._shift_on else CTRL_KEY_STYLE)

    def _toggle_caps(self):
        self._caps_on = not self._caps_on
        self._apply_shift_caps()
        self.caps_btn.setStyleSheet(ACTIVE_CTRL_STYLE if self._caps_on else CTRL_KEY_STYLE)

    def _toggle_symbol(self):
        self._symbol_mode = not self._symbol_mode
        if self._symbol_mode:
            # 进入符号键盘
            # Row 0: 数字行 → SYMBOL_ROW
            for btn, ch, disp in zip(self.num_buttons, SYMBOL_ROW, SYMBOL_ROW_DISPLAY):
                btn.setText(disp)
                btn._real_text = ch
            # Row 1: 字母行 q-p (indices 0-9) → EXTRA_SYMBOLS[0]
            for i, ch in enumerate(EXTRA_SYMBOLS[0]):
                self._letter_buttons[i].setText(ch)
                self._letter_buttons[i]._real_text = ch
            # Row 2: 字母行 a-l (indices 10-18) → EXTRA_SYMBOLS[1][0:9]
            for i, ch in enumerate(EXTRA_SYMBOLS[1][:9]):
                self._letter_buttons[10 + i].setText(ch)
                self._letter_buttons[10 + i]._real_text = ch
            # Row 3: z-m (indices 19-25) → EXTRA_SYMBOLS[1][9] + 后缀
            self._letter_buttons[19].setText(EXTRA_SYMBOLS[1][9])
            self._letter_buttons[19]._real_text = EXTRA_SYMBOLS[1][9]
            for i, suffix in enumerate(COMMON_SUFFIXES):
                self._letter_buttons[20 + i].setText(suffix)
                self._letter_buttons[20 + i]._real_text = suffix

            self.sym_btn.setStyleSheet(ACTIVE_CTRL_STYLE)
            self.sym_btn.setText("ABC")
        else:
            # 退出符号键盘 → 恢复数字和字母
            for btn, ch in zip(self.num_buttons, NUMBER_ROW):
                btn.setText(ch)
                btn._real_text = ch
            # 字母区根据当前 shift/caps 状态恢复
            self._apply_shift_caps()
            self.sym_btn.setStyleSheet(CTRL_KEY_STYLE)
            self.sym_btn.setText("?!(.")

    def _apply_shift_caps(self):
        if self._symbol_mode:
            return  # 符号键盘下不改变字母区显示
        use_shift = self._shift_on != self._caps_on  # XOR
        rows = KEY_ROWS["shift"] if use_shift else KEY_ROWS["normal"]
        flat = [ch for row in rows for ch in row]
        for btn, ch in zip(self._letter_buttons, flat):
            btn.setText(ch)
            btn._real_text = ch

    # ── 窗口拖拽 ──

    def _title_mouse_press(self, event: QMouseEvent):
        if event.button() == Qt.MouseButton.LeftButton:
            self._drag_pos = event.globalPosition().toPoint() - self.frameGeometry().topLeft()

    def _title_mouse_move(self, event: QMouseEvent):
        if event.buttons() & Qt.MouseButton.LeftButton and self._drag_pos is not None:
            self.move(event.globalPosition().toPoint() - self._drag_pos)

    # ── 目标绑定 ──

    def attach(self, widget):
        """绑定到一个输入控件，键盘输入将发送到此控件"""
        self._target_widget = widget

    def detach(self):
        self._target_widget = None

    def show_at(self, x, y):
        """在屏幕坐标 (x, y) 处显示"""
        self.move(x, y)
        self.show()
        self.raise_()

    def show_below(self, widget):
        """显示在指定控件下方"""
        pos = widget.mapToGlobal(QPoint(0, widget.height() + 4))
        screen = QApplication.primaryScreen().availableGeometry()
        if pos.x() + self.width() > screen.width():
            pos.setX(screen.width() - self.width() - 8)
        self.show_at(pos.x(), pos.y())
