from machine import Pin
from neopixel import NeoPixel
import _thread
import time, math
import sys

class LedPatterns:
    def __init__(self, pin, num_leds):
        self.num_leds = num_leds
        self.np = NeoPixel(Pin(pin), num_leds)
        self.run = True
    
    def color_chase(self, color, wait):
        self.run = True
        for i in range(self.num_leds):
            self.np[i] = color
            self.np.write()
            if not run:
                break
            time.sleep(wait)
    
    def wheel(self, pos):
        if pos < 0 or pos > 255:
            return (0, 0, 0)
        if pos < 85:
            return (255 - pos * 3, pos * 3, 0)
        if pos < 170:
            pos -= 85
            return (0, 255 - pos * 3, pos * 3)
        pos -= 170
        return (pos * 3, 0, 255 - pos * 3)
    
    def rainbow_cycle(self, wait):
        run = True
        for j in range(255):
            for i in range(self.num_leds):
                self.np[i] = self.wheel((i * 256 // self.num_leds) + j)
            self.np.write()
            if not run:
                break
            time.sleep(wait)
    
    def breathing(self, color, duration):
        steps = 256
        for _ in range(steps):
            brightness = int(255 * abs(steps / 2 - _) / (steps / 2))
            self.np.fill((brightness * color[0] // 255, brightness * color[1] // 255, brightness * color[2] // 255))
            self.np.write()
            time.sleep(duration / steps)
    
    def color_transition(self, start_color, end_color, duration):
        steps = 256
        for step in range(steps):
            color = (
                int(start_color[0] + (end_color[0] - start_color[0]) * step / steps),
                int(start_color[1] + (end_color[1] - start_color[1]) * step / steps),
                int(start_color[2] + (end_color[2] - start_color[2]) * step / steps),
            )
            self.np.fill(color)
            self.np.write()
            if not self.running():
                return
            time.sleep(duration / steps)
            
    def scanner(self, color, speed):
        position = 0
        direction = 1

        while True:
            self.np.fill((0, 0, 0))
            self.np[position] = color
            self.np.write()
            time.sleep(speed)
            position += direction
            if position == self.num_leds - 1 or position == 0:
                direction *= -1
    
    def bidirectional_scanner(self, color_left, color_right=None, speed=0.1):
        color_right = color_right or color_left
        position_left = 0
        position_right = self.num_leds - 1
        direction_left = 1
        direction_right = -1
        while True:
            self.np.fill((0, 0, 0))
            self.np[position_left] = color_left
            self.np[position_right] = color_right
            self.np.write()
            time.sleep(speed)

            position_left += direction_left
            position_right += direction_right

            if position_left == self.num_leds - 1 or position_left == 0:
                direction_left *= -1
            if position_right == self.num_leds - 1 or position_right == 0:
                direction_right *= -1
    
    def sine_wave_propagation(self, color, speed):
        frequency = 10
        phase = 0
        while True:
            self.np.fill((0, 0, 0)) 
            for i in range(self.num_leds):
                brightness = int(127.5 * (math.sin(frequency * i + phase) + 1))
                self.np[i] = (brightness * color[0] // 255, brightness * color[1] // 255, brightness * color[2] // 255)
            self.np.write()
            time.sleep(speed)
            phase += 0.1

    def fill(self,color):
        for i in range(self.num_leds):
            self.np[i] = color
            self.np.write()