package org.workcraft.plugins.son.algorithm;

import java.util.Collection;
import java.util.HashSet;

import org.workcraft.dom.Node;
import org.workcraft.plugins.son.SON;
import org.workcraft.plugins.son.connections.SONConnection.Semantics;
import org.workcraft.plugins.son.elements.Block;
import org.workcraft.plugins.son.elements.TransitionNode;

public class TSONAlg extends RelationAlgorithm {

    private final SON net;
    private final RelationAlgorithm relation;

    public TSONAlg(SON net) {
        super(net);
        this.net = net;
        relation = new RelationAlgorithm(net);
    }

    /**
     * get all inputs of a given block without concerning connection types
     */
    public Collection<Node> getBlockInputs(Block block) {
        Collection<Node> result = new HashSet<>();
        Collection<Node> components = block.getComponents();

        for (Node node : components) {
            for (Node pre : net.getPreset(node)) {
                if (!components.contains(pre)) {
                    result.add(pre);
                }
            }
            for (Node post : net.getPostset(node)) {
                if (!components.contains(post) && net.getSONConnectionType(node, post) == Semantics.SYNCLINE) {
                    result.add(post);
                }
            }
        }
        result.addAll(net.getPreset(block));
        return result;
    }

    /**
     * get all outputs of a given block without concerning connection types
     */
    public Collection<Node> getBlockOutputs(Block block) {
        Collection<Node> result = new HashSet<>();
        Collection<Node> components = block.getComponents();

        for (Node node: components) {
            for (Node post : net.getPostset(node)) {
                if (!components.contains(post)) {
                    result.add(post);
                }
            }
            for (Node pre : net.getPreset(node)) {
                if (!components.contains(pre) && net.getSONConnectionType(node, pre) == Semantics.SYNCLINE) {
                    result.add(pre);
                }
            }
        }
        result.addAll(net.getPostset(block));
        return result;
    }

    /**
     * get petri net-based inputs of a given block
     */
    public Collection<Node> getBlockPNInputs(Block block) {
        Collection<Node> result = new HashSet<>();
        Collection<Node> components = block.getComponents();

        for (Node node : components) {
            for (Node pre : net.getPreset(node)) {
                if (!components.contains(pre) && net.getSONConnectionType(node, pre) == Semantics.PNLINE) {
                    result.add(pre);
                }
            }
        }
        for (Node pre : net.getPreset(block)) {
            if (net.getSONConnectionType(block, pre) == Semantics.PNLINE) {
                result.add(pre);
            }
        }

        return result;
    }

    /**
     * get petri net-based outputs of a given block
     */
    public Collection<Node> getBlockPNOutputs(Block block) {
        Collection<Node> result = new HashSet<>();
        Collection<Node> components = block.getComponents();

        for (Node node : components) {
            for (Node post : net.getPostset(node)) {
                if (!components.contains(post) && net.getSONConnectionType(node, post) == Semantics.PNLINE) {
                    result.add(post);
                }
            }
        }
        for (Node post : net.getPostset(block)) {
            if (net.getSONConnectionType(block, post) == Semantics.PNLINE) {
                result.add(post);
            }
        }

        return result;
    }

    /**
     * get cson-based inputs of a given block
     */
    public Collection<Node> getBlockASynInputs(Block block) {
        Collection<Node> result = new HashSet<>();
        Collection<Node> components = block.getComponents();

        for (Node node : components) {
            if (node instanceof TransitionNode) {
                for (Node pre : this.getPreASynEvents((TransitionNode) node)) {
                    if (!components.contains(pre) && pre != null) {
                        result.add(pre);
                    }
                }
            }
        }
        return result;
    }

    /**
     * get cson-based outputs of a given block
     */
    public Collection<Node> getBlockASynOutputs(Block block) {
        Collection<Node> result = new HashSet<>();
        Collection<Node> components = block.getComponents();

        for (Node node : components) {
            if (node instanceof TransitionNode) {
                for (Node post : this.getPostASynEvents((TransitionNode) node)) {
                    if (!components.contains(post) && post != null) {
                        result.add(post);
                    }
                }
            }
        }
        return result;
    }

    /**
     * check if a given input causally precede all outputs
     */
    public boolean isCausallyPrecede(Node input, Collection<Node> outputs) {
        for (Node post : relation.getPostPNSet(input)) {
            if (outputs.contains(post)) {
                outputs.remove(post);
            } else {
                isCausallyPrecede(post, outputs);
            }
        }
        if (outputs.isEmpty()) {
            return true;
        } else {
            return false;
        }
    }

}
