import random
from gpiozero import PWMLED
from rpi_ws281x import Color, PixelStrip, ws
import time
import threading

# ------------------------------------------------------------------------------
# Control script for 3D printer fan & LED control
#
#
# Raspi Zero W Pin assignment, in order from top to bottom on PCB
#
#  On PCB  |  Cable color    |  Raspberry PIN
# --------------------------------------------
#  5V      |  not connected  |  ------
#  LED     |  grey           |  GPIO 19 [PWM]
#  GND     |  black          |  ------
#  DHT22   |  white          |  GPIO 26
#  FAN1    |  purple         |  GPIO 13
#  FAN2    |  blue           |  GPIO 6
#


# LED strip configuration:
LED_COUNT = 120        # Number of LED pixels.
LED_PIN = 19           # GPIO pin connected to the pixels (must support PWM!).
LED_FREQ_HZ = 800000   # LED signal frequency in hertz (usually 800khz)
LED_DMA = 10           # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 255   # Set to 0 for darkest and 255 for brightest
LED_INVERT = False     # True to invert the signal (for NPN transistor)
LED_CHANNEL = 1
LED_STRIP = ws.SK6812_STRIP
LED_SUBSETS = {
    'top_back': list(range(24)),
    'top_mid': list(range(24, 48)),
    'left': list(range(48, 72)),
    'top_front': list(range(72, 96)),
    'right': list(reversed(range(96, 120))),
}
LED_SUBSETS['top'] = LED_SUBSETS['top_back'] + \
    LED_SUBSETS['top_mid'] + LED_SUBSETS['top_front']
LED_SUBSETS['sides'] = LED_SUBSETS['left'] + LED_SUBSETS['right']
LED_SUBSETS['all'] = LED_SUBSETS['top'] + LED_SUBSETS['sides']

LED_SUBSET_NAMES = {
    'top_back': "Oben hinten",
    'top_mid': "Oben mitte",
    'left': "Seite links",
    'top_front': "Oben vorne",
    'right': "Seite rechts",
    'top': "Oben",
    'sides': "Seiten",
    'all': "Alle"
}

leds = PixelStrip(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA,
                  LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP)
leds.begin()


# Fan configuration
PWM_FREQ = 600

venting_fan = PWMLED(13, frequency=PWM_FREQ)
filter_fan = PWMLED(6, frequency=PWM_FREQ)

#
led_thread_running = True
led_thread = None


def normalize_subset(subset):
    if type(subset) is str:
        return LED_SUBSETS[subset]
    else:
        return subset


def next_color_func(r, g, b):
    if type(r) is tuple or type(g) is tuple or type(b) is tuple:
        def next_color():
            return Color(*(random.randint(e[0], e[1]) if isinstance(e, tuple) else e for e in (r, g, b)))
    else:
        def next_color():
            return Color(r, g, b)
    return next_color


def _thread_set_leds_color(r, g, b, subset='all'):
    global led_thread_running
    led_thread_running = True
    next_color = next_color_func(r, g, b)

    for i in normalize_subset(subset):
        leds.setPixelColor(i, next_color())
    leds.show()
    led_thread_running = False


def _thread_set_leds_brightness(new_brightness):
    global led_thread_running
    led_thread_running = True

    leds.setBrightness(new_brightness)
    leds.show()

    led_thread_running = False


def _thread_set_leds_on(subset='all'):
    global led_thread_running
    led_thread_running = True

    changed = False
    for i in normalize_subset(subset):
        if leds.getPixelColor(i) == 0:
            leds.setPixelColor(i, Color(180, 180, 180))
            changed = True
    if changed:
        leds.show()

    led_thread_running = False


def _thread_set_leds_off(subset='all'):
    _thread_set_leds_color(0, 0, 0, subset=subset)


def _thread_effect_color_wipe(r, g, b, wait_ms=25, subset='all'):
    """Wipe color across display a pixel at a time."""
    global led_thread_running

    next_color = next_color_func(r, g, b)

    _thread_set_leds_off(subset)
    led_thread_running = True
    while led_thread_running:
        for i in normalize_subset(subset):
            leds.setPixelColor(i, next_color())
            leds.show()
            if not led_thread_running:
                break
            time.sleep(wait_ms / 1000.0)
        for i in reversed(normalize_subset(subset)):
            leds.setPixelColor(i, Color(0, 0, 0))
            leds.show()
            if not led_thread_running:
                break
            time.sleep(wait_ms / 1000.0)


def _thread_effect_blink_on_off(r, g, b, wait_ms=1000, subset='all'):
    global led_thread_running
    next_color = next_color_func(r, g, b)

    _thread_set_leds_off(subset)
    led_thread_running = True

    while led_thread_running:
        for i in normalize_subset(subset):
            leds.setPixelColor(i, next_color())
        leds.show()
        time.sleep(wait_ms / 1000.0)

        if not led_thread_running:
            break

        for i in normalize_subset(subset):
            leds.setPixelColor(i, Color(0, 0, 0))
        leds.show()
        time.sleep(wait_ms / 1000.0)


def _thread_effect_move_blink(r, g, b, wait_ms=500, subset='all'):
    global led_thread_running
    next_color = next_color_func(r, g, b)

    _thread_set_leds_off(subset)
    led_thread_running = True

    ctr = 0
    while led_thread_running:
        for nr, led_id in enumerate(normalize_subset(subset)):
            color = next_color() if nr % 2 == ctr % 2 else Color(0, 0, 0)
            leds.setPixelColor(led_id, color)
        leds.show()
        ctr += 1
        time.sleep(wait_ms / 1000.0)


def _start_in_led_thread(func, *args, **kwargs):
    global led_thread_running
    global led_thread
    led_thread_running = False
    if led_thread is not None:
        led_thread.join()
    led_thread = threading.Thread(target=func, args=args, kwargs=kwargs)
    led_thread.start()


# ---------------------- Settings Lights ---------------------------------


def set_leds_color(r, g, b, subset='all'):
    _start_in_led_thread(_thread_set_leds_color, r, g, b, subset)


def set_leds_on(subset='all'):
    if not led_thread_running:
        _start_in_led_thread(_thread_set_leds_on, subset)


def set_leds_off(subset='all'):
    _start_in_led_thread(_thread_set_leds_off, subset)


def set_leds_brightness(new_brightness):
    _start_in_led_thread(_thread_set_leds_brightness, new_brightness)


def effect_color_wipe(r, g, b, wait_ms=25, subset='all'):
    _start_in_led_thread(_thread_effect_color_wipe, r, g, b, wait_ms, subset)


def effect_move_blink(r, g, b, wait_ms=500, subset='all'):
    _start_in_led_thread(_thread_effect_move_blink, r, g, b, wait_ms, subset)


def effect_blink_on_off(r, g, b, wait_ms=1000, subset='all'):
    _start_in_led_thread(_thread_effect_blink_on_off, r, g, b, wait_ms, subset)
