import time
from arena_api.system import system
from arena_api.buffer import BufferFactory
import ctypes
import numpy as np
import cv2
import json

# Global variables for ROI selection
drawing = False
start_point = None
end_point = None
roi_selected = False
image_copy = None

# Load configuration from a file
def load_config(config_path):
    with open(config_path, 'r') as file:
        config = json.load(file)
    return config

# Function to create devices with retries
def create_devices_with_tries():
    tries, tries_max, sleep_time_secs = 0, 6, 10
    while tries < tries_max:
        devices = system.create_device()
        if not devices:
            print(f'Try {tries+1} of {tries_max}: Waiting for {sleep_time_secs} secs for a device...')
            time.sleep(sleep_time_secs)
            tries += 1
        else:
            print(f'{len(devices)} device(s) created.')
            return devices
    raise Exception('No device found! Please connect a device and try again.')

# Function to configure the camera's exposure settings
def configure_exposure(nodes, exposure_time):
    nodes['ExposureAuto'].value = 'Off'  # Disable automatic exposure
    if not nodes['ExposureTime'] or not nodes['ExposureTime'].is_writable:
        raise Exception("Exposure Time node not available or not writable")
    
    # Set exposure time within allowed range
    exposure_time = max(min(exposure_time, nodes['ExposureTime'].max), nodes['ExposureTime'].min)
    nodes['ExposureTime'].value = exposure_time
    print(f"Exposure time set to {nodes['ExposureTime'].value} us")

# Mouse callback function for ROI selection
def click_and_drag(event, x, y, flags, param):
    global drawing, start_point, end_point, roi_selected, image, image_copy

    if event == cv2.EVENT_LBUTTONDOWN and not roi_selected:
        drawing = True
        start_point = (x, y)

    elif event == cv2.EVENT_MOUSEMOVE and drawing:
        end_point = (x, y)
        temp_image = image.copy()
        cv2.rectangle(temp_image, start_point, end_point, (0, 255, 0), 2)
        cv2.imshow("Captured Image", temp_image)

    elif event == cv2.EVENT_LBUTTONUP and drawing:
        drawing = False
        end_point = (x, y)
        roi_selected = True
        cv2.rectangle(image, start_point, end_point, (0, 255, 0), 2)
        cv2.imshow("Captured Image", image)
        print(f"Selected ROI: Top-Left: {start_point}, Bottom-Right: {end_point}")

    elif event == cv2.EVENT_RBUTTONDOWN and roi_selected:
        print("Clearing selection. You can select a new ROI.")
        image = image_copy.copy()
        cv2.imshow("Captured Image", image)
        roi_selected = False

def calculate_ROI(image, DUT_Camera_width_pixel, DUT_Camera_height_pixel, Tray_Cavity_width_pixel, Tray_Cavity_height_pixel, new_width, DUT_ROI_Top_left, DUT_ROI_Bottom_right):

    # Resize the image to a width of 1600 pixels, maintaining the aspect ratio
    # original_height, original_width = image.shape[:2]
    # new_height = int((new_width / original_width) * original_height)
    # resized_image = cv2.resize(image, (new_width, new_height))

    # # Select ROI on the resized image
    # roi = cv2.selectROI("Select Checkerboard ROI", resized_image, fromCenter=False, showCrosshair=True)
    # cv2.destroyWindow("Select Checkerboard ROI")  # Close the ROI selection window

    # # Calculate the scaling factor
    # scale_x = original_width / new_width
    # scale_y = original_height / new_height

    # # Get the coordinates of the ROI on the original image
    # x, y, w, h = map(int, roi)
    # x_original = int(x * scale_x)
    # y_original = int(y * scale_y)
    #DUT_ROI = [x_original, y_original,(x_original + Tray_Cavity_width_pixel), (y_original + Tray_Cavity_height_pixel)]
    x_original = DUT_ROI_Top_left[0]
    y_original = DUT_ROI_Top_left[1]
    DUT_ROI = [x_original, y_original,(x_original + Tray_Cavity_width_pixel), (y_original + Tray_Cavity_height_pixel)]
    DUT_Bottom_Camera_ROI = [abs((x_original + Tray_Cavity_width_pixel) - DUT_Camera_width_pixel), abs((y_original + Tray_Cavity_height_pixel) - DUT_Camera_height_pixel), abs(x_original + Tray_Cavity_width_pixel), abs(y_original + Tray_Cavity_height_pixel)]
    DUT_Top_Camera_ROI = [x_original, y_original, (x_original + DUT_Camera_width_pixel), (y_original + DUT_Camera_height_pixel)]
    return DUT_Top_Camera_ROI, DUT_Bottom_Camera_ROI, DUT_ROI

# Main entry point of the program
def example_entry_point():
    global image, image_copy, start_point, end_point
    # Global variables for ROI selection
    drawing = False
    start_point = None
    end_point = None
    roi_selected = False
    image_copy = None
    config_path = 'C:/Program Files/CCD/HMI_config.json'
    config = load_config(config_path)
    # Load values from the configuration
    exposure_time = config["exposure_time"]
    resized_width = (config["resized_width"])
    DUT_Camera_width_pixel = config["DUT_Camera_width_pixel"]
    DUT_Camera_height_pixel = config["DUT_Camera_height_pixel"]
    Tray_Cavity_width_pixel = config["Tray_Cavity_width_pixel"]
    Tray_Cavity_height_pixel = config["Tray_Cavity_height_pixel"]
    DUT_ROI_Top_left = tuple(config["DUT_ROI_Top_left"])
    DUT_ROI_Bottom_right = tuple(config["DUT_ROI_Bottom_right"])

    devices = create_devices_with_tries()
    device = system.select_device(devices)
    nodemap = device.nodemap

    # Configure exposure settings
    configure_exposure(nodemap, exposure_time)

    # Capture an image for ROI selection
    with device.start_stream():
        buffer = device.get_buffer()
        item = BufferFactory.copy(buffer)
        device.requeue_buffer(buffer)

        buffer_bytes_per_pixel = len(item.data) // (item.width * item.height)
        array = (ctypes.c_ubyte * 1 * item.width * item.height).from_address(ctypes.addressof(item.pbytes))
        npndarray = np.ndarray(buffer=array, dtype=np.uint8, shape=(item.height, item.width, buffer_bytes_per_pixel))
        # Calculate the distance
        # Calculate the distance
        try:
            DUT_Top_Camera_ROI, DUT_Bottom_Camera_ROI, DUT_ROI= calculate_ROI(npndarray, DUT_Camera_width_pixel, DUT_Camera_height_pixel, Tray_Cavity_width_pixel, Tray_Cavity_height_pixel, resized_width, DUT_ROI_Top_left, DUT_ROI_Bottom_right)
            new_values = {"DUT_Top_Camera_ROI": tuple(DUT_Top_Camera_ROI), "DUT_Bottom_Camera_ROI": tuple(DUT_Bottom_Camera_ROI), "DUT_ROI": tuple(DUT_ROI)}
            # Open the JSON file and update the specific values
            with open(config_path, 'r+') as file:
                data = json.load(file) # Read the existing data    
                data.update(new_values) # Update only the specified parameters        
                file.seek(0) # Move the cursor to the beginning of the file before writing       
                json.dump(data, file, indent=4) # Write the updated data back to the file            
                file.truncate()# Optionally truncate the file to ensure no leftover data
            #print(f"Distance to checkerboard: {distance:.2f} mm")
        except ValueError as e:
            print(f"Error: {e}")

            print("Completed")
        
    cv2.destroyAllWindows()

    # Revert to initial exposure settings (if needed)
    system.destroy_device()
    print("Destroyed all created devices")

if __name__ == '__main__':
    print("\nStarted\n")
    example_entry_point()
    print("\nFinished Successfully")
 explain this