package de.renew.formalism.fs;

import java.util.Enumeration;
import java.util.Hashtable;

import de.uni_hamburg.fs.ConceptImpl;
import de.uni_hamburg.fs.Name;
import de.uni_hamburg.fs.Partition;
import de.uni_hamburg.fs.TypeException;

import de.renew.shadowcompiler.ShadowLookup;
import de.renew.shadowcompiler.ShadowLookupExtension;
import de.renew.shadowcompiler.ShadowLookupExtensionFactory;
import de.renew.simulatorontology.shadow.ShadowNetElement;


/**
 * ShadowLookupExtension's in the context of FS.
 */
public class FSShadowLookupExtension implements ShadowLookupExtension {
    /**
     * Logger for the {@link FSShadowLookupExtension} class.
     */
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(FSShadowLookupExtension.class);

    /**
     * The factory producing this kind of extension.
     */
    private static final Factory FACTORY = new Factory();

    /**
     * Default constructor for FSShadowLookupExtension.
     */
    public FSShadowLookupExtension() {}

    /**
     * Retrieves the {@link FSShadowLookupExtension} associated with the given
     * {@link ShadowLookup} instance.
     * <p>
     * If no such extension exists yet, it will be created using this class's
     * factory and then returned.
     *
     * @param lookup the shadow lookup context from which to obtain the extension
     * @return the corresponding {@link FSShadowLookupExtension} instance
     */
    public static FSShadowLookupExtension lookup(ShadowLookup lookup) {
        return (FSShadowLookupExtension) lookup.getShadowLookupExtension(FACTORY);
    }

    /** Mapping from ShadowConcept to ConceptImpl. */
    private Hashtable<String, ConceptImpl> _conceptMap = new Hashtable<String, ConceptImpl>();

    /** Mapping from ConceptImpl.Feature to ShadowConcept or ShadowAssoc. */
    private Hashtable<String, ShadowNetElement> _appropMap =
        new Hashtable<String, ShadowNetElement>();

    /** Mapping from ConceptImpls to Partition of Subconcepts. */
    private Hashtable<ConceptImpl, Partition> _partitionMap =
        new Hashtable<ConceptImpl, Partition>();

    /**
     * Associates a shadow concept from the graphical model with its corresponding
     * feature-structure concept in the type system.
     * <p>
     * This mapping allows later retrieval of the logical concept that represents
     * a given shadow concept.
     *
     * @param shadowConcept the shadow concept from the graphical model
     * @param concept the corresponding feature-structure concept to associate
     */
    public void setConcept(ShadowConcept shadowConcept, ConceptImpl concept) {
        _conceptMap.put(shadowConcept.getName(), concept);
    }

    /**
     * Returns the feature-structure concept corresponding to the given
     * shadow concept from the graphical model.
     * <p>
     * The mapping is based on the shadow concept's name as stored in the
     * internal concept map.
     *
     * @param shadowConcept the shadow concept from the graphical model
     * @return the corresponding {@link ConceptImpl}, or {@code null} if no mapping exists
     */
    public ConceptImpl getConcept(ShadowConcept shadowConcept) {
        return _conceptMap.get(shadowConcept.getName());
    }

    /**
     * Returns an enumeration of all shadow concept names currently registered
     * in this lookup extension.
     * @return an enumeration of concept names
     */
    public Enumeration<String> allConcepts() {
        return _conceptMap.keys();
    }

    /**
     * Associates a given feature of a concept with its corresponding
     * shadow net element in the graphical model.
     * <p>
     * This mapping allows later lookup of which shadow element
     * defines a specific feature of a concept.
     *
     * @param concept the feature-structure concept to which the feature belongs
     * @param feature the feature name of the concept
     * @param elem the shadow net element representing this feature in the model
     */
    public void setApprop(ConceptImpl concept, Name feature, ShadowNetElement elem) {
        _appropMap.put(concept.getName() + "." + feature, elem);
    }

    /**
     * Retrieves the shadow net element that represents the given feature
     * of the specified concept.
     * <p>
     * The element is identified by the combination of the concept name and
     * the feature name as stored in the internal approp's map.
     *
     * @param concept the feature-structure concept whose feature is being queried
     * @param feature the feature name
     * @return the corresponding {@link ShadowNetElement}, or {@code null} if none exists
     */
    public ShadowNetElement getApprop(ConceptImpl concept, Name feature) {
        return _appropMap.get(concept.getName() + "." + feature);
    }

    /**
     * Adds a subconcept to the sub-partition of a given super concept.
     * <p>
     * If no partition exists yet for the specified super concept, a new
     * {@link Partition} is created and registered. Otherwise, the subconcept
     * is added to the existing partition. This method ensures that all
     * disjoint subconcepts are tracked within the type system.
     *
     * @param supi the super concept to which the sub-partition belongs
     * @param sub the subconcept to add to the sub-partition
     * @throws TypeException if adding the subconcept violates type constraints
     */
    public void addToSubPartition(ConceptImpl supi, ConceptImpl sub) throws TypeException {
        Partition suppart = _partitionMap.get(supi);
        if (suppart == null) {
            LOGGER.debug("Creating new sub-partition for " + supi.getName());
            LOGGER.debug("Adding " + sub.getName() + " to sub-partition of " + supi.getName());
            suppart = new Partition(sub);
            _partitionMap.put(supi, suppart);
        } else {
            LOGGER.debug("Adding " + sub.getName() + " to sub-partition of " + supi.getName());
            suppart.addConcept(sub);
        }
    }

    /**
     * Factory for creating {@link FSShadowLookupExtension} instances.
     */
    public static class Factory implements ShadowLookupExtensionFactory {
        /**
         * Default constructor for Factory.
         */
        public Factory() {}

        @Override
        public String getCategory() {
            return "de.renew.formalism.fs";
        }

        @Override
        public ShadowLookupExtension createExtension() {
            return new FSShadowLookupExtension();
        }
    }
}