"""
This library is intended to be used with the streamlined versions of some units
in the Engineer Your World (EYW) Computing curriculum. The library places some
of the most difficult to understand code from the rigorous versions of the units
(notably several OpenCV and Numpy functions) inside of more intuitive functions.
   _____________________________
  |                             |
  |--- DO NOT EDIT THIS FILE ---|
  |_____________________________|

Written by Engineer Your World, University of Texas at Austin
Updated in Spring 2025
"""
import cv2
import numpy

def create_mask (image, min_value, max_value):
    """
    This funtion cuts and keeps all of the pixels from an image (must be a
    3-channel image) that have values within the range of the given min and max
    values. Next it assigns all of the other pixels a value of 0 (black), then
    returns a mask that can be used to block every pixel location with a value
    of 0.
    """
    # Ensure that the min and max values do not go over 255. Values of 256 or
    # more cause a "256 out of bounds" OverflowError that freezes the program
    # when it tries to convert into the proper data type below.
    if min_value[0] > 255:
        min_value[0] = 255
    if min_value[1] > 255:
        min_value[1] = 255
    if min_value[2] > 255:
        min_value[2] = 255
    if max_value[0] > 255:
        max_value[0] = 255
    if max_value[1] > 255:
        max_value[1] = 255
    if max_value[2] > 255:
        max_value[2] = 255
    # Convert the min and max values to the proper data type: unsigned 8-bit
    # integer.
    min_value = numpy.array(min_value, dtype = "uint8")
    max_value = numpy.array(max_value, dtype = "uint8")
    # Create a mask that only allows the pixels in the desired range through.
    mask = cv2.inRange(image, min_value, max_value)
    # Return the mask, which is made up of pure black and pure white pixels.
    return mask

def create_colored_paper (original_image, b,g,r):
    """
    This function creates a new image with the same dimensions as the original
    image (in the first argument) where every pixel has the color determined by
    the second, third, and fourth (b,g,r) arguments.
    """
    # Determine the proper image dimensions.
    image_height = original_image.shape[0]
    image_width = original_image.shape[1]
    image_channels = original_image.shape[2]
    # Create a new image with the same dimensions, with all bgr values = zero.
    colored_paper = numpy.zeros((image_height,image_width,image_channels),
                                numpy.uint8)
    # Re-assign every pixel in the new image the new desired bgr values.
    colored_paper[0:image_height,0:image_width, 0:image_channels] = [b,g,r]
    # Return the new colored paper.
    return colored_paper  

def apply_mask (image, mask):
    """
    Creates a new image where all of the pixels that are not blocked by the mask
    remain the same as the original image (first argument), and the blocked
    pixels are now all black. 
    """
    # Change the value of every pixel covered by the mask to zero, keep the
    # rest of the pixels as they are.
    filtered_image = cv2.bitwise_or(image,image, mask=mask)
    # Return the resulting masked image.
    return filtered_image

def combine_images (image1, image2):
    """
    Adds two images together. This function simply replaces the confusing
    bitwise_or terminology. Note that if both images have non-zero b,g,r values
    at the same pixel location (which shouldn't happen in typical use scenarios
    in the EYW curriculum) the larger value for b, g, and/or r will be used to
    color that pixel location. This is due to using bitwise_or instead of
    bitwise_and (which would use the smaller b,g,r values).
    """
    # Combine the two images together into one image.
    combined_image = cv2.bitwise_or(image1, image2)
    # Return the newly combined image.
    return combined_image
