package de.uni_hamburg.fs;

import collections.CollectionEnumeration;
import collections.Map;


/**
 * A concrete implementation of feature structure nodes that provides type-safe
 * feature management and manipulation capabilities. FSNode extends AbstractNode
 * to implement the core functionality of feature structures within the system.
 * <p>
 * The class supports:
 * <ul>
 *   <li>Type-safe feature management using OrderedTable</li>
 *   <li>Feature appropriateness checking and validation</li>
 *   <li>Multiple initialization paths through various constructors</li>
 *   <li>Optional debug mode for enhanced type checking</li>
 *   <li>Safe feature value manipulation and access</li>
 *   <li>Deep structure duplication capabilities</li>
 * </ul>
 * <p>
 * Features are stored in an ordered table and managed with strict type checking
 * to ensure the integrity and consistency of the feature structure. The class
 * provides comprehensive support for feature manipulation while maintaining
 * type safety throughout all operations.
 */
public class FSNode extends AbstractNode {

    /**
     * Debug flag that enables enhanced type checking when true.
     * When enabled, additional validation is performed during feature operations.
     */
    private static final boolean DEBUG = false;

    /**
     * Ordered table storing the features of this node.
     * Contains feature names as keys and their corresponding node values.
     */
    OrderedTable _feats;

    /**
     * Constructs a FSNode with specified node type and features map.
     *
     * @param nodetype the type of the node
     * @param feats    the map of features to initialize the node with
     */
    public FSNode(Type nodetype, Map feats) {
        super(nodetype);
        this._feats = new OrderedTable(nodetype.appropFeatureNames());
        CollectionEnumeration features = feats.keys();
        while (features.hasMoreElements()) {
            Name feature = (Name) features.nextElement();
            setFeature(feature, (Node) feats.at(feature));
        }
    }

    /**
     * Constructs a FSNode with specified node type.
     *
     * @param nodetype the type of the node
     */
    public FSNode(Type nodetype) {
        super(nodetype);
        this._feats = new OrderedTable(nodetype.appropFeatureNames());
    }

    /**
     * Constructs a default FSNode with ANY type.
     */
    public FSNode() {
        this(Type.ANY);
    }

    /**
     * Constructs a FSNode with the specified node type string.
     *
     * @param nodetype the type of the node as a string
     * @throws UnificationFailure if the node type cannot be unified
     */
    public FSNode(String nodetype) throws UnificationFailure {
        this(ConjunctiveType.getType(nodetype));
    }

    /**
     * Constructs a FSNode with specified node type string and ordered features table.
     *
     * @param nodetype the type of the node as a string
     * @param feats    the ordered table of features
     * @throws UnificationFailure if the node type cannot be unified
     * @throws TypeException      if there is a type mismatch
     */
    public FSNode(String nodetype, OrderedTable feats) throws UnificationFailure, TypeException {
        this(ConjunctiveType.getType(nodetype), feats);
    }

    FSNode(Type nodetype, OrderedTable feats) {
        super(nodetype);
        this._feats = feats;
    }

    @Override
    public CollectionEnumeration featureNames() {
        return _feats.keys();
    }

    @Override
    public boolean hasFeature(Name featureName) {
        return _feats.includesKey(featureName);
    }

    @Override
    public Node delta(Name featureName) throws NoSuchFeatureException {
        if (_feats.includesKey(featureName)) {
            return (Node) _feats.at(featureName);
        }
        if (_nodetype.isApprop(featureName)) {
            return _nodetype.appropType(featureName).newNode();
        }
        throw new NoSuchFeatureException(featureName, _nodetype);
    }

    /**
     * Sets the value of the feature with the given name.
     * This method should only be called during construction of
     * a Node and with a value of the correct type.
     */
    @Override
    public void setFeature(Name featureName, Node value) {
        if (DEBUG) {
            //if (nodetype.isApprop(featureName) && nodetype.appropType(featureName).subsumes(value.getType())) {
            if (!(_nodetype.isApprop(featureName)
                && _nodetype.appropType(featureName).canUnify(value.getType()))) {
                throw new NoSuchFeatureException(featureName, _nodetype);
            }
        }
        if (value == null) {
            _feats.removeAt(featureName);
        } else {
            _feats.putAt(featureName, value);
        }
    }

    @Override
    public Node duplicate() {
        return new FSNode(_nodetype, (OrderedTable) _feats.duplicate());
    }
}