import sys, os, cv2, numpy as np
from pathlib import Path
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QAction, QImage, QPixmap, QDragEnterEvent, QDropEvent
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QLabel, QPushButton, QFileDialog,
    QVBoxLayout, QHBoxLayout, QSplitter, QTabWidget, QFormLayout, QSpinBox, QCheckBox
)

IMAGE_EXT = {".jpg", ".jpeg", ".png", ".bmp", ".gif"}
VIDEO_EXT = {".mp4", ".mov", ".avi", ".mkv", ".m4v"}

def bgr_to_qpixmap(bgr):
    rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
    h, w, ch = rgb.shape
    qimg = QImage(rgb.data, w, h, ch*w, QImage.Format.Format_RGB888)
    return QPixmap.fromImage(qimg)

class MediaViewer(QWidget):
    """画像と動画を共通で扱う左側ビューワ"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.label = QLabel("Drop files here\n(画像/動画)")
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setStyleSheet("background:#222;color:#aaa;border:1px solid #444;")
        self.label.setMinimumSize(640, 360)

        self.btn_bar = QHBoxLayout()
        self.btn_open = QPushButton("Open")
        self.btn_play = QPushButton("Play")
        self.btn_pause = QPushButton("Pause")
        self.btn_stop = QPushButton("Stop")
        for b in (self.btn_open, self.btn_play, self.btn_pause, self.btn_stop):
            self.btn_bar.addWidget(b)
        self.btn_bar.addStretch()

        lay = QVBoxLayout(self)
        lay.addWidget(self.label, 1)
        lay.addLayout(self.btn_bar)

        # 動画用
        self.timer = QTimer(self)
        self.timer.timeout.connect(self._next_frame)
        self.cap = None
        self.current_path = None
        self._is_video = False

        # イベント
        self.btn_open.clicked.connect(self.open_file)
        self.btn_play.clicked.connect(self.play)
        self.btn_pause.clicked.connect(self.pause)
        self.btn_stop.clicked.connect(self.stop)

        # D&D 有効化
        self.setAcceptDrops(True)

    # ---- Public API ----
    def load(self, path: str):
        self.stop()
        self.current_path = path
        suffix = Path(path).suffix.lower()

        if suffix in IMAGE_EXT:
            self._is_video = False
            data = np.fromfile(path, dtype=np.uint8)
            img = cv2.imdecode(data, cv2.IMREAD_COLOR)
            if img is None:
                self.label.setText("画像を開けませんでした")
                return
            self.label.setPixmap(bgr_to_qpixmap(img).scaled(
                self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
        elif suffix in VIDEO_EXT:
            self._is_video = True
            self.cap = cv2.VideoCapture(path)
            if not self.cap.isOpened():
                self._is_video = False
                self.label.setText("動画を開けませんでした")
                return
            self.play()
        else:
            self.label.setText("未対応の形式です")

    def open_file(self):
        path, _ = QFileDialog.getOpenFileName(self, "Open media", "", 
                                              "Media (*.jpg *.jpeg *.png *.bmp *.gif *.mp4 *.mov *.avi *.mkv *.m4v)")
        if path:
            self.load(path)

    def play(self):
        if not self._is_video:
            return
        if self.cap is None:
            return
        # 30fps程度で更新（必要に応じて調整）
        self.timer.start(33)

    def pause(self):
        self.timer.stop()

    def stop(self):
        self.timer.stop()
        if self.cap is not None:
            self.cap.release()
            self.cap = None

    # ---- Internal ----
    def _next_frame(self):
        if self.cap is None:
            return
        ok, frame = self.cap.read()
        if not ok:
            self.pause()
            return
        self.label.setPixmap(bgr_to_qpixmap(frame).scaled(
            self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    # ---- D&D ----
    def dragEnterEvent(self, e: QDragEnterEvent):
        if e.mimeData().hasUrls():
            e.acceptProposedAction()

    def dropEvent(self, e: QDropEvent):
        urls = e.mimeData().urls()
        if not urls:
            return
        path = urls[0].toLocalFile()
        if os.path.isfile(path):
            self.load(path)

    # リサイズ時もアスペクト保持で表示更新
    def resizeEvent(self, ev):
        super().resizeEvent(ev)
        if self.current_path and not self._is_video and self.label.pixmap():
            # 静止画のみスケールし直し
            self.open_image_again()

    def open_image_again(self):
        if not self.current_path or self._is_video:
            return
        data = np.fromfile(self.current_path, dtype=np.uint8)
        img = cv2.imdecode(data, cv2.IMREAD_COLOR)
        if img is None:
            return
        self.label.setPixmap(bgr_to_qpixmap(img).scaled(
            self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))


class RightPanel(QWidget):
    """右側：プレビュー/設定のタブ"""
    def __init__(self, parent=None):
        super().__init__(parent)
        tabs = QTabWidget()

        # Analyser タブ(解析)
        self.analyser = QWidget()
        a_lay = QVBoxLayout(self.analyser)
        # ボタンを追加
        a_lay.addWidget(QPushButton("Run Analysis"))
        a_lay.addStretch()

        # Preview タブ（分析結果の簡易表示など）
        self.preview = QWidget()
        pv_lay = QVBoxLayout(self.preview)
        pv_lay.addWidget(QLabel("分析プレビュー（ここに結果サムネ/メトリクス等）"))
        pv_lay.addStretch()

        # Settings タブ（しきい値や切替など）
        self.settings = QWidget()
        st_form = QFormLayout(self.settings)
        self.spin_conf = QSpinBox(); self.spin_conf.setRange(10, 90); self.spin_conf.setValue(35)
        self.chk_boxes = QCheckBox("検知枠を表示"); self.chk_boxes.setChecked(True)
        st_form.addRow("信頼度(%)", self.spin_conf)
        st_form.addRow(self.chk_boxes)

        tabs.addTab(self.analyser, "Analyser")
        tabs.addTab(self.settings, "Settings")
        tabs.addTab(self.preview, "Preview")

        lay = QVBoxLayout(self)
        lay.addWidget(tabs)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("CV Studio (Prototype)")
        self.resize(1200, 700)

        self.viewer = MediaViewer()
        self.right = RightPanel()

        splitter = QSplitter()
        splitter.addWidget(self.viewer)
        splitter.addWidget(self.right)
        # 半々に分割
        splitter.setSizes([self.width()//2, self.width()//2])

        # 2.1
        # splitter.setStretchFactor(0, 3)
        # splitter.setStretchFactor(1, 2)
        
        self.setCentralWidget(splitter)

        self._build_menu()
        self.setAcceptDrops(True)

    def _build_menu(self):
        open_act = QAction("Open...", self)
        open_act.triggered.connect(self.viewer.open_file)
        self.menuBar().addMenu("File").addAction(open_act)

    # ルートウィンドウにもD&D（フォルダから直接落とせるように）
    def dragEnterEvent(self, e: QDragEnterEvent):
        if e.mimeData().hasUrls():
            e.acceptProposedAction()

    def dropEvent(self, e: QDropEvent):
        urls = e.mimeData().urls()
        if urls:
            path = urls[0].toLocalFile()
            if os.path.isfile(path):
                self.viewer.load(path)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec())
