package leigh.eo.util;

import leigh.eo.EO;
import leigh.eo.EOLoop;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;

/**
 * Utility methods for working with EOs.
 *
 * @author Alex Leigh
 */
public class EOUtil {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(EOUtil.class);

    /**
     * Set the <code>id</code> attribute of the provided EO to a randomly generated UUID.
     */
    public static EO makeId(EO eo) {
        eo.setValue("id", UUID.randomUUID());
        return eo;
    }

    /**
     * Iterates all the EOs contained within <code>eo</code>, visiting each only once, and adds them to the set
     * <code>dst</code>. If <code>add</code> is true then <code>eo</code> itself will be added to <code>dst</code>.
     *
     * @since mk11 (ZERO CONTRAST)
     */
    public static void scan(Set<EO> dst, EO eo, boolean add) {
        if (dst.contains(eo)) return;

        if (add) dst.add(eo);

        for (String key : eo.getKeys()) {
            Object o = eo.get(key);
            if (o instanceof EOLoop) {
                for (EO child : ((EOLoop) o)) {
                    scan(dst, child, true);
                }
            }
        }
    }

    /**
     * Perform a join between two loop sets.
     */
    public static void join(EOLoop parentLoop, EOLoop childLoop, String parentJoinKey, String childJoinKey,
                            EOJoinKeyDeterminer determiner) {
        HashMap<Serializable, EO> index = new HashMap<>();
        for (EO parent : parentLoop) {
            Serializable keyVal = parent.get(parentJoinKey);
            if (keyVal == null) {
                logger.info("Parent was missing the join key. [eo: parent]", parentJoinKey);
                continue;
            }

            index.put(keyVal, parent);
        }

        for (EO child : childLoop) {
            Serializable keyVal = child.get(childJoinKey);
            if (keyVal == null) {
                logger.warn("Child EO missing join key. [eo: {}]", child);
                continue;
            }

            EO parent = index.get(keyVal);
            if (parent == null) {
                logger.warn("Parent EO not found for child EO. [key: {}]", keyVal);
                continue;
            }

            parent.getValueLoop(determiner.getChildLoopGet(parent, child)).add(child);
        }
    }

    public static Set<String> findAllKeys(EOLoop loop) {
        TreeSet<String> keys = new TreeSet<>();
        for (EO eo : loop) {
            keys.addAll(eo.getKeys());
        }
        return keys;
    }
}
