import time
import struct
import logging
import math
import random

logger = logging.getLogger(__name__)

RED    = (0xff, 0x00, 0x00)
ORANGE = (0xff, 0x80, 0x00)
YELLOW = (0xff, 0xff, 0x00)
GREEN  = (0x00, 0xff, 0x00)
CYAN   = (0x00, 0xff, 0xff)
BLUE   = (0x00, 0x00, 0xff)
PURPLE = (0x80, 0x00, 0xff)
VIOLET = (0xff, 0x00, 0xff)
WHITE  = (0xff, 0xff, 0xff)
BLACK  = (0x00, 0x00, 0x00)

COLORS = (RED, ORANGE, YELLOW, GREEN, CYAN, BLUE, PURPLE, VIOLET, WHITE)

class Color(object):
    def __init__(self, r, g, b, a):
        self.r = r
        self.g = g
        self.b = b
        self.a = a
    def __str__(self):
        return "{}({}, {}, {}, {})".format(self.__class__.__name__, self.r, self.g, self.b, self.a)

class Fader(object):
    def __init__(self, start, end, duration):
        self.f = open('/dev/apa102c', 'wb')
        self.duration = duration
        self.start = start
        self.color = start
        self.on_random = False
        self.set_end(end)

    def set_end(self, end):
        self.done = False
        self.start_time = time.time()
        self.end = end
        self.start = self.color
        start = self.start
        self.delta = Color(
            r=end.r - start.r,
            g=end.g - start.g,
            b=end.b - start.b,
            a=end.a - start.a,
            )
        logger.debug("Set end to {}. Delta now {}.".format(self.end, self.delta))

    def step(self):
        now = time.time()
        time_passed = now - self.start_time
        if time_passed > self.duration:
            self.color = self.end
            self.done = True
        else:
            progress = time_passed / self.duration
            start = self.start
            delta = self.delta
            self.color = Color(
                r=start.r + progress * delta.r,
                g=start.g + progress * delta.g,
                b=start.b + progress * delta.b,
                a=start.a + progress * delta.a,
                )
        self.sync()

        if self.done and self.on_random:
            self.set_end(Color(*random.choice(COLORS), 16))

    def sync(self):
        c = self.color
        self.f.write(struct.pack('BBBB', 
            self.clamp(c.r),
            self.clamp(c.g),
            self.clamp(c.b),
            self.clamp(c.a),
            ) * 9)
        self.f.flush()

    def clamp(self, f):
        i = int(math.floor(f))
        if i < 0: i = 0
        elif i > 255: i = 255
        return i

if __name__ == '__main__':
    f = Fader(
        start=Color(0, 0, 0, 16),
        end=Color(0, 0, 255, 16),
        duration=10,
        )
    while True:
        f.set_end(Color(*random.choice(COLORS), 16))
        for s in range(10000):
            f.step()
            if f.done:
                break
            time.sleep(0.1)
