package be.iminds.ilabt.jfed.experimenter_gui;

import be.iminds.ilabt.jfed.experiment.SfaExperimentPart;
import be.iminds.ilabt.jfed.experimenter_gui.config.AdvertisementFetchManager;
import be.iminds.ilabt.jfed.experimenter_gui.config.JFedExperimenterGuiConfigProvider;
import be.iminds.ilabt.jfed.experimenter_gui.config.JFedGuiConfig;
import be.iminds.ilabt.jfed.experimenter_gui.config.JFedGuiConfigImpl;
import be.iminds.ilabt.jfed.experimenter_gui.dialogs.DialogsFactory;
import be.iminds.ilabt.jfed.experimenter_gui.dialogs.DialogsFactoryImpl;
import be.iminds.ilabt.jfed.experimenter_gui.slice.ExperimentViewController;
import be.iminds.ilabt.jfed.experimenter_gui.tasks.TasksFactory;
import be.iminds.ilabt.jfed.experimenter_gui.tasks.TasksFactoryImpl;
import be.iminds.ilabt.jfed.experimenter_gui.ui.ExperimentEditorTabHost;
import be.iminds.ilabt.jfed.experimenter_gui.ui.GTSEditorTabHost;
import be.iminds.ilabt.jfed.experimenter_gui.ui.StatusUpdateListener;
import be.iminds.ilabt.jfed.experimenter_gui.ui.progressbar.TaskService;
import be.iminds.ilabt.jfed.highlevel.jobs.parts.ExperimentPartControllerManager;
import be.iminds.ilabt.jfed.highlevel.jobs.sfa_parts.SfaExperimentPartController;
import be.iminds.ilabt.jfed.lowlevel.GeniUserProvider;
import be.iminds.ilabt.jfed.ssh_terminal_tool.putty.SshKeyInfoUnlocker;
import be.iminds.ilabt.jfed.util.GeniUrn;
import be.iminds.jfed.gts_highlevel.jobs.gts_parts.GtsExperimentPart;
import be.iminds.jfed.gts_highlevel.jobs.gts_parts.GtsExperimentPartController;
import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.common.eventbus.SubscriberExceptionHandler;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.Matchers;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
import javafx.application.HostServices;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Provider;
import javax.inject.Singleton;

import static com.google.inject.name.Names.named;

/**
 * User: twalcari
 * Date: 12/1/2014
 * Time: 16:47
 */
public class ExperimenterModule extends AbstractModule {
    private final ObservableMap<GeniUrn, ExperimentViewController> experimentViewControllers =
            FXCollections.observableHashMap();

    private final Logger eventbusLogger = LoggerFactory.getLogger("Experimenter-EventBus");

    private final Launch launch;

    public ExperimenterModule(Launch launch) {
        this.launch = launch;
    }

    @Provides
    public HostServices provideHostServices(Launch launch) {
        return launch.getHostServices();
    }

    @Provides
    @Singleton
    public ExperimentPartControllerManager getEpcManager(Provider<SfaExperimentPartController> sfaExperimentPartControllerProvider,
                                                         Provider<GtsExperimentPartController> gtsExperimentPartControllerProvider){
        ExperimentPartControllerManager epcManager = new ExperimentPartControllerManager(true);
        epcManager.registerController(SfaExperimentPart.class, sfaExperimentPartControllerProvider.get());
        epcManager.registerController(GtsExperimentPart.class, gtsExperimentPartControllerProvider.get());

        return epcManager;
    }


    @Override
    protected void configure() {
        bind(Launch.class).toInstance(launch);
        bind(GeniUserProvider.class).to(Launch.class);
        bind(Stage.class).annotatedWith(named("parentStage")).toProvider(Launch.class);
        bind(SshKeyInfoUnlocker.class).to(Launch.class);
        bind(StatusUpdateListener.class).to(ExperimenterGUI.class);
        bind(TaskService.class).to(ExperimenterGUI.class);
        bind(ExperimentEditorTabHost.class).to(ExperimenterGUI.class);
        bind(GTSEditorTabHost.class).to(ExperimenterGUI.class);

        bind(DialogsFactory.class).to(DialogsFactoryImpl.class);
        bind(TasksFactory.class).to(TasksFactoryImpl.class);

        bind(JFedGuiConfig.class).to(JFedGuiConfigImpl.class).in(Singleton.class);
        bind(AdvertisementFetchManager.class).in(Singleton.class);
        bind(JFedExperimenterGuiConfigProvider.class).in(Singleton.class);

        //create an eventbus with logger, which registers objects automatically in which it is injected as an dependency
        SubscriberExceptionHandler subscriberExceptionHandler =
                (exception, context) ->
                        eventbusLogger.error("Error while processing event " + context.getEvent() + " at "
                                        + context.getSubscriber().getClass().toGenericString() + "#"
                                        + context.getSubscriberMethod().toGenericString(),
                                exception);
        EventBus eventBus = new EventBus(subscriberExceptionHandler);
        eventBus.register(this);

        bind(EventBus.class).toInstance(eventBus);
        bindListener(Matchers.any(), new TypeListener() {
            public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
                typeEncounter.register((InjectionListener<I>) eventBus::register);
            }
        });
    }

    @Subscribe
    public void handleDeadEventBusEvent(DeadEvent deadEvent) {
        eventbusLogger.warn("Detected a dead event in eventbus: {}. Submitted by {}", deadEvent.getEvent(), deadEvent.getSource());
    }
}
