import cv2
import numpy as np
import time

def create_logo():
    logo = np.zeros((64, 64, 3), dtype=np.uint8)
    logo.fill(255)
    cv2.circle(logo, (32, 32), 30, (0, 0, 255), -1)
    cv2.rectangle(logo, (16, 16), (48, 48), (255, 0, 0), -1)
    pts = np.array([[32, 8], [16, 48], [48, 48]], np.int32).reshape((-1, 1, 2))
    cv2.fillPoly(logo, [pts], (0, 255, 0))
    hex_pts = np.array([[32, 5], [42, 15], [42, 35], [32, 45], [22, 35], [22, 15]], np.int32).reshape((-1, 1, 2))
    cv2.polylines(logo, [hex_pts], True, (0, 255, 255), 2)
    return logo

def load_watermarks(img_path_1, img_path_2, target_size):
    img_1 = cv2.imread(img_path_1, cv2.IMREAD_COLOR)
    img_2 = cv2.imread(img_path_2, cv2.IMREAD_COLOR)
    if img_1 is None or img_2 is None:
        raise ValueError("Error loading watermark images")
    img_1 = cv2.resize(img_1, target_size)
    img_2 = cv2.resize(img_2, target_size)
    return img_1, img_2

def create_watermark_masks(images):
    masks = []
    for img in images:
        hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        mask = cv2.inRange(hsv_img, np.array([0, 0, 0]), np.array([50, 50, 50]))
        mask_inv = cv2.bitwise_not(mask)
        masks.append((mask, mask_inv))
    return masks

def apply_watermark(frame, watermark, mask, mask_inv):
    background = cv2.bitwise_and(frame, frame, mask=mask)
    foreground = cv2.bitwise_and(watermark, watermark, mask=mask_inv)
    return cv2.add(foreground, background)

def adjust_brightness(frame, beta):
    return np.clip(frame.astype(np.float32) + beta, 0, 255).astype(np.uint8)

def apply_geometric_mean_filter(frame, ksize):
    frame_float = frame.astype(np.float32) / 255.0
    frame_log = np.log(frame_float + 1e-10)
    mean_log = cv2.boxFilter(frame_log, ddepth=-1, ksize=(ksize, ksize))
    return (np.exp(mean_log) * 255).astype(np.uint8)

def calculate_average_brightness(frame, ksize):
    smoothed_frame = apply_geometric_mean_filter(frame, ksize)
    return np.mean(cv2.cvtColor(smoothed_frame, cv2.COLOR_BGR2GRAY))

def detect_faces(frame, cascade):
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    return cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=7, minSize=(20, 20))

def blur_faces(frame, faces, kernel_size):
    for (x, y, w, h) in faces:
        frame[y:y+h, x:x+w] = cv2.GaussianBlur(frame[y:y+h, x:x+w], kernel_size, 30)
    return frame

def apply_fade(frame, alpha):
    return cv2.addWeighted(frame, alpha, np.zeros_like(frame), 1 - alpha, 0)

def process_video():
    # Initialize video captures
    talking_vid = cv2.VideoCapture("talking.mp4")
    bg_vid = cv2.VideoCapture("office.mp4")
    end_vid = cv2.VideoCapture("endscreen.mp4")

    # Check if videos opened successfully
    if not talking_vid.isOpened() or not bg_vid.isOpened() or not end_vid.isOpened():
        raise ValueError("Error opening video files")

    # Video parameters
    w, h = 1280, 720
    resize_width, resize_height = 540, 320
    fps = bg_vid.get(cv2.CAP_PROP_FPS)

    # Initialize video writer
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter('Final_Processed_Video.mp4', fourcc, fps, (w, h))

    # Load and prepare resources
    logo = create_logo()
    watermark1, watermark2 = load_watermarks("watermark1.png", "watermark2.png", (w, h))
    watermark_masks = create_watermark_masks([watermark1, watermark2])
    face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")

    # Green screen parameters
    lower_green = np.array([40, 100, 100])
    upper_green = np.array([80, 255, 255])

    # Brightness parameters
    ksize_brightness = 5
    is_day_video = calculate_average_brightness(cv2.resize(bg_vid.read()[1], (w, h)), ksize_brightness) >= 90

    # Watermark parameters
    current_watermark = 0
    switch_interval = 5
    start_time = time.time()

    # Video processing stages
    stage = 'fade_in_bg'
    fade_frames = int(fps * 2)  # 2 seconds fade
    frame_count = 0

    while True:
        if stage == 'fade_in_bg':
            ret, bg_frame = bg_vid.read()
            if not ret:
                break
            bg_frame = cv2.resize(bg_frame, (w, h))
            alpha = min(1.0, frame_count / fade_frames)
            frame = apply_fade(bg_frame, alpha)
            frame_count += 1
            if frame_count >= fade_frames:
                stage = 'main_processing'
                frame_count = 0

        elif stage == 'main_processing':
            ret1, talking_frame = talking_vid.read()
            ret2, bg_frame = bg_vid.read()
            
            if not ret1:
                stage = 'fade_out_bg'
                continue
            if not ret2:
                bg_vid.set(cv2.CAP_PROP_POS_FRAMES, 0)
                ret2, bg_frame = bg_vid.read()

            # Resize frames
            bg_frame = cv2.resize(bg_frame, (w, h))
            talking_frame = cv2.resize(talking_frame, (w, h))

            # Face detection and blurring
            faces = detect_faces(bg_frame, face_cascade)
            bg_frame = blur_faces(bg_frame, faces, (15, 15))

            # Green screen removal
            hsv_frame = cv2.cvtColor(talking_frame, cv2.COLOR_BGR2HSV)
            mask = cv2.inRange(hsv_frame, lower_green, upper_green)
            mask_inv = cv2.bitwise_not(mask)
            foreground = cv2.bitwise_and(talking_frame, talking_frame, mask=mask_inv)
            foreground = cv2.resize(foreground, (resize_width, resize_height))
            mask_inv = cv2.resize(mask_inv, (resize_width, resize_height))

            # Overlay foreground on background
            roi = bg_frame[h - resize_height:h, 0:resize_width]
            bg_roi = cv2.bitwise_and(roi, roi, mask=cv2.bitwise_not(mask_inv))
            combined_roi = cv2.add(bg_roi, foreground)
            bg_frame[h - resize_height:h, 0:resize_width] = combined_roi

            # Brightness adjustment
            avg_brightness = calculate_average_brightness(bg_frame, ksize_brightness)
            if is_day_video and avg_brightness > 35:
                bg_frame = adjust_brightness(bg_frame, -50)
            elif not is_day_video and avg_brightness < 110:
                bg_frame = adjust_brightness(bg_frame, 50)

            # Watermark application
            if time.time() - start_time >= switch_interval:
                current_watermark = 1 - current_watermark
                start_time = time.time()
            mask, mask_inv = watermark_masks[current_watermark]
            bg_frame = apply_watermark(bg_frame, [watermark1, watermark2][current_watermark], mask, mask_inv)

            # Logo overlay
            logo_h, logo_w = logo.shape[:2]
            bg_frame[10:10 + logo_h, w - 10 - logo_w:w - 10] = logo

            frame = bg_frame

        elif stage == 'fade_out_bg':
            ret, bg_frame = bg_vid.read()
            if not ret:
                stage = 'fade_in_end'
                frame_count = 0
                continue
            bg_frame = cv2.resize(bg_frame, (w, h))
            alpha = max(0.0, 1 - (frame_count / fade_frames))
            frame = apply_fade(bg_frame, alpha)
            frame_count += 1

        elif stage == 'fade_in_end':
            ret, end_frame = end_vid.read()
            if not ret:
                break
            end_frame = cv2.resize(end_frame, (w, h))
            alpha = min(1.0, frame_count / fade_frames)
            frame = apply_fade(end_frame, alpha)
            frame_count += 1
            if frame_count >= fade_frames:
                stage = 'play_end'

        elif stage == 'play_end':
            ret, end_frame = end_vid.read()
            if not ret:
                stage = 'fade_out_end'
                frame_count = 0
                continue
            frame = cv2.resize(end_frame, (w, h))

        elif stage == 'fade_out_end':
            ret, end_frame = end_vid.read()
            if not ret:
                break
            end_frame = cv2.resize(end_frame, (w, h))
            alpha = max(0.0, 1 - (frame_count / fade_frames))
            frame = apply_fade(end_frame, alpha)
            frame_count += 1

        out.write(frame)
        cv2.imshow("Processed Video", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    talking_vid.release()
    bg_vid.release()
    end_vid.release()
    out.release()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    process_video()