# SPDX-FileCopyrightText: 2023 Cedar Grove Maker Studios
# SPDX-License-Identifier: MIT

# TV Backlight
# Feather M4 + AS7341 Color Sensor + NeoPixel strip

import board
import math
from simpleio import map_range
from adafruit_as7341 import AS7341, Gain
import neopixel

strip = neopixel.NeoPixel(board.D12, 30, brightness=1.0)
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.5)

# Instantiate and set up the spectrometer sensor
i2c = board.I2C()  # uses board.SCL and board.SDA
sensor = AS7341(i2c)

# The integration time step size in 2.78µs increments (JP 128, 999 max)
sensor.astep = 256
# The integration time step count (JP 50, 255 max)
sensor.atime = 50
# The internal gain setting (JP 256, 512 max)
sensor.gain = Gain.GAIN_512X
# The breakout board's LED brightness (JP 5, ? max)
sensor.led_current = 0
# Disable the LED
sensor.led = False

# Calculate the sensor channel count value maximum (13,107)
MAX_COUNT = (sensor.astep + 1) * (sensor.atime + 1)

# The color matching tolerance index; 0 to 104,8856 (8 * max_sensor_count)
TOLERANCE = 40


def convert_count_to_rgb(count_tuple):
    """Converts the 8-channel sensor measurement count tuple into RGB888 for
    use by a NeoPixel."""
    r8 = int(map_range(count_tuple[7], 0, MAX_COUNT, 0, 255))
    g8 = int(map_range(count_tuple[4], 0, MAX_COUNT, 0, 255))
    b8 = int(map_range(count_tuple[2], 0, MAX_COUNT, 0, 255))
    return r8, g8, b8


# The initial starting color (black)
previous_color = [0, 0, 0, 0, 0, 0, 0, 0]

while True:
    # Get counts from the eight visible channels
    target_color = sensor.all_channels[0:8]
    # Light up the on-board NeoPixel with the target color
    pixel[0] = convert_count_to_rgb(target_color)

    # Create list of channel deltas using list comprehension
    channel_deltas = [
        (target_color[idx] - count) for idx, count in enumerate(previous_color)
    ]
    # Resolve squared deltas to a Euclidean difference
    color_distance = math.sqrt(sum([d**2 for d in channel_deltas]))
    # Check fof a color match
    color_match = bool(color_distance <= TOLERANCE)

    # Determine the step sizes needed to move from the previous color to the
    # target color
    if not color_match:
        # Create a list of step sizes for each channel if colors aren't matched
        steps = [8 * (d / color_distance) for d in channel_deltas]
    else:
        # Create a list of zero-sized steps if colors are matched
        steps = [0 for d in channel_deltas]  # create a list of zero-sized steps

    # Calculate a new previous color by adding a step to the previous previous_color
    previous_color = [color + steps[idx] for idx, color in enumerate(previous_color)]

    # Light up the neopixel strip with the new previous color
    strip.fill(convert_count_to_rgb(previous_color))