import cv2
import numpy as np
import threading
import time

#quick sort function
def quicksort(array):
	"""Quick sort function for 1D array"""
	if len(array) <= 1:
		return array
	pivot = array[len(array)//2]
	left = [x for x in array if x < pivot]
	middle = [x for x in array if x == pivot]
	right = [x for x in array if x > pivot]
	return quicksort(left) + middle + quicksort(right)

#row sort function for threads
def sort_rows(start_row, end_row, frame, thread_id):
	"""Sort rows assigned to this thread"""
	for row_index in range(start_row, end_row):
		frame[row_index] = quicksort(frame[row_index])
	print(f"Thread {thread_id} finished rows {start_row}-{end_row-1}")

#function to process video
def process_video(video_file, output_file, num_threads):
	cap = cv2.VideoCapture(video_file)
	if not cap.isOpened():
		print("Error: Cannot open video file")
		return 0 #if failed

	#video properties
	frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
	frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
	fps = cap.get(cv2.CAP_PROP_FPS)
	total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

	#video writer
	fourcc = cv2.VideoWriter_fourcc(*'mp4v')
	out = cv2.VideoWriter(output_file, fourcc, fps, (frame_width, frame_height))
	frame_count = 0
	start_time = time.time()

	#process each frame
	while True:
		ret, frame = cap.read()
		if not ret:
			break

		frame_count += 1
		print(f"Processing frame {frame_count}/{total_frames}")

		#converts to grayscale
		gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

		#split rows for threads
		rows_per_thread = frame_height // num_threads
		threads = []
		for i in range(num_threads):
			start_row = i * rows_per_thread
			end_row = (i+1)*rows_per_thread if i != num_threads-1 else frame_height
			t = threading.Thread(target=sort_rows, args=(start_row, end_row, gray_frame, i+1))
			threads.append(t)
			t.start()

		#wait for all threads
		for t in threads:
			t.join()

		#convert back to BGR to save video
		sorted_frame = cv2.cvtColor(gray_frame, cv2.COLOR_GRAY2BGR)
		out.write(sorted_frame)

	end_time = time.time()
	total_time = end_time - start_time

	cap.release()
	out.release()

	print(f"Finished processing {frame_count} frames in {total_time:.2f} seconds")
	print(f"Threads used: {num_threads}")
	return total_time

#main
if __name__ == "__main__":
	video_file = "2a.mp4"
	output_file_seq = "output_sequential.mp4"
	output_file_threads = "output_threads.mp4"

	print("\n=== Sequential processing 1 thread ===")
	sequential_time = process_video(video_file, output_file_seq, num_threads=1)
	print("\n=== Threaded processing 4 threads ===")
	threaded_time = process_video(video_file, output_file_threads, num_threads=4)

	#performance metrics
	speedup = sequential_time / threaded_time if threaded_time > 0 else 0
	efficiency = speedup / 4 #cause 4 threads were used

	print("\n=== Performance metrics ===")
	print(f"Sequential time: {sequential_time:.2f} seconds")
	print(f"Threaded time: {threaded_time:.2f} seconds")
	print(f"Speedup: {speedup:.2f}")
	print(f"Efficiency: {efficiency:.2f}")

	
