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

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonValue;
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.ColorUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/*
* HA CSAK MAGAS = FEHÉR
* HA MAGAS ÉS BASS = FEHÉR
* HA MAGAS VOLT ÉS NINCS BASS = FADE OUT
* HA MAGAS VOLT ÉS BASS = INSTANT BASS
*/
public class RangeFlashAnimation extends ColoredAnimation implements Audio {

    public static enum SoundPitch {
        COMBINED_BASS(s -> Math.max(s.bands.get(BandType.BASS), s.bands.get(BandType.SUB_BASS))),
        BASS(s -> s.bands.get(BandType.BASS)),
        SUB_BASS(s -> s.bands.get(BandType.SUB_BASS)),
        COMBINED_MID(s -> Math.max(s.bands.get(BandType.UPPER_MID), Math.max(s.bands.get(BandType.LOW_MID), s.bands.get(BandType.MID)))),
        LOW_MID(s -> s.bands.get(BandType.LOW_MID)),
        MID(s -> s.bands.get(BandType.MID));

        private final Function<AudioProcessor.AudioState, Float> pitchFunction;

        @JsonValue
        public String getValue() {
            return name().toLowerCase();
        }

        SoundPitch(Function<AudioProcessor.AudioState, Float> pitchFunction) {
            this.pitchFunction = pitchFunction;
        }
    }

    private float threshold;
    private long fadeOutTime;

    private SoundPitch soundPitch;

    @JsonIgnore private long fadeOutState;

    public RangeFlashAnimation(float threshold, long fadeOutTime, SoundPitch soundPitch, Map<AnimationColorType, AnimationColor> colors) {
        super(102, colors);
        this.threshold = threshold;
        this.fadeOutTime = fadeOutTime;
        this.soundPitch = soundPitch;
        this.fadeOutState = 0;
    }

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

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

        // code
        float value = this.soundPitch.pitchFunction.apply(s);
        //float bass = s.bands.get(BandType.BASS) > s.bands.get(BandType.SUB_BASS) ? s.bands.get(BandType.BASS) : s.bands.get(BandType.SUB_BASS);

        List<KobaColor> colors = fg.getColor(ledStrip.getLedCount());

        //System.out.println(bass);
        if (value >= threshold) {
            //System.out.println("BASSZUS");
            fadeOutState = System.currentTimeMillis() + fadeOutTime;
        }

        if (this.fadeOutState >= System.currentTimeMillis()) {
            long millisDiff = this.fadeOutState - System.currentTimeMillis();

            List<KobaColor> newColors = new ArrayList<>();
            for (KobaColor color : colors) {
                int alpha = (int) ((float) millisDiff / this.fadeOutTime * color.getAlpha());
                //int newAlpha = (int) (millisDiff / this.fadeOutTime * alpha);
                newColors.add(color.withAlpha(alpha));
            }

            //System.out.println("Filled output colors: " + newColors);
            outputColors.fill(newColors);
        }


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

    static {
        register(() -> new RangeFlashAnimation(
                0.9f,
                500,
                SoundPitch.COMBINED_BASS,
                Map.of(AnimationColorType.FOREGROUND, new AnimationColor(),
                        AnimationColorType.BACKGROUND, new AnimationColor().type(AnimationColor.ColorType.GRADIENT))));
    }
}