package de.frajul.particlelab.entities.particles;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import de.frajul.particlelab.data.Vector;
import de.frajul.particlelab.entities.particles.attributes.attributeValues.SpawnShape;
import de.frajul.particlelab.rendering.Lock;
import de.frajul.particlelab.rendering.Timer;

/**
 * Created by Julian on 02.08.2016.
 */
public class ParticleSource {

    private Vector position;
    private ParticleEffect effect;
    private Random random;

    private boolean alife;
    private float currentDelay;
    private float lifePercent;
    private float maxTime = -1;
    private float passedTime;
    private float emittPause;
    private float passedEmittPause;
    private float passedSecond;
    private float windStrength;
    private float gravityStrength;
    private Vector spawnSize = null;

    private ParticleData particleData;
    private List<Particle> activeParticles = new ArrayList<>();
    private List<Particle> inactiveParticles = new ArrayList<>();
    private Lock activeParticleLock = new Lock();

    public ParticleSource(Vector position, ParticleEffect effect) {
        this.position = position;
        this.effect = effect;
        random = effect.getRandom();
        effect.addSource(this);
    }

    public void start() {
        alife = true;
        currentDelay = effect.getDelay().createValue(random, 0);
        maxTime = effect.getDuration().createValue(random, 0) + currentDelay;
        passedTime = 0;
        emittPause = calcEmittPause();
        passedEmittPause = 0;
        passedSecond = 0;
        lifePercent = 0;
    }

    public void update(Timer timer) {
        if (alife) {
            passedTime += timer.getFrameTime();
            lifePercent = passedTime / maxTime;

            if (passedTime >= currentDelay) {
                passedEmittPause += timer.getFrameTime();
                calcWindAndGravity();
                while (passedEmittPause >= emittPause) {
                    passedEmittPause -= emittPause;
                    emitt();
                }

                passedSecond += timer.getFrameTime();
                if (passedSecond >= 1000) {
                    passedSecond -= 1000;
                    calcEmittPause();
                }
            }
            if (passedTime >= maxTime)
                die();
        }
        updateParticles(timer);
    }

    private void updateParticles(Timer timer) {
        activeParticleLock.lock();
        Iterator<Particle> iter = activeParticles.iterator();
        while (iter.hasNext()) {
            Particle particle = iter.next();
            particle.update(timer, windStrength, gravityStrength);
            if (!particle.isActive()) {
                inactiveParticles.add(particle);
                iter.remove();
            } else {
                particle.move(new Vector(particle.getMovement()).mul(timer.getFrameTime() / 1000));
            }
        }
        activeParticleLock.unlock();
    }

    private void die() {
        alife = false;
        if (effect.getOptions().isContinuous())
            start();
    }

    public void emitt() {
        if (particleData == null)
            particleData = effect.createParticleData();
        float xOff = effect.getxOffset().createValue(random, 0) * ParticleSystem.TRANSFER_VALUE;
        float yOff = effect.getyOffset().createValue(random, 0) * ParticleSystem.TRANSFER_VALUE;
        if (effect.getSpawnShape() == SpawnShape.Shape.SQUARE) {
            float width = effect.getSpawnWidth().getRange().createValue(random, effect.getSpawnWidth().getTimeline().getValueAtTime(lifePercent));
            float height = effect.getSpawnHeight().getRange().createValue(random, effect.getSpawnHeight().getTimeline().getValueAtTime(lifePercent));
            xOff += (random.nextFloat() * width - width * 0.5f)*ParticleSystem.TRANSFER_VALUE;
            yOff += (random.nextFloat() * height - height * 0.5f)*ParticleSystem.TRANSFER_VALUE;
        }
        setUpParticle(new Vector(position).translate(xOff, yOff));
    }

    private Particle setUpParticle(Vector position) {
        Particle particle;
        if (inactiveParticles.size() > 0)
            particle = inactiveParticles.remove(0);
        else
            particle = new Particle(random);

        particle.activate(position, effect.getLife().getTimeline().getValueAtTime(lifePercent), particleData);
        activeParticleLock.lock();
        activeParticles.add(particle);
        activeParticleLock.unlock();
        return particle;
    }

    private void calcWindAndGravity() {
        windStrength = effect.getWind().getRange().createValue(random, effect.getWind().getTimeline().getValueAtTime(lifePercent));
        gravityStrength = effect.getGravity().getRange().createValue(random, effect.getGravity().getTimeline().getValueAtTime(lifePercent));
    }

    private float calcEmittPause() {
        float emittedPerSecond = effect.getEmission().getRange().createValue(random, effect.getEmission().getTimeline().getValueAtTime(passedTime / maxTime));
        return 1000 / emittedPerSecond;
    }

    public Lock getActiveParticleLock() {
        return activeParticleLock;
    }

    public List<Particle> getActiveParticles() {
        return activeParticles;
    }
}
