package de.renew.application;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import de.renew.shadowcompiler.DefaultShadowNetLoader;
import de.renew.simulator.api.IFinderRegistration;
import de.renew.simulatorontology.loading.Finder;
import de.renew.simulatorontology.loading.PathlessFinder;
import de.renew.simulatorontology.shadow.ShadowNetLoader;

/**
 * This class offers methods to register and deregister {@link Finder} and {@link PathlessFinder} instances to the simulator.
 * It is provided to the interface module through the {@link FinderRegistrationProvider}. The facade for this class is {@link de.renew.simulator.api.FinderRegistration}.
 */
class FinderRegistrationImpl implements IFinderRegistration {

    /**
     * Holds all registered finders for the default shadow net loader. Access to
     * this set is synchronized on the set itself, not on the
     * <code>SimulatorPlugin</code> instance.
     */
    private final Set<Finder> _registeredFinders = Collections.synchronizedSet(new HashSet<>());

    /**
     * Holds all registered pathless finders for the default shadow net loader.
     * Access to this set is synchronized on the set {@link #_registeredFinders},
     * not on the <code>SimulatorPlugin</code> instance or the set itself.
     */
    private final Set<PathlessFinder> _registeredPathlessFinders = new HashSet<>();

    /**
     * Remembers the default shadow net loader for the current simulation if
     * one has been requested.
     */
    private DefaultShadowNetLoader _currentShadowNetLoader;

    @Override
    public void registerDefaultNetFinder(Finder finder) {
        synchronized (_registeredFinders) {
            _registeredFinders.add(finder);
            if (_currentShadowNetLoader != null) {
                _currentShadowNetLoader.registerFinder(finder);
            }
        }
    }

    @Override
    public void removeDefaultNetFinder(Finder finder) {
        synchronized (_registeredFinders) {
            _registeredFinders.remove(finder);
            if (_currentShadowNetLoader != null) {
                _currentShadowNetLoader.removeFinder(finder);
            }
        }
    }

    @Override
    public void registerDefaultPathlessFinder(PathlessFinder finder) {
        synchronized (_registeredFinders) {
            _registeredPathlessFinders.add(finder);
            if (_currentShadowNetLoader != null) {
                _currentShadowNetLoader.registerPathlessFinder(finder);
            }
        }
    }

    @Override
    public void removeDefaultPathlessFinder(PathlessFinder finder) {
        synchronized (_registeredFinders) {
            _registeredPathlessFinders.remove(finder);
            if (_currentShadowNetLoader != null) {
                _currentShadowNetLoader.removePathlessFinder(finder);
            }
        }
    }

    @Override
    public void setNetLoader(ShadowNetLoader netLoader) {
        if (netLoader == null) {
            _currentShadowNetLoader = null;
            return;
        }
        if (!(netLoader instanceof DefaultShadowNetLoader)) {
            throw new IllegalArgumentException("A DefaultShadowNetLoader was expected!");
        }
        _currentShadowNetLoader = (DefaultShadowNetLoader) netLoader;
        synchronized (_registeredFinders) {
            _registeredFinders.forEach(_currentShadowNetLoader::registerFinder);
            _registeredPathlessFinders.forEach(_currentShadowNetLoader::registerPathlessFinder);
        }
    }

    @Override
    public ShadowNetLoader getNetLoader() {
        return _currentShadowNetLoader;
    }
}
