package de.renew.unify;

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


final class Unknown implements Unifiable, Referable {
    private final BacklinkSet _backlinkSet = new BacklinkSet();
    private final RecorderChecker _recorderChecker;

    Unknown() {
        _recorderChecker = new RecorderChecker(null);
    }

    @Override
    public boolean isComplete() {
        return false;
    }

    @Override
    public boolean isBound() {
        return false;
    }

    @Override
    public void addBacklink(Reference reference, IStateRecorder recorder) {
        _recorderChecker.checkRecorder(recorder);
        _backlinkSet.addBacklink(reference, recorder);
    }

    // Moved here so that only an unknown needs to know about its
    // unification strategy.
    void unifySilently(Object obj, IStateRecorder recorder, Set<Notifiable> listeners)
        throws Impossible
    {
        if (obj instanceof Unknown that) {
            if (that._backlinkSet.size() < _backlinkSet.size()) {
                // The other unknown posses fewer backlinks to be updated.
                // It should be updated, so that we can save some work.
                that.unifySilently(this, recorder, listeners);
                // Nothing more to do here.
                return;
            }

            // I'll do it myself, it's cheaper this way.
        } else if (obj instanceof Referable referable) {
            referable.occursCheck(this, new HashSet<>());
        }


        // Make the update.
        _backlinkSet.updateBacklinked(this, obj, listeners, recorder);
    }

    @Override
    public void occursCheck(Unknown that, Set<IdentityWrapper> visited) throws Impossible {
        // Do I cause a cycle?
        if (this == that) {
            throw new Impossible();
        }
    }

    /**
     * Disable the normal <code>equals</code> and <code>hashCode</code>
     * methods.  Because unknowns can change their values, it would be
     * disastrous to insert them into a hashtable.
     *
     * @throws RuntimeException always.
     **/
    @Override
    public boolean equals(Object that) {
        throw new RuntimeException("Somebody compared an unknown.");
    }

    /**
     * Disable the normal <code>equals</code> and <code>hashCode</code>
     * methods.  Because unknowns can change their values, it would be
     * disastrous to insert them into a hashtable.
     *
     * @throws RuntimeException always.
     **/
    @Override
    public int hashCode() {
        throw new RuntimeException("Somebody took the hash code of an unknown.");
    }

    @Override
    public String toString() {
        return "<UNKNOWN!>";
    }
}