'''
License
-------
No Copyright

The person who associated a work with this deed has dedicated the work to the
public domain by waiving all of his or her rights to the work worldwide under
copyright law, including all related and neighboring rights, to the extent
allowed by law.

You can copy, modify, distribute and perform the work, even for commercial
purposes, all without asking permission.

See https://creativecommons.org/share-your-work/public-domain/cc0/ for more
information on the license.


Project
-------
This file is part of the wedding pictogram project, made by
Maarten Tromp <maarten@geekabit.nl>.

More info on the project:
https://www.geekabit.nl/projects/wedding-gift-sprite/
'''


from machine import Pin
from neopixel import NeoPixel
import time
import random
#import network

import config # board configuration 

# global vars
l = 24			# number of led pixels
maxBrightness = 10	# max brighness
np = NeoPixel(Pin(15, Pin.OUT), l)	# create array of pixels
button = Pin(0, Pin.IN, Pin.PULL_UP)

# vars for outlineRunner
pix_loc = [-2] * 6
pix_col = [0] * 6
pix_speed = [1] * 6


# common functions
def wheel(wheelPos):
	# colour wheel: red -> orange -> yellow -> green -> blue -> purple -> red
	# input: wheelPos = 0..255
	# output: r, g, b values
	wheelPos = wheelPos % 256
	r = 0
	g = 0
	b = 0
	if(wheelPos < 42):
		# red - orange
		i = wheelPos / 42
		r = maxBrightness
		g = int(i * maxBrightness / 4)
		return r, g, b
	if(wheelPos < 84):
		# orange - yellow
		wheelPos -= 42
		i = wheelPos / 42
		r = maxBrightness
		g = int((1 - i) * maxBrightness / 4 + i * 3 / 4 * maxBrightness)
		return r, g, b
	if(wheelPos < 127):
		# yellow - green
		wheelPos -= 84
		i = wheelPos / 42
		r = int((1 - i) * maxBrightness)
		g = int((1 - i) * 3 / 4 * maxBrightness + i * maxBrightness)
		return r, g, b
	if(wheelPos < 170):
		# green - blue
		wheelPos -= 127
		i = wheelPos / 42
		g = int((1 - i) * maxBrightness)
		b = int(i * maxBrightness)
		return r, g, b
	if(wheelPos < 212):
		# blue - purple
		wheelPos -= 170
		i = wheelPos / 42
		r = int(i * maxBrightness)
		b = maxBrightness
		return r, g, b
	# purple - red
	wheelPos -= 212
	r = maxBrightness
	b = int((1 - wheelPos / 42) * maxBrightness)
	return r, g, b


# demos
# parameters:
#	distance: 0 = close, 1=halfway, 2=far
#	ticks: counter
# return values:
#	done: true when this demo is complete

def ledTest(d, t):
	if(d == 0):
		# close -> 7 colours
		colours = [[maxBrightness, 0, 0], [0, maxBrightness, 0], [0, 0, maxBrightness], [maxBrightness, maxBrightness // 4, 0], [maxBrightness, maxBrightness, 0], [maxBrightness, 0, maxBrightness], [0, maxBrightness, maxBrightness]]
	if(d == 1):
		# halfway -> whitish color, black
		#colours = [[maxBrightness, 0, 0], [maxBrightness, maxBrightness, maxBrightness], [0, 0, 0], [maxBrightness, maxBrightness, 0], [maxBrightness, maxBrightness, maxBrightness], [0, 0, 0], [0, 0, maxBrightness], [maxBrightness, maxBrightness, maxBrightness], [0, 0, 0]]
		colours = [[maxBrightness, maxBrightness // 4, maxBrightness // 4], [0, 0, 0], [maxBrightness, maxBrightness, maxBrightness // 4], [0, 0, 0], [maxBrightness // 4, maxBrightness // 4, maxBrightness], [0, 0, 0]]
	if(d == 2):
		# far -> white, black
		colours = [[maxBrightness, maxBrightness, maxBrightness], [0, 0, 0]]

	colourIndex = (t // l) % len(colours)
	step = t % l

	np[step] = colours[colourIndex]
	if(colourIndex == len(colours) - 1 and step == l - 1):
		return True


def whiteOverRainbow(d, t):
	# inspired by particle xmas tree demo
	# https://github.com/particle-iot/xmastree/blob/master/src/firmware/animations.h
	whiteLength = 3
	rainbowSpeed = 4

	if(d == 0):
		# close
		maxRounds = 4
	if(d == 1):
		# halfway
		maxRounds = 3
	if(d == 2):
		# far
		maxRounds = 2

	headPos = (whiteLength - 1 + t) % l
	tailPos = t % l
	rainbowPos = (rainbowSpeed * t) % 256
	rounds = t / l
	for i in range(l):
		if ((i >= tailPos and i <= headPos) or (tailPos > headPos and i >= tailPos) or (tailPos > headPos and i <= headPos)):
			np[i] = (maxBrightness, maxBrightness, maxBrightness)
		else:
			pos = int(256 * (i / l + rainbowPos / 256))
			r, g, b = wheel(pos)
			if(d == 0):
				# close -> rainbow background
				np[i] = (r, g, b)
			if(d == 1):
				# halfway -> dimmed rainbow background
				np[i] = (r // 4, g // 4, b // 4)
			if(d == 2):
				# far -> black background
				np[i] = (0, 0, 0)
	if(rounds > maxRounds):
		return True


def chasingPixels(d, t):
	speeds = [-8, -5, -3, -2, -1, 1, 2, 3, 5, 8]
	outline = [1, 2, 3, 4, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 20, 21, 22, 23]

	# on first tick, init global vars
	# does not seem to work though
#	if(t == 0):
#		pix_loc = [-2] * 6 # current location
#		pix_col = [0, 0, 0] * 6 # colour
#		pix_speed = [1] * 6 # number of ticks to wait between steps

	if(d == 0):
		# close -> 7 colours
		numPix = 4
		maxTicks = 200
		colours = [[maxBrightness, 0, 0], [0, maxBrightness, 0], [0, 0, maxBrightness], [maxBrightness, maxBrightness // 4, 0], [maxBrightness, maxBrightness, 0], [maxBrightness, 0, maxBrightness], [0, maxBrightness, maxBrightness]]
	if(d == 1):
		# halfway -> 3x colour, 3x white
		numPix = 3
		maxTicks = 150
		colours = [[maxBrightness, 0, 0], [maxBrightness, maxBrightness, maxBrightness], [maxBrightness, maxBrightness, 0], [maxBrightness, maxBrightness, maxBrightness], [0, 0, maxBrightness], [maxBrightness, maxBrightness, maxBrightness]]
	if(d == 2):
		# far -> white
		numPix = 2
		maxTicks = 100
		colours = [[maxBrightness, maxBrightness, maxBrightness]]

	# clear all leds
	for i in range(l):
		np[i] = (0, 0, 0)

	# advance pixels
	for p in range(numPix):
		# test if it's time for next step
		if(t % pix_speed[p] == 0):
			# find out direction
			if(pix_speed[p] > 0):
				pix_loc[p] += 1
			else:
				pix_loc[p] -= 1
			# test for bounds
			if(pix_loc[p] < 0 or pix_loc[p] > len(outline)):
				# test if we're done
				if(t > maxTicks):
					return True
				# re-initialize pixel
				pix_speed[p] = random.choice(speeds)
				if(pix_speed[p] > 0):
					pix_loc[p] = 0 # start at bottom
				else:
					pix_loc[p] = len(outline) # start at top
				pix_col[p] = random.randrange(len(colours))
		# draw pixels
		for i in range(len(outline)):
			if(pix_loc[p] == i):
				pix_col[p] %= len(colours) # distance might have changed
				# add pixel colour to alreacy existing led colour
				r = min(np[outline[i]][0] + colours[pix_col[p]][0], maxBrightness)
				g = min(np[outline[i]][1] + colours[pix_col[p]][1], maxBrightness)
				b = min(np[outline[i]][2] + colours[pix_col[p]][2], maxBrightness)
				np[outline[i]] = (r, g, b)


def fill(d, t):
	# orders differ a bit between bride and groom
	if(config.isGroom == True):
		vert = [[12, 13], [11], [10, 14], [9, 15], [6, 18], [8, 16], [5, 19], [0, 7, 17], [4, 20], [3, 21], [2, 22], [1, 23]] # 12 long
		horiz = [[7, 8, 9], [1, 2, 3, 4, 5, 6, 10, 12], [0, 11], [13, 14, 18, 19, 20, 21, 22, 23], [15, 16, 17]] # 5 long
	else:
		vert = [[12, 13], [11], [10, 14], [9, 15], [6, 18], [8, 16], [5, 19], [7, 17], [4, 20], [3, 21], [0, 2, 22], [1, 23]] # 12 long
		horiz = [[2, 7], [3, 8], [4, 9], [1, 5, 6, 10, 12], [0, 11], [13, 14, 18, 19, 23], [15, 20], [16, 21], [17, 22]] # 9 long
	# same colours as ledTest
	if(d == 0):
		# close -> 7 colours
		maxTicks = 250
		colours = [[maxBrightness, 0, 0], [0, maxBrightness, 0], [0, 0, maxBrightness], [maxBrightness, maxBrightness // 4, 0], [maxBrightness, maxBrightness, 0], [maxBrightness, 0, maxBrightness], [0, maxBrightness, maxBrightness]]
	if(d == 1):
		# halfway -> color, white, black
		maxTicks = 200
		colours = [[maxBrightness, 0, 0], [maxBrightness, maxBrightness, maxBrightness], [0, 0, 0], [maxBrightness, maxBrightness, 0], [maxBrightness, maxBrightness, maxBrightness], [0, 0, 0], [0, 0, maxBrightness], [maxBrightness, maxBrightness, maxBrightness], [0, 0, 0]]
	if(d == 2):
		# far -> white, black
		maxTicks = 150
		colours = [[maxBrightness, maxBrightness, maxBrightness], [0, 0, 0]]
	random = [124, 140, 79, 19, 25, 113, 135, 130, 202, 148, 6] # https://xkcd.com/221/
	step = t % 30
	colourIndex = t // 30 % len(colours)
	dir = random[t // 30 % len(random)] % 4
	line = []
	if(dir == 0):
		# top to bottom
		if(step < len(vert)):
			line = vert[step]
		if(step > 19):
			t += 10
	if(dir == 1):
		# bottom to top
		if(step < len(vert)):
			line = vert[len(vert) - step - 1]
		if(step > 19):
			t += 10
	if(dir == 2):
		# left to right
		if(config.isServer):
			# server shows step 0..9
			if(step < len(horiz)):
				line = horiz[step]
		else:
			# client shows step 10..19
			if(step > 9):
				step -= 10
				if(step < len(horiz)):
					line = horiz[step]
	if(dir == 3):
		# right to left
		if(config.isServer):
			# server shows step 0..9
			if(step > 9):
				step -= 10
				if(step < len(horiz)):
					line = horiz[len(horiz) - 1 - step]
		else:
			# client shows step 10..19
			if(step < len(horiz)):
				line = horiz[len(horiz) - 1 - step]
	for pixel in line:
		np[pixel] = colours[colourIndex]
	if(t > maxTicks and step == 19):
		return True


def pinkTwinkle(d, t):
	# inspired by pink wedding invitation
	# set base colour
	if(d == 0):
		# close -> pink
		r = maxBrightness
		g = int(maxBrightness / 2.5)
		b = int(maxBrightness / 2.5)
	return True


def boringBreathe():
	wait = 50
	while True:
		for j in range(2 * maxBrightness - 1):
			if (j < maxBrightness + 1):
				v = j
			else:
				v = 2 * maxBrightness - j
			for i in range(l):
				np[i] = (v, v, v)
			np.write()
			time.sleep_ms(wait)


#def defeest():
	# 2 blue and 5 yellow pixels


#def l():
	# make [L] sign: blue arm and upper body blue, rest yellow


# could do fire effect


# main function
def main():
	# init
	distance = 0 # 0=close, 1=halfway, 2=far
	ticks = 0
	demos = {
		0: whiteOverRainbow,
		1: chasingPixels,
		2: fill,
		3: ledTest,
	}
	demoIndex = 0
#	demoDone = True
	lastButtonValue = 1

#	# configure Wifi
#	if(config.isServer == True):
#		# server
#		ap = network.WLAN(network.AP_IF)
#		ap.active(True)
#		ap.config(essid='Groom', authmode=network.AUTH_WPA_WPA2_PSK, password='jer03n&m1ngm1ng')
#		ap.ifconfig(('192.168.42.1', '255.255.255.0', '192.168.42.1', '192.168.42.1'))
#	else:
#		# client
#		sta_if = network.WLAN(network.STA_IF)
#		sta_if.active(True)

	# main loop
	while True:
		# read button
		buttonValue = button.value()
		if(buttonValue == 0 and lastButtonValue == 1):
			distance = (distance + 1) % 3
		lastButtonValue = buttonValue

		# call demo
		demoDone = demos[demoIndex](distance, ticks)
		np.write()
		ticks += 1

		if(demoDone):
			# advance to next demo
			demoIndex = (demoIndex + 1) % len(demos)
			ticks = 0

#			if(config.isServer == True):
#				# test is client is connected
#				if(ap.isconnected == True):
#					distance = 0
#				else:
#					distance = 2
#				
#			else:
#				# set leds to pink during wifi scan
#				for i in range(l):
#					np[i] = (maxBrightness, int(maxBrightness / 2.5), int(maxBrightness / 2.5))
#				np.write()
#
#				sta_if.disconnect()
#				# scan for accesspoints
#				aps = sta_if.scan()
#				aps = list(filter(lambda x: x[0] == b'Groom', aps))
#				if len(aps) > 0:
#					# ap found
##					d = aps[0][3]
#					distance = 0
#				else:
#					# ap not found
#					distance = 2
#				# (re)connect to ap
#				sta_if.connect("Groom", "jer03n&m1ngm1ng")

		# sleep is distance dependant
		if(distance == 0):
			sleep = 50
		if(distance == 1):
			sleep = 100
		if(distance == 2):
			sleep = 200
		time.sleep_ms(sleep)

main()


# to indent block in vim: use shift-v to select lines and then '<' and '>' to increment/decrement indentation
# to comment use ctrl-v to select lines and shift-i # to add a '#' character
# to uncomment use ctrl-v to select and x to remove '#'

# to break micropython execution, use `screen /dev/ttyUSB0 115200' and send ctrl+c. Minicom does not work. Quit screen with ctrl-a \


# end of file
