package lc.mecha.graph;

import lc.mecha.log.MechaLogger;
import lc.mecha.log.MechaLoggerFactory;
import lc.mecha.util.StringAccumulatorV2;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * This class provides a basic implementation of <code>DGNavigator</code>.
 *
 * @author Alex Leigh
 * @since mk11 (ZERO CONTRAST)
 */
public abstract class DGNavigatorBase implements DGNavigator {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(DGNavigatorBase.class);
    private final DGNode self;
    private DGNode parent;

    protected DGNavigatorBase(DGNode self) {
        this.self = self;
    }

    public DGNode getParent() {
        return parent;
    }

    public void setParent(DGNode parent) {
        logger.debug("setParent() [parent: {}] [self: {}]", parent, self);
        if (this.parent != null && parent != null) throw new IllegalStateException();
        this.parent = parent;
    }

    public DGKey getIdentifier() {
        if (parent == null) return null;
        DGKey id = getParent().getDGNavigator().getChildIdentifier(self);
        if (id == null) throw new IllegalStateException();
        return id;
    }

    public List<DGKey> getPath(DGNode destination) {
        ArrayList<DGKey> path = new ArrayList<>();
        HashSet<DGNode> visited = new HashSet<>();
        DGNode currentNode = self;

        logger.debug("My parent is {}", this.getParent());

        while (currentNode != null && currentNode.getDGNavigator().getParent() != null && currentNode != destination) {
            logger.debug("Current node. [node: {}]", currentNode);
            path.add(0, currentNode.getDGNavigator().getIdentifier());
            currentNode = currentNode.getDGNavigator().getParent();
            if (visited.contains(currentNode)) throw new IllegalStateException();
            visited.add(currentNode);
        }

        return path;
    }

    public List<DGKey> getPath() {
        return getPath(null);
    }

    public String getPortablePath(DGNode destination) {
        return toString(getPath(destination));
    }

    public String getPortablePath() {
        return getPortablePath(null);
    }

    public String toString(List<DGKey> path) {
        StringAccumulatorV2 sa = new StringAccumulatorV2(KEY_SEPARATOR);

        for (DGKey key : path) {
            sa.push(key.getPortableString());
        }

        return sa.asString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        DGNavigatorBase that = (DGNavigatorBase) o;

        return parent != null ? parent.equals(that.parent) : that.parent == null;
    }

    @Override
    public int hashCode() {
        return parent != null ? parent.hashCode() : 0;
    }
}
