package me.koba1.led.animations.audio.bass;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import me.koba1.led.animations.ColoredAnimation;
import me.koba1.led.animations.audio.Audio;
import me.koba1.led.audio.AudioProcessor;
import me.koba1.led.audio.BandType;
import me.koba1.led.model.LedStrip;
import me.koba1.led.model.colors.AnimationColor;
import me.koba1.led.model.colors.AnimationColorType;
import me.koba1.led.model.colors.KobaColor;
import me.koba1.led.model.colors.KobaOutputColor;
import me.koba1.led.utils.ColorConstants;
import me.koba1.led.utils.ColorUtils;

import java.util.*;

public class OuterWaveBassAnimation extends ColoredAnimation implements Audio {

    // mirrored úgymond
    @AllArgsConstructor
    private static class Wave {
        private int position; // 0 - center

        public Wave increasePosition() {
            this.position++;
            return this;
        }
    }

    private float threshold;
    private long waveTime;
    private int waveLength;

    @JsonIgnore private long stepTime;
    @JsonIgnore
    private final List<Wave> waves;
    @JsonIgnore
    private final KobaOutputColor waveColors;
    @JsonIgnore
    private long nextStep;
    @JsonIgnore
    private long newWaveCooldown;

    public OuterWaveBassAnimation(float threshold, long waveTime, int waveLength, Map<AnimationColorType, AnimationColor> colors) {
        super(101, colors);
        this.threshold = threshold;
        this.waveTime = waveTime;
        this.waveLength = waveLength;
        this.waves = new ArrayList<>();
        this.waveColors = new KobaOutputColor();

        this.newWaveCooldown = 0;
        this.nextStep = 0;
    }

    @Override
    public KobaOutputColor getColors(LedStrip ledStrip) {
        return null;
    }

    @Override
    public KobaOutputColor getColors(LedStrip ledStrip, AudioProcessor.AudioState s) {
        long unixTime = System.currentTimeMillis();
        this.stepTime = this.waveTime / (ledStrip.getLedCount() / 2);
        outputColors.clear();
        AnimationColor bg = this.colors.get(AnimationColorType.BACKGROUND);
        //AnimationColor fg = this.colors.get(AnimationColorType.FOREGROUND);
        outputColors.fill(bg.getColor(ledStrip.getLedCount()));
        this.waveColors.clear();

        if (this.nextStep <= unixTime) {
            stepWaves(ledStrip);
            this.nextStep = unixTime + this.stepTime;
        }

        float bass = s.bands.get(BandType.BASS) > s.bands.get(BandType.SUB_BASS) ? s.bands.get(BandType.BASS) : s.bands.get(BandType.SUB_BASS);
        if(bass >= threshold && newWaveCooldown <= unixTime) {
            startNewWave();
            newWaveCooldown = unixTime + 10L;
        }
//        if (nextWave == 0 || nextWave < System.currentTimeMillis()) { // threshold jön majd ide
//            startNewWave(ledStrip);
//            nextWave = System.currentTimeMillis() + 700L;
//        }

        drawAllWave(ledStrip);

        mirrorOutputColors(ledStrip);

        KobaOutputColor difference = ColorUtils.getDifference(this.previousColors, this.outputColors);
        this.previousColors.set(this.outputColors);
        return difference;
    }

    private void mirrorOutputColors(LedStrip s) {
        int center = s.getLedCount() / 2;
        for (Map.Entry<Integer, KobaColor> entry : new HashMap<>(this.waveColors.getColors()).entrySet()) {
            this.outputColors.set(entry.getKey(), entry.getValue()); // positive
            this.outputColors.set(center - (entry.getKey() - center), entry.getValue()); // negative
        }
    }

    private void drawAllWave(LedStrip s) {
        for (Wave w : new ArrayList<>(this.waves)) {
            drawWave(w, s);
        }
    }

    private void stepWaves(LedStrip s) {
        for (Wave w : new ArrayList<>(this.waves)) {
            w.increasePosition();
            if (w.position > s.getLedCount() / 2) {
                this.waves.remove(w);
                //System.out.println("Removed wave");
                continue;
            }
        }
    }

    private void startNewWave() {
        Wave w = new Wave(0);
        this.waves.add(w);
        //drawWave(w, s);
    }

    private void drawWave(Wave wave, LedStrip s) {
        int center = s.getLedCount() / 2;
        AnimationColor bg = this.colors.get(AnimationColorType.BACKGROUND);
        AnimationColor fg = this.colors.get(AnimationColorType.FOREGROUND);

        for (int i = 0; i < this.waveLength; i++) {
            int idx = wave.position - i;
            if (idx < 0) continue;

            /*KobaColor color = fg.getColor(center + idx, s.getLedCount());
            int alpha = color.getAlpha();
            if ((double) i / this.waveLength >= 0.33) {
                alpha = 255 - 30 * (i - 1); // 10-t át lehet írni 10-30
            }
            this.waveColors.set(center + idx, color.withAlpha(alpha));*/


            this.waveColors.set(center + idx, fg.getColor(center + idx, s.getLedCount()));
        }

        // régi ledet beteszi feketére
        if (wave.position > this.waveLength) {
            this.waveColors.set(wave.position - this.waveLength, bg.getColor(wave.position - this.waveLength, s.getLedCount()));
            // negative
        }
    }

    static {
        register(() -> new OuterWaveBassAnimation(
                0.9f,
                150,
                4,
                Map.of(AnimationColorType.FOREGROUND, new AnimationColor(),
                        AnimationColorType.BACKGROUND, new AnimationColor().type(AnimationColor.ColorType.GRADIENT))));
    }
}
