package de.renew.formalism.fs;

import de.uni_hamburg.fs.Name;

import de.renew.simulatorontology.shadow.ShadowInscribable;
import de.renew.simulatorontology.shadow.ShadowInscription;
import de.renew.simulatorontology.shadow.ShadowNetElement;


/**
 * Represents an association between two shadow concepts in a shadow Petri net
 * for the FS formalism.
 * <p>
 * Each association links a source concept to a target concept and may
 * carry multiplicity information and a feature name via inscriptions.
 */
public class ShadowAssoc extends ShadowInscribable {

    /**
     * Logger for the {@link ShadowAssoc} class.
     */
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(ShadowAssoc.class);

    /**
     * Multiplicity constant indicating that the association can have at most one value.
     */
    public static final int ZERO_OR_ONE = 0;

    /**
     * Multiplicity constant indicating that the association can have zero or more values.
     */
    public static final int ZERO_OR_MORE = 1;

    /**
     * Multiplicity constant indicating that the association must have at least one value.
     */
    public static final int ONE_OR_MORE = 2;

    /**
     * The source concept of this association.
     */
    private ShadowConcept _concept;

    /**
     * The target concept of this association.
     */
    private ShadowConcept _type;


    /**
     * Creates a new association between two shadow concepts in the same net.
     * <p>
     * The association links the given source concept to the target concept
     * (type) and registers it with the same shadow net as the source.
     *
     * @param concept the source concept of the association
     * @param type the target concept of the association
     */
    public ShadowAssoc(ShadowConcept concept, ShadowConcept type) {
        super(concept.getNet());
        this._concept = concept;
        this._type = type;
    }

    /**
     * Returns the source concept of this association.
     *
     * @return the {@link ShadowConcept} from which this association originates
     */
    public ShadowConcept getConcept() {
        return _concept;
    }

    /**
     * Returns the target concept of this association.
     *
     * @return the {@link ShadowConcept} that is the target of this association
     */
    public ShadowConcept getType() {
        return _type;
    }

    /**
     * Returns the multiplicity of this association, based on its inscriptions.
     * <p>
     * Multiplicity defines how many instances of the target concept can be associated.
     *
     * @return a constant indicating the multiplicity of this association
     */
    public int getMultiplicity() {
        java.util.Iterator<ShadowNetElement> iterator = elements().iterator();
        while (iterator.hasNext()) {
            String inscr = ((ShadowInscription) iterator.next()).getInscription().trim();
            if ("*".equals(inscr) || "0..*".equals(inscr)) {
                return ZERO_OR_MORE;
            } else if ("1..*".equals(inscr)) {
                return ONE_OR_MORE;
            }
        }
        return ZERO_OR_ONE;
    }

    /**
     * Returns the feature name for this association as used in the FS system.
     * <p>
     * If the association has an explicit inscription without a range, that is used
     * as the feature name. Otherwise, a default name is generated from the target concept's
     * name.
     *
     * @return the {@link Name} representing the feature for this association
     */
    public Name getFeature() {
        java.util.Iterator<ShadowNetElement> iterator = elements().iterator();
        String feature = null;
        boolean multi = false;
        while (iterator.hasNext() && feature == null) {
            String inscr = ((ShadowInscription) iterator.next()).getInscription().trim();
            if (inscr.indexOf("*") >= 0) {
                multi = true;
            } else if (inscr.indexOf("..") < 0) {
                feature = inscr;
            }
        }
        if (feature == null) {
            feature = _type.getName();
            int pos = feature.indexOf("::");
            if (pos >= 0) {
                feature = feature.substring(pos + 2);
            }
            feature = feature.substring(0, 1).toLowerCase() + feature.substring(1);
            if (multi) {
                feature += "s";
            }
            LOGGER.debug("Concept " + _concept.getName() + " gets default feature name " + feature);
        }
        return new Name(feature);
    }

    @Override
    public void discard() {
        // is there anything else to do here?
    }
}