package p.studio;

import com.fasterxml.jackson.databind.ObjectMapper;
import p.studio.lsp.api.LspServerLifecycle;
import p.studio.lsp.events.StudioEventBus;
import p.studio.utilities.ThemeService;
import p.studio.utilities.i18n.I18nService;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

public interface Container {
    I18nService getI18n();

    ThemeService getTheme();

    StudioEventBus getEventBus();

    ObjectMapper getMapper();

    EmbeddedPacker getPacker();

    StudioBackgroundTasks getBackgroundTasks();

    LspServerLifecycle getLspServerLifecycle();

    void shutdownContainer();

    static void install(Container container) {
        final Container candidate = Objects.requireNonNull(container, "container");
        final Container current = Holder.CURRENT.get();
        if (current == null) {
            if (Holder.CURRENT.compareAndSet(null, candidate)) {
                return;
            }
        }
        if (Holder.CURRENT.get() != candidate) {
            throw new IllegalStateException("Container already installed.");
        }
    }

    static Container current() {
        final Container container = Holder.CURRENT.get();
        if (container == null) {
            throw new IllegalStateException("Container has not been installed.");
        }
        return container;
    }

    static I18nService i18n() {
        return current().getI18n();
    }

    static ThemeService theme() {
        return current().getTheme();
    }

    static StudioEventBus eventBus() {
        return current().getEventBus();
    }

    static ObjectMapper mapper() {
        return current().getMapper();
    }

    static EmbeddedPacker packer() {
        return current().getPacker();
    }

    static StudioBackgroundTasks backgroundTasks() {
        return current().getBackgroundTasks();
    }

    static LspServerLifecycle lspServerLifecycle() {
        return current().getLspServerLifecycle();
    }

    static void shutdown() {
        final Container container = Holder.CURRENT.get();
        if (container != null) {
            container.shutdownContainer();
        }
    }

    record EmbeddedPacker(
            p.packer.PackerWorkspaceService workspaceService,
            Runnable shutdownAction) {

        public EmbeddedPacker {
            Objects.requireNonNull(workspaceService, "workspaceService");
            Objects.requireNonNull(shutdownAction, "shutdownAction");
        }

        public void shutdown() {
            shutdownAction.run();
        }
    }

    final class Holder {
        private static final AtomicReference<Container> CURRENT = new AtomicReference<>();

        private Holder() {
        }
    }
}
