package de.uni_hamburg.fs;

import collections.CollectionEnumeration;
import collections.LinkedList;
import collections.UpdatableSeq;


/**
 * Represents a node in a linked list structure that can hold typed elements.
 * This class extends AbstractNode to implement list-specific functionality.
 */
public class ListNode extends AbstractNode {

    /**
     * The head node of this list, representing the first element.
     * When null, indicates an empty head.
     */
    private Node _hd = null;

    /**
     * The tail node of this list, representing the remaining elements after the head.
     * When null, indicates an empty tail.
     */
    private Node _tl = null;

    /**
     * Constructs a new ListNode with the specified list type.
     *
     * @param listtype The type definition for this list node
     */
    public ListNode(ListType listtype) {
        super(listtype);
    }

    /**
     * Constructs a new ListNode with the specified list type and initial head and tail nodes.
     *
     * @param listtype The type definition for this list node
     * @param hd       The head node of the list
     * @param tl       The tail node of the list
     * @throws TypeException if the types of head or tail nodes cannot be unified with the list type
     */
    public ListNode(ListType listtype, Node hd, Node tl) throws TypeException {
        super(listtype);
        Type basetype = getBaseType();
        if (hd.getType().canUnify(basetype) && tl.getType().canUnify(new ListType(basetype))) {
            this._hd = hd;
            this._tl = tl;
        } else {
            throw new TypeException();
        }
    }

    /**
     * Returns the base type of this list node.
     *
     * @return The base type of the list
     */
    public Type getBaseType() {
        return ((ListType) _nodetype).getBaseType();
    }

    @Override
    public CollectionEnumeration featureNames() {
        UpdatableSeq feats = new LinkedList();
        if (_hd != null) {
            feats.insertLast(ListType.HEAD);
        }
        if (_tl != null) {
            feats.insertLast(ListType.TAIL);
        }
        return feats.elements();
        //      return nodetype.appropFeatureNames();
    }

    private boolean isHead(Name featureName) {
        return ListType.HEAD.equals(featureName);
    }

    private boolean isTail(Name featureName) {
        return ListType.TAIL.equals(featureName);
    }

    @Override
    public boolean hasFeature(Name featureName) {
        return (isHead(featureName) && _hd != null) || (isTail(featureName) && _tl != null);
    }

    /**
     * Returns the head node of this list.
     * If the head is null, creates a new node of the base type.
     *
     * @return The head node of the list
     */
    public Node getHead() {
        if (_hd == null) {
            return getBaseType().newNode();
        }
        return _hd;
    }

    /**
     * Returns the tail node of this list.
     * If the tail is null, creates a new list node of the base type.
     *
     * @return The tail node of the list
     */
    public Node getTail() {
        if (_tl == null) {
            return new ListType(getBaseType()).newNode();
        }
        return _tl;
    }

    @Override
    public Node delta(Name featureName) throws NoSuchFeatureException {
        if (isHead(featureName)) {
            return getHead();
        }
        if (isTail(featureName)) {
            return getTail();
        }
        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.
     */
    @Override
    public void setFeature(Name featureName, Node value) {
        if (isHead(featureName)) {
            _hd = value;
            return;
        } else if (isTail(featureName)) {
            _tl = value;
            return;
        }
        throw new NoSuchFeatureException(featureName, _nodetype);
    }

    @Override
    public Node duplicate() {
        // dont use special constructor as type double checking
        // is not necessary.
        ListNode copy = new ListNode((ListType) _nodetype);
        copy._hd = _hd;
        copy._tl = _tl;
        return copy;
    }
}