package me.koba1.led;

import jakarta.annotation.PostConstruct;
import lombok.Getter;
import me.koba1.led.animations.BaseAnimation;
import me.koba1.led.animations.Speed;
import me.koba1.led.animations.audio.Audio;
import me.koba1.led.audio.AudioProcessor;
import me.koba1.led.device.DeviceManager;
import me.koba1.led.device.packets.UpdateColorPacket;
import me.koba1.led.managers.LedStripManager;
import me.koba1.led.managers.SettingsManager;
import me.koba1.led.model.LedStrip;
import me.koba1.led.model.colors.KobaOutputColor;
import me.koba1.led.simulator.SimulatorService;
import me.koba1.led.utils.AnimationBootstrap;
import me.koba1.led.utils.KobaLogger;
import me.koba1.led.utils.Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Getter
@SpringBootApplication
public class Main {
    @Getter private static final File savedDataFile = new File("saved.json");

    @Getter private static Main instance;

    private final LedStripManager ledStripManager;
    private final SettingsManager settingsManager;
    private final DeviceManager deviceManager;
    private final KobaLogger logger;
    private AudioProcessor.AudioThreadHandle audioProcessorHandle;

    public static void main(String[] args) {
        Utils.getFile(savedDataFile);

        SpringApplication.run(Main.class, args);
    }

    @Autowired private SimulatorService simulatorService;

    // private long lastTime = System.nanoTime();

    public Main() {
        instance = this;
        this.logger = new KobaLogger();

        AnimationBootstrap.init();

        this.ledStripManager = new LedStripManager();
        this.settingsManager = new SettingsManager();

        //this.ledStripManager.load();
        //this.settingsManager.load();

        this.deviceManager = new DeviceManager();
        ledStripManager.setupDefaults();
        audioProcessorHandle = AudioProcessor.startAudioThread();
        this.logger.info("System loaded");
    }

    @PostConstruct
    public void startScheduler() {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

        final Map<Integer, Long> lastUpdatePerStrip = new ConcurrentHashMap<>();
        scheduler.scheduleAtFixedRate(() -> {
            try {
                long now = System.nanoTime();
                Map<Integer, KobaOutputColor> outputColors = new HashMap<>();
                for (Map.Entry<Integer, LedStrip> value : ledStripManager.getLedStrips().entrySet()) {
                    LedStrip strip = value.getValue();
                    if (!strip.isPower()) {
                        //outputColors.put(value.getKey(), KobaOutputColor.createBlack(value.getValue().getId()));
                        // LedStripManager updateban már offra rakom 1x a ledeket, azért nem kell folyamaotsan kiküldei
                        lastUpdatePerStrip.remove(strip.getId());
                        continue;
                    }

                    BaseAnimation animation = strip.getSelectedAnimation();
                    if (animation == null) continue;
                    if (animation instanceof Audio s) {
                        KobaOutputColor c = s.getColors(strip, AudioProcessor.getAudioStateCopy());
                        if (c != null) {
                            outputColors.put(strip.getId(), c);
                        }
                    } else {
                        // IDLE ANIMATION
                        if (animation instanceof Speed s) {
                            // 0-100 a speed: 0-500ms
                            long lastUpdate = lastUpdatePerStrip.getOrDefault(strip.getId(), 0L);
                            long delayNanos = (long) ((s.getSpeed() / 100.0) * 500_000_000L);

                            if (delayNanos == 0) {
                                lastUpdatePerStrip.put(strip.getId(), now);
                                continue;
                            }
                            long elapsed = now - lastUpdate;
                            if (elapsed < delayNanos) {
                                continue;
                            }
                            lastUpdatePerStrip.put(strip.getId(), now);
                        }

                        KobaOutputColor output = animation.getColors(strip);
                        if (output != null) {
                            //simulatorService.sendColors(output.getIntegerColors());
                            //ledStripColors.put(strip.getPin(), output.getIntegerColors());
                            outputColors.put(strip.getId(), output);
                        }
                    }

                    if (!outputColors.isEmpty()) {
                       // System.out.println(outputColors.get(strip.getId()).getColors());
                        //UpdateColorPacket packet = new UpdateColorPacket(outputColors);
                        simulatorService.sendColors(outputColors.get(strip.getId()).getIntegerColors());
                        //this.deviceManager.sendPacket(new UpdateColorPacket(outputColors));
                        //System.out.println("Sendin colors " + outputColors);
                    }
                }
//             System.out.println(System.nanoTime() - now);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, 1000, 16, TimeUnit.MILLISECONDS); // ~60 FPS
    }

    public static KobaLogger getLogger() {
        return instance.logger;
    }
}
