package be.iminds.ilabt.jfed.highlevel.jobs.parts;

import be.iminds.ilabt.jfed.experiment.CapabilityChecker;
import be.iminds.ilabt.jfed.experiment.Experiment;
import be.iminds.ilabt.jfed.experiment.ExperimentPart;
import be.iminds.ilabt.jfed.experiment.ExperimentPartsContainer;

import java.util.HashMap;
import java.util.Map;

/**
 * User: twalcari
 * Date: 1/12/2016
 * Time: 16:21
 */
public class ExperimentPartControllerManager implements CapabilityChecker {

    private final Map<Class<? extends ExperimentPart>, ExperimentPartController> controllerMap = new HashMap<>();
    private final Map<Class<? extends ExperimentPartsContainer>, NonSfaExperimentPartController>
            containerControllerMap = new HashMap<>();

    /** constructor to prevent Guice from ever auto-injecting this. A module needs to initialise this explicitly.
     * That can be done with modules like SfaOnlyExperimentModule (in highlevel-library module) or ExperimenterModule (in experimenter-gui module)
     * */
    public ExperimentPartControllerManager(boolean ignored) { }

    public <EP extends ExperimentPart> void registerController(Class<EP> clazz,
                                                               ExperimentPartController<EP> controller) {
        Class<? extends ExperimentPart> classToCheck = clazz;

        do {
            if (controllerMap.containsKey(classToCheck)) {
                throw new IllegalArgumentException("There is already a controller registered for class " +
                        classToCheck.getName());
            }

            //noinspection unchecked
            classToCheck = (Class<? extends ExperimentPart>) classToCheck.getSuperclass();
        } while (classToCheck != ExperimentPart.class);

        controllerMap.put(clazz, controller);

        if (controller instanceof NonSfaExperimentPartController) {
            NonSfaExperimentPartController<EP, ?> nonSfaController = (NonSfaExperimentPartController<EP, ?>) controller;

            Class<? extends ExperimentPartsContainer> containerClazz = nonSfaController.getContainerClass();

            if (containerClazz != null) {

                if (containerControllerMap.containsKey(containerClazz)) {
                    throw new IllegalArgumentException("There is already a controller registered for container class "
                            + containerClazz.getName());
                }

                containerControllerMap.put(containerClazz, nonSfaController);
            }
        }
    }

    public void populateNonSfaExperimentParts(Experiment experiment) {

        for (ExperimentPartController experimentPartController : controllerMap.values()) {
            if (experimentPartController instanceof NonSfaExperimentPartController) {
                ((NonSfaExperimentPartController) experimentPartController)
                        .populateExperimentParts(experiment);
            }
        }
    }

    public <EP extends ExperimentPart> ExperimentPartController<EP> getController(ExperimentPart experimentPart) {
        if (!controllerMap.containsKey(experimentPart.getClass()))
            throw new RuntimeException("No Controller available for ExperimentPart-class "
                    + experimentPart.getClass().getName());

        //noinspection unchecked
        return controllerMap.get(experimentPart.getClass());
    }

    public <EP extends ExperimentPart, EPC extends ExperimentPartsContainer>
    NonSfaExperimentPartController<EP, EPC> getContainerController(EPC container) {
        if (!containerControllerMap.containsKey(container.getClass()))
            throw new RuntimeException("No controller available for ExperimentPartsContainer-ckass "
                    + container.getClass().getName());

        //noinspection unchecked
        return containerControllerMap.get(container.getClass());
    }


    @Override
    public boolean supportsRenew(ExperimentPart experimentPart) {
        return getController(experimentPart).supportsRenew(experimentPart);
    }

    @Override
    public boolean supportsEditSSHKeys(ExperimentPart experimentPart) {
        return getController(experimentPart).supportsEditSshKeys(experimentPart);
    }
}
