package me.koba1.led.animations.idle;

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

@Setter
@Getter
@ToString(callSuper = true)
public class WaveAnimation extends ColoredAnimation implements Speed, Idle {
    public static enum WaveType {
        DEFAULT,
        REVERSE,
        BOUNCE;

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

    private int length;
    private int speed;
    private boolean distance;
    private WaveType type;

    @JsonIgnore private int state;
    @JsonIgnore private boolean bounceDirection;

    public WaveAnimation() {
        super(1);
    }

    // @JsonCreator
    public WaveAnimation(int length, int speed, boolean distance, WaveType type, Map<AnimationColorType, /*@JsonProperty("colors")*/ AnimationColor> colors) {
        super(1, colors);

        this.length = length;
        this.speed = speed;
        this.distance = distance;
        this.type = type;

        this.state = 0;
        this.bounceDirection = false;
    }

    @Override
    public KobaOutputColor getColors(LedStrip ledStrip) {
        outputColors.clear();
        AnimationColor bg = this.colors.get(AnimationColorType.BACKGROUND);
        AnimationColor fg = this.colors.get(AnimationColorType.FOREGROUND);

        outputColors.fill(bg.getColor(ledStrip.getLedCount()));
        switch (this.type) {
            case DEFAULT: {

                for (int i = this.state; i < this.state + this.length; i++) {
                    int ledIdx = i < ledStrip.getLedCount() ? i : i - ledStrip.getLedCount();
                    setColor(ledStrip, fg, i, ledIdx);
                }

                if (this.state >= ledStrip.getLedCount())
                    this.state = 0;

                this.state++;
                break;
            }
            case REVERSE: {
                int lastLedIndex = ledStrip.getLedCount() - 1;

                for (int i = this.state; i < this.state + this.length; i++) {
                    int ledIdx = lastLedIndex - i;
                    if (ledIdx < 0) {
                        ledIdx = lastLedIndex + (ledIdx + 1);
                    }

                    setColor(ledStrip, fg, i, ledIdx);
                }

                if (this.state >= ledStrip.getLedCount())
                    this.state = 0;

                this.state++;
                break;
            }

            case BOUNCE: {
                int lastLedIndex = ledStrip.getLedCount() - 1;

                for (int i = this.state; i < this.state + this.length; i++) {
                    int ledIdx;
                    if (!this.bounceDirection) { // balrol jobbra
                        ledIdx = i < ledStrip.getLedCount() ? i : i - ledStrip.getLedCount();
                    } else {
                        ledIdx = lastLedIndex - i;
                        if (ledIdx < 0) {
                            ledIdx = lastLedIndex + (ledIdx + 1);
                        }
                    }

                    setColor(ledStrip, fg, i, ledIdx);
                }

                if (this.state + this.length >= ledStrip.getLedCount()) {
                    this.bounceDirection = !this.bounceDirection;
                    this.state = 0;
                }

                this.state++;
                break;
            }
        }

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

    private void setColor(LedStrip ledStrip, AnimationColor animColor, int i, int ledIdx) {
        KobaColor color = animColor.getColor(i - this.state, this.length);
        int alpha = color.getAlpha();
        if(this.distance) {
            float distance = this.state + this.length - i;
            if (distance / length >= 0.33) {
                alpha = (int) (255 - 20 * (distance - 1)); // 10-t át lehet írni 10-30
            }
        }

        outputColors.set(ledIdx, color.withAlpha(alpha));
    }


    static {
        register(() -> new WaveAnimation(
                2,
                50,
                true,
                WaveType.DEFAULT,
                Map.of(AnimationColorType.FOREGROUND, new AnimationColor(),
                        AnimationColorType.BACKGROUND, new AnimationColor().type(AnimationColor.ColorType.GRADIENT))));
    }
}
