import cv2
import sys
import json
import time
import base64
import hashlib
import numpy as np
import pyzbar.pyzbar as pyzbar

from qrtransfert.constants import DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT

def decode(file, correction_threshold, fps_to_decode, debug):
   
    cap = cv2.VideoCapture(file)

    if not cap.isOpened():
        print(f"Erreur : Impossible to open file '{file}'")
        return []

    original_fps = cap.get(cv2.CAP_PROP_FPS)
    if original_fps == 0:
        print("Warning: Unable to get video framerate. Processing all frames.")
        frames_to_skip = 0
    else:
        frames_to_skip = int(original_fps / fps_to_decode)
        if frames_to_skip < 1:
            frames_to_skip = 1
        print(f"Original framerate of the video : {original_fps:.2f} FPS")
        print(f"Processing at approximately {fps_to_decode} FPS. Will skip {frames_to_skip - 1} frame between each processing")

    decoded_data_set = set()
    decoded_data_list = None
    filename = None
    src_checksum = None
    frame_count = 0

    debug_windows_name = "Debug - Press 'Q' to exit"
    cv2.namedWindow(debug_windows_name, cv2.WINDOW_NORMAL)
    cv2.resizeWindow(debug_windows_name, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)

    while True:
        ret, frame = cap.read()

        if not ret:
            break

        frame_count += 1

        if frame_count % frames_to_skip == 0 or frames_to_skip == 0:
            
            rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

            r_frame = np.zeros_like(rgb_frame)
            r_only = rgb_frame[:, :, 0]
            r_frame[:, :, 0] = r_only
            r_frame[:, :, 1] = r_only
            r_frame[:, :, 2] = r_only
            r_frame = cv2.cvtColor(r_frame, cv2.COLOR_BGR2GRAY)

            g_frame = np.zeros_like(rgb_frame)
            g_only = rgb_frame[:, :, 1]
            g_frame[:, :, 0] = g_only
            g_frame[:, :, 1] = g_only
            g_frame[:, :, 2] = g_only
            g_frame = cv2.cvtColor(g_frame, cv2.COLOR_BGR2GRAY)

            b_frame = np.zeros_like(rgb_frame)
            b_only = rgb_frame[:, :, 2]
            b_frame[:, :, 0] = b_only
            b_frame[:, :, 1] = b_only
            b_frame[:, :, 2] = b_only
            b_frame = cv2.cvtColor(b_frame, cv2.COLOR_BGR2GRAY)

            for f in (r_frame, g_frame, b_frame):
                # Clean image
                _, clean_frame = cv2.threshold(f, correction_threshold, 255, cv2.THRESH_BINARY)

                if debug:
                    resized_clean_frame = cv2.resize(clean_frame, (DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT), interpolation=cv2.INTER_AREA)
                    cv2.imshow(debug_windows_name, resized_clean_frame)
                    time.sleep(0.3)
                    if cv2.waitKey(10) & 0xFF == ord('q'):
                        sys.exit(0)
                else:
                    decoded_objects = pyzbar.decode(clean_frame, symbols=[pyzbar.ZBarSymbol.QRCODE])
                    for obj in decoded_objects:

                        sequence_number = int.from_bytes(obj.data[0:2], byteorder='big')
                        total_sequences = int.from_bytes(obj.data[2:4], byteorder='big')

                        # Initialize array with good size
                        if decoded_data_list is None:
                            decoded_data_list = [None] * (total_sequences -1)

                        # Speedup process
                        if sequence_number != total_sequences:
                            if decoded_data_list[int(sequence_number-1)] is not None:
                                continue
                        else:
                            if filename is not None:
                                continue

                        if None not in decoded_data_list and filename is not None:
                            break

                        print(f"Got part: {sequence_number}/{total_sequences}")

                        decoded_data = obj.data[4:].decode('utf-8')
                        
                        if decoded_data not in decoded_data_set:
                            decoded_data_set.add(decoded_data)

                            if sequence_number == total_sequences:
                                decoded_data = base64.b64decode(decoded_data)
                                json_data = json.loads(decoded_data)
                                filename = json_data['filename'] if json_data['filename'] else "noname"
                                src_checksum = json_data['checksum'] if json_data['checksum'] else "000"
                            else:
                                decoded_data_list[(sequence_number-1)] = decoded_data
    
    if not debug:
        cap.release()
        cv2.destroyAllWindows()

        if decoded_data_list:
            nb_parts = sum(1 for item in decoded_data_list if item is not None)
            nb_missing_parts = int(total_sequences-nb_parts-1)
            if nb_missing_parts > 0:
                print(f"Missing some part of the file ({nb_missing_parts})")
            else:
                base64_string = ''.join(decoded_data_list)
                binary_data = base64.b64decode(base64_string)
                checksum = hashlib.md5(binary_data).hexdigest()
                with open(filename, 'wb') as f:
                    f.write(binary_data)
                print(f"File '{filename}' successfully wrote")
                if checksum != src_checksum:
                    print("WARNING: the checksum is not the same that the original file")

