package p.studio;

import com.fasterxml.jackson.databind.ObjectMapper;
import p.packer.Packer;
import p.studio.lsp.api.LspServerLifecycle;
import p.studio.lsp.events.StudioEventBus;
import p.studio.lsp.events.StudioPackerEventAdapter;
import p.studio.lsp.services.LspServerLifecycleImpl;
import p.studio.utilities.ThemeService;
import p.studio.utilities.i18n.I18nService;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public final class AppContainer implements Container {
    private final I18nService i18nService;
    private final ThemeService themeService;
    private final StudioEventBus studioEventBus;
    private final ObjectMapper mapper;
    private final EmbeddedPacker embeddedPacker;
    private final StudioBackgroundTasks backgroundTasks;
    private final LspServerLifecycle lspServerLifecycle;

    public AppContainer() {
        this.i18nService = new I18nService();
        this.themeService = new ThemeService();
        this.studioEventBus = new StudioEventBus();
        this.mapper = new ObjectMapper();
        final ExecutorService backgroundExecutor = Executors.newFixedThreadPool(2, new StudioWorkerThreadFactory());
        this.backgroundTasks = new StudioBackgroundTasks(backgroundExecutor);
        final Packer packer = Packer.bootstrap(this.mapper, new StudioPackerEventAdapter(studioEventBus));
        this.embeddedPacker = new EmbeddedPacker(packer.workspaceService(), packer::close);
        this.lspServerLifecycle = new LspServerLifecycleImpl();
    }

    @Override
    public I18nService getI18n() {
        return i18nService;
    }

    @Override
    public ThemeService getTheme() {
        return themeService;
    }

    @Override
    public StudioEventBus getEventBus() {
        return studioEventBus;
    }

    @Override
    public ObjectMapper getMapper() {
        return mapper;
    }

    @Override
    public EmbeddedPacker getPacker() {
        return embeddedPacker;
    }

    @Override
    public StudioBackgroundTasks getBackgroundTasks() {
        return backgroundTasks;
    }

    @Override
    public p.studio.lsp.api.LspServerLifecycle getLspServerLifecycle() {
        return lspServerLifecycle;
    }

    @Override
    public void shutdownContainer() {
        embeddedPacker.shutdown();
        backgroundTasks.shutdown();
    }

    private static final class StudioWorkerThreadFactory implements ThreadFactory {
        private final AtomicInteger nextId = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable runnable) {
            final Thread thread = new Thread(runnable, "studio-worker-" + nextId.getAndIncrement());
            thread.setDaemon(true);
            return thread;
        }
    }
}
