############################################
# Created on 1-10-2013. Miguel Angel Astor #
############################################
import math
import random

import pygame

import math_utils
import imloader

class Particle(pygame.sprite.Sprite):
    def __init__(self, lifespan, scale, texture, gravity = [0.0, 9.8], position = [0,0], initial_vel = [100.0, 100.0], friction = 1.0, frame_rate = 60.0):
        pygame.sprite.Sprite.__init__(self)

        self.age = 0
        self.lifespan = lifespan
        self.gravity = [gravity[0] / frame_rate, gravity[1] / frame_rate]
        self.position = position
        self.velocity = [float(initial_vel[0]) / frame_rate, float(initial_vel[1]) / frame_rate] # Pixels per frame.
        self.friction = friction
        self.size = (int(float(texture.get_width()) * scale), int(float(texture.get_height()) * scale))
        self.alive = True
        self.frame_rate = frame_rate

        self.then = pygame.time.get_ticks()

        self.image = pygame.transform.smoothscale(texture, self.size)
        self.rect = self.image.get_rect()
        self.rect.center = (self.position[0], self.position[1])

        self.screen_w = pygame.display.Info().current_w
        self.screen_h = pygame.display.Info().current_h

    def is_alive(self):
        return self.alive

    def kill(self):
        self.alive = False

    def set_gravity(self, gravity):
        self.gravity = list(gravity)
    
    def update(self):
        if self.alive:
            if self.age >= self.lifespan:
                self.alive = False
                return None

            now = pygame.time.get_ticks()
            delta_t = now - self.then
            
            self.position[0] += (self.velocity[0] * delta_t) * (self.frame_rate / 1000.0)
            self.position[1] += (self.velocity[1] * delta_t) * (self.frame_rate / 1000.0)

            self.velocity[0] *= self.friction
            self.velocity[1] *= self.friction

            self.position[0] += (self.gravity[0] * delta_t) * (self.frame_rate / 1000.0)
            self.position[1] += (self.gravity[1] * delta_t) * (self.frame_rate / 1000.0)
        
            self.rect.center = (self.position[0], self.position[1])

            self.age += 1  
            self.then = now

            if self.rect.center[0] < 0 - self.rect.width or self.rect.center[0] > self.screen_w + self.rect.width:
                self.kill()
                return None
            if self.rect.center[1] < 0 - self.rect.height or self.rect.center[1] > self.screen_h + self.rect.height:
                self.kill()
                return None

    def draw(self, canvas):
        if self.alive:
            canvas.blit(self.image, self.rect)
        

class ParticleSystem:
    def __init__(self, id, name, texture_filename, lifespan = 1000, max_particles = 1000, parts_per_second = 25, angle = 0):
        self.id = id
        self.name = name
        self.lifespan = lifespan
        self.max_particles = max_particles
        self.ppms = float(parts_per_second) / 1000.0 # Particles per milisecond.
        self.mspp = float(1000.0 / parts_per_second) # Miliseconds per particle.
        self.angle = angle
        self.working = False
        self.particles = set()
        self.texture = imloader.cached_image_loader.get_image_to_screen_percent(texture_filename)
        self.part_creation_accum = 0.0
        self.then = pygame.time.get_ticks()

        self.gravity = [0.0, 9.8]
        self.position = [pygame.display.Info().current_w / 2, pygame.display.Info().current_h / 2]
        self.initial_velocity_max = 50 # Pixels per second.
        self.friction = 0.99
        self.frame_rate = 60.0

        self.rectangle = pygame.Rect(0, 0, 25, 25)
        self.rectangle.center = (self.position[0], self.position[1])

        random.seed(None)

    def is_working(self):
        return self.working

    def start(self):
        self.working = True

    def stop(self):
        self.working = False

    def set_position(self, position):
        self.position = list(position)

    def set_gravity(self, gravity):
        self.gravity = list(gravity)

    def set_max_velocity(self, max_vel):
        self.initial_velocity_max = max_vel

    def set_angle(self, angle):
        self.angle = angle

    def set_friction(self, friction):
        self.friction = friction

    def update(self):
        # Calculate the time delta.
        now = pygame.time.get_ticks()
        delta_t = now - self.then

        # Eliminate dead particles.
        remove_set = set()
        for particle in self.particles:
            if not particle.is_alive():
                remove_set.add(particle)
        self.particles.difference_update(remove_set)

        if self.working:            
            # Create new particles if possible.
            if len(self.particles) < self.max_particles:
                max_parts = self.max_particles - len(self.particles)
                self.part_creation_accum += (self.ppms * delta_t)
                parts_needed = int(self.part_creation_accum // 1) 
                if parts_needed >= 1:
                    for i in range(parts_needed):
                        velocity = [float(random.randrange(-self.initial_velocity_max, self.initial_velocity_max)),
                                    float(random.randrange(-self.initial_velocity_max, self.initial_velocity_max))]
                        particle = Particle(
                            int(self.lifespan),
                            max(min(random.random(), 1.0), 0.2),
                            self.texture,
                            list(self.gravity),
                            list(self.position),
                            velocity,
                            self.friction,
                            int(self.frame_rate))
                        self.particles.add(particle)
                    self.part_creation_accum = 0.0

        # Update every particle.
        for particle in self.particles:
            particle.update()
        
        # Restart the time counter.
        self.then = now

    def draw(self, canvas):
        for particle in self.particles:
            particle.draw(canvas)
