# By Dan Hagon (@axiomsofchoice)
# 05/06/2022 at a rainy EMFCamp
# See: https://2022.badge.emfcamp.org/projects/its_raining_at_emfcamp2022
# 24/07/2022 at a fast drying MCH2022 ¯\_(ツ)_/¯
# See: https://mch2022.badge.team/projects/maycontainh2o
# MIT License

import mch22
import buttons
import display
import virtualtimers as vt

from math import sin, cos, pi, sqrt, atan2
from random import uniform, randrange

from .bno055 import init as acc_init
from .bno055 import get_accel

class Particle():

    def __init__(self, disp_w, disp_h, cur_acc, max_speed=5., lifetime=10):
        self.position = (int(uniform(0., disp_w)), int(uniform(0., disp_h)))
        # Speed in pixels per tick (where tick is typically 100ms)
        speed = uniform(.75 * max_speed, max_speed)

        # Make sure the generated direction is within a sector of the circle
        norm = sqrt(cur_acc[0] * cur_acc[0] + cur_acc[1] * cur_acc[1])
        cur_dir = (cur_acc[0] / norm, cur_acc[1] / norm)
        rad_dir = uniform(atan2(cur_dir[0], cur_dir[1]) - (0.1 * pi),
                          atan2(cur_dir[0], cur_dir[1]) + 0.1 * pi)
        self.velocity = (speed * sin(rad_dir), speed * cos(rad_dir))

        self.lifetime = randrange(lifetime // 2, lifetime)
        self.life_line = []

    def update_pos(self, accel):
        self.position = (self.position[0] + self.velocity[0], self.position[1] + self.velocity[1])
        self.velocity = (self.velocity[0] + accel[0], self.velocity[1] + accel[1])
        self.lifetime -= 1
        self.life_line.append(self.position)

    def draw(self, ):
        if len(self.life_line) > 1:
            for i in range(len(self.life_line) - 1):
                display.drawLine(int(self.life_line[i][0]),
                          int(self.life_line[i][1]),
                          int(self.life_line[i + 1][0]),
                          int((self.life_line[i + 1][1])),
                          # MCH2022 Yellow
                          0xF9C74F)
        display.drawLine(int(self.pos[0]),
                  int(self.pos[1]),
                  int(self.pos[0] + self.vel[0]),
                  int((self.pos[1] + self.vel[1])),
                  # MCH2022 Light blue
                  0x56B5A0)

    @property
    def pos(self):
        return self.position

    @property
    def vel(self):
        return self.velocity

    @property
    def is_live(self):
        return self.lifetime > 0


class ParticlesApp():

    def seed_particles(self, w, h, curr_acc, max_particles=18):
        if self.particle_state is None:
            self.particle_state = []

        num_parts = len(self.particle_state)
        self.particle_state.extend([Particle(w, h, curr_acc) for _ in range(max_particles - num_parts)])

    def reboot(self, pressed):
        if pressed:
            vt.delete(self.animation_tick)
            vt.stop()

            display.windowRemove(self.window_name)
            mch22.exit_python()

    def animation_tick(self):

        # MCH2022 Purple
        display.drawFill(0x532C85)

        # Get acceleromter value
        raw_acc = get_accel()
        f_acc = (float(raw_acc[0]), float(raw_acc[1]), float(raw_acc[2]))
        denom = sqrt(f_acc[0] * f_acc[0] + f_acc[1] * f_acc[1] + f_acc[2] * f_acc[2])
        normalised = (f_acc[0] / denom, f_acc[1] / denom, f_acc[2] / denom)
        gravity = 9.8
        accel_vals = (normalised[0] * gravity, normalised[1] * gravity, normalised[2] * gravity)
        # accel_vals = (0.0, -9.8)

        # Update particle state
        for p in self.particle_state:
            p.update_pos(accel_vals)

        # Remove the dead particles
        self.particle_state = [p for p in self.particle_state if p.is_live]

        # If necessary add new particles
        self.seed_particles(self.w, self.h, accel_vals)

        # Draw particles
        for p in self.particle_state:
            p.draw()

        display.flush()

        return self.tick_period

    def on_activate(self, tick_period=10):

        self.window_name = "maycontainh2o"

        self.w = display.width()
        self.h = display.height()

        display.windowCreate(self.window_name,
                            self.w,
                            self.h)

        self.particle_state = []

        acc_init()

        buttons.attach(buttons.BTN_A, self.reboot)

        self.tick_period = tick_period
        vt.new(self.tick_period, self.animation_tick)
        vt.begin()


app = ParticlesApp()
app.on_activate()