public class ValidateReverse {

    private ValidateReverseContext validateReverseContext;
    private AddReverseNode addReverseNode;

    public ValidateReverse(ValidateReverseContext validateReverseContext) {
        this.validateReverseContext = validateReverseContext;
        validateReverseInit();
    }

    public final void validateReverseInit()  {
        addReverseNode = new AddReverseNode(getValidateReverseContext());
    }

    public static void gen(FlowchartContext externalContext) {
        ValidateReverseContext validateReverseContext = new ValidateReverseContext(externalContext);
        ValidateReverse validateReverse = new ValidateReverse(validateReverseContext);
        validateReverse.validateReversibleNodes();
    }

    private void validateReversibleNodes() {
        setValidNodes();
        setReachableNodes();
        setReverseChildLists();
        
        getValidNodeList().clear();
        Node start = getStartNode();
        addReverseNode(start);
        setChildIndices();
    }

    private void setValidNodes() {
        Node start = getStartNode();
        addValidNode(start);
        for (Node node : getValidNodeList()) { //set reachable nodes
            if (node.getNodechildList().size() > 1) {
                node.getNodechildList().sort(Nodechild.INDEX_ORDER);
            }
            getReverseChildList(node);
            for (Nodechild nc : node.getNodechildList()) {
                Node childNode = nc.getChild();
                addValidNode(childNode);
            }
        }
    }

    private void setReachableNodes() {
        Node start = getStartNode();
        addReachable(start);
        for (Node node : getReachableList()) { //set uncollapsed reachable nodes
            for (Nodechild nc : node.getNodechildList()) {
                if (!nc.collapsed()) {
                    Node childNode = nc.getChild();
                    addReachable(childNode);
                }
            }
        }
    }

    private void setReverseChildLists() {
        for (Node node : getValidNodeList()) {
            for (Nodechild nc : node.getNodechildList()) {
                if (!nc.collapsed()) {
                    Node childNode = nc.getChild();
                    addReverseChild(childNode, nc);
                }
            }
        }
    }

    private void setChildIndices() {
        for (Node node : getValidNodeList()) { //set reachable nodes
            int i = 0;
            for (Nodechild nc : node.getNodechildList()) {
                nc.setChildIndex(i++);
            }
        }
    }

    private ValidateReverseContext getValidateReverseContext() {
        return validateReverseContext;
    }

    private AddReverseNode getAddReverseNode() {
        return addReverseNode;
    }

    private FlowchartContext getFlowchartContext() {
        return getValidateReverseContext().getFlowchartContext();
    }

    private SourceAppData getSourceAppData() {
        return getFlowchartContext().getSourceAppData();
    }

    private void addReverseNode(Node node) {
        getAddReverseNode().addReverseNode(node);
    }

    private Node getStartNode() {
        return getSourceAppData().getStartNode();
    }

    private boolean addValidNode(Node item) {
        return getSourceAppData().addValidNode(item);
    }

    private NodeHList getValidNodeList() {
        return getSourceAppData().getValidNodeList();
    }

    private NodechildIList getReverseChildList(Node mapkey) {
        return getValidateReverseContext().getReverseChildList(mapkey);
    }

    private boolean addReachable(Node item) {
        return getValidateReverseContext().addReachable(item);
    }

    private NodeHList getReachableList() {
        return getValidateReverseContext().getReachableList();
    }

    private void addReverseChild(Node mapkey, Nodechild reverseChild) {
        getValidateReverseContext().addReverseChild(mapkey, reverseChild);
    }
}