package me.koba1.led.animations.idle;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import me.koba1.led.animations.ColoredAnimation;
import me.koba1.led.animations.Speed;
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 me.koba1.led.utils.Utils;

import java.awt.*;
import java.util.Map;

@Setter
@Getter
@ToString(callSuper = true)
public class RainbowAnimation extends ColoredAnimation implements Speed, Idle {

     private final static double MIN = 0.1;
     private final static double MAX = 1.0;

    private int speed;
    private boolean moving;
    private boolean breathing;

    @JsonIgnore private KobaColor[] rainbowPalette;
    @JsonIgnore private int state;
    @JsonIgnore private double breathingAlpha; // 0-1
    @JsonIgnore private int breathingDirection; // 0-1
    @JsonIgnore private double progress = 0.0;   // 0..1

    public RainbowAnimation() {
        super(2);
    }

    // @JsonCreator
    public RainbowAnimation(int speed, boolean moving, boolean breathing, /*@JsonProperty("colors")*/ Map<AnimationColorType, AnimationColor> colors) {
        super(2, colors);
        this.speed = speed;
        this.moving = moving;
        this.breathing = breathing;
        this.state = 0;
        this.breathingDirection = 1;
    }

    @Override
    public KobaOutputColor getColors(LedStrip ledStrip) {
        int ledCount = ledStrip.getLedCount(); // Cache once
        ensurePaletteInitialized(ledCount);

        outputColors.clear();
        /*if(this.breathing) {
            double speedMultiplier = getSpeed() / 100.0; // 0..1
            double step = 0.05;       // delta pl 1/16 fps

            progress += step * breathingDirection;

            // TÜKRÖZÉS (nem clamp!)
            if (progress >= MAX) breathingDirection = -1;
            else if (progress <= MIN) breathingDirection = 1;

            // System.out.println(breathingAlpha + " " + progress);
            // breathingAlpha = (MIN + progress * (MAX - MIN));
            breathingAlpha = Utils.clamp(progress, MIN, MAX);
            System.out.println(breathingAlpha);
        }*/

        if(this.breathing) {
            double time = System.currentTimeMillis() / 1000.0;
            double speedMultiplier = getSpeed() / 100.0;
            this.progress += 0.08;
            breathingAlpha = 1 - Math.sin(progress);
        }

        for (int i = 0; i < ledCount; i++) {
            int paletteIndex = (i + state) % ledCount;
            if(!moving)
                paletteIndex = i;
            if(this.breathing) {
                KobaColor c = rainbowPalette[paletteIndex];
                outputColors.set(i, c.withAlpha((int) (c.getAlpha() * breathingAlpha))); // No allocation, just reference
                continue;
            }

            outputColors.set(i, rainbowPalette[paletteIndex]); // No allocation, just reference
        }

        state = (state + 1) % ledCount;

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

    private void ensurePaletteInitialized(int ledCount) {
        if (rainbowPalette == null || rainbowPalette.length != ledCount) {
            rainbowPalette = new KobaColor[ledCount];
            for (int i = 0; i < ledCount; i++) {
                float hue = (float) i / ledCount;
                Color awtColor = Color.getHSBColor(hue, 1.0f, 1.0f);
                rainbowPalette[i] = new KobaColor(rgbToHex(awtColor.getRed(), awtColor.getGreen(), awtColor.getBlue()));
            }
        }
    }

    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
    private static final char[] HEX_BUFFER = new char[7];
    private String rgbToHex(int r, int g, int b) {
        HEX_BUFFER[0] = '#';

        // Gyors bitműveletek és lookup table
        HEX_BUFFER[1] = HEX_ARRAY[(r >> 4) & 0xF];
        HEX_BUFFER[2] = HEX_ARRAY[r & 0xF];
        HEX_BUFFER[3] = HEX_ARRAY[(g >> 4) & 0xF];
        HEX_BUFFER[4] = HEX_ARRAY[g & 0xF];
        HEX_BUFFER[5] = HEX_ARRAY[(b >> 4) & 0xF];
        HEX_BUFFER[6] = HEX_ARRAY[b & 0xF];

        return new String(HEX_BUFFER); // Csak 1 String allokáció
    }

    static {
        register(() -> new RainbowAnimation(50, false, true, Map.of(AnimationColorType.MAIN, new AnimationColor())));
    }
}
