import tkinter as tk
import cv2
from pathlib import Path
from PIL import Image, ImageTk
from ImageContainer import ImageContainer
from utils.face_utils import detect_faces_bgr
from utils.filters import apply_filters


class CameraWindow:

    def __init__(self):
        self.name = "Camera"
        self.category = "Utility"
        self.cap = None
        self.top = None
        self.preview_label = None
        self.prev_face = None
        self.last_filtered = None
        self.idx = 0
        self.title_var = None
        self.controls_var = None

        project_root = Path(__file__).resolve().parents[1]
        assets = project_root.parent / "assets"

        self.params = {
            "face_blur": {"enlarge": 0.45, "feather": 45, "blur_ksize": 41},
            "hat": {"asset_path": str(assets / "cowboy_hat.png"), "scale": 1.15, "head_clearance": 0.12},
            "glasses": {"asset_path": str(assets / "glasses.png"), "scale": 1.05, "y_factor": 0.38},
            "mustache": {"asset_path": str(assets / "moustache.png"), "scale": 0.8, "y_factor": 0.72, "center_y": True},
        }

        self.filters = ["none", "face_blur", "hat", "glasses", "mustache"]

    def getManipulationName(self) -> str:
        return "Camera Window"

    def run(self, app):
        self.app = app
        self.root = app._root
        self._open_camera()

    def _open_camera(self):
        self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
        if not self.cap or not self.cap.isOpened():
            print("Could not open camera")
            return

        self.top = tk.Toplevel(self.root)
        self.top.title("Camera")
        self.top.configure(bg="#0f1115")
        self.top.protocol("WM_DELETE_WINDOW", self._close)
        self.top.bind("<Key>", self._on_key)

        container = tk.Frame(self.top, bg="#0f1115")
        container.pack(fill="both", expand=True, padx=12, pady=12)

        header = tk.Frame(container, bg="#0f1115")
        header.pack(fill="x", pady=(0, 8))
        self.title_var = tk.StringVar(value="Camera Active: None")
        title = tk.Label(header, textvariable=self.title_var, fg="#eaeef2", bg="#0f1115", font=("Segoe UI", 12, "bold"))
        title.pack(side="left")

        body = tk.Frame(container, bg="#0f1115")
        body.pack(fill="both", expand=True)
        preview_wrap = tk.Frame(body, bg="#1b1f26", bd=1, relief="ridge")
        preview_wrap.pack(fill="both", expand=True)
        self.preview_label = tk.Label(preview_wrap, bg="#000000")
        self.preview_label.pack(fill="both", expand=True)

        footer = tk.Frame(container, bg="#0f1115")
        footer.pack(fill="x", pady=(8, 0))
        self.controls_var = tk.StringVar(value="Left/Right: Switch   Enter: Capture   Q: Quit")
        controls = tk.Label(footer, textvariable=self.controls_var, fg="#aab4bf", bg="#0f1115", font=("Segoe UI", 10))
        controls.pack(side="left")
        self._update_frame()

    def _on_key(self, event):
        k = event.keysym
        if k in ("q", "Q", "Escape"):
            self._close()
        elif k == "Left":
            self.idx = (self.idx - 1) % len(self.filters)
        elif k == "Right":
            self.idx = (self.idx + 1) % len(self.filters)
        elif k in ("Return", "KP_Enter"):
            self._capture_to_app()
            self._close()

    def _update_frame(self):
        ok, frame = self.cap.read()
        if not ok:
            self._close()
            return

        current = self.filters[self.idx]
        names = [] if current == "none" else [current]

        faces = detect_faces_bgr(frame)
        ctx = {}
        if len(faces):
            self.prev_face = self._smooth_box(self.prev_face, tuple(faces[0]), 0.6)
            ctx["faces"] = [self.prev_face]
        else:
            self.prev_face = None

        filtered = apply_filters(frame, names, self.params, ctx=ctx)
        self.last_filtered = filtered
        if self.title_var is not None:
            self.title_var.set(f"Camera Filter: {self._pretty(current)}")

        preview_bgr = self._resize_for_preview(filtered, max_w=960, max_h=720)
        rgb = cv2.cvtColor(preview_bgr, cv2.COLOR_BGR2RGB)
        imgtk = ImageTk.PhotoImage(Image.fromarray(rgb))
        self.preview_label.imgtk = imgtk
        self.preview_label.configure(image=imgtk)

        self.top.after(10, self._update_frame)


    def _capture_to_app(self):
        if self.last_filtered is None:
            return
        rgb = cv2.cvtColor(self.last_filtered, cv2.COLOR_BGR2RGB)
        pil_img = Image.fromarray(rgb)
        if self.app._currentImage is None:
            self.app._currentImage = ImageContainer()
        self.app._currentImage._imageData = pil_img
        self.app._currentImage._history = []
        self.app._renderCurrentImage()

    def _close(self):
        try:
            if self.cap:
                self.cap.release()
        except Exception:
            pass
        try:
            if self.top:
                self.top.destroy()
        except Exception:
            pass

    @staticmethod
    def _smooth_box(prev, box, alpha=0.6):
        if prev is None:
            return box
        px, py, pw, ph = prev
        x, y, w, h = box
        return (
            int(px * alpha + x * (1 - alpha)),
            int(py * alpha + y * (1 - alpha)),
            int(pw * alpha + w * (1 - alpha)),
            int(ph * alpha + h * (1 - alpha)),
        )

    @staticmethod
    def _resize_for_preview(img_bgr, max_w=960, max_h=720):
        h, w = img_bgr.shape[:2]
        scale = min(max_w / max(w, 1), max_h / max(h, 1), 1.0)
        if scale >= 0.999:
            return img_bgr
        new_w, new_h = int(w * scale), int(h * scale)
        return cv2.resize(img_bgr, (new_w, new_h), interpolation=cv2.INTER_AREA)

    @staticmethod
    def _pretty(name):
        names = {
            "none": "None",
            "face_blur": "Background Blur",
            "hat": "Hat",
            "glasses": "Glasses",
            "mustache": "Mustache",
        }
        return names.get(name, name)
