class PermutationGenerator {

    public boolean checkMutability(OTTopologyFragmentRefinementModel refinementModel) {
        logger.info("Starting mutability check of {}", refinementModel.getIdFromIdOrNameField());
        this.mutabilityErrorReason = "";

        List<TNodeTemplate> detectorNodeTemplates = refinementModel.getDetector().getNodeTemplates();
        Set<TNodeTemplate> mutableNodes = detectorNodeTemplates.stream()
            .filter(nodeTemplate -> !isStayPlaceholder(nodeTemplate, refinementModel))
            .collect(Collectors.toSet());

        List<OTStringList> permutationOptions = new ArrayList<>();
        refinementModel.setPermutationOptions(permutationOptions);
        Sets.powerSet(mutableNodes).stream()
            .filter(set -> !(
                set.size() == 0 || set.size() == mutableNodes.size()
            )).forEach(permutation -> permutationOptions.add(
            new OTStringList(
                permutation.stream()
                    .map(HasId::getId)
                    .collect(Collectors.toList())
            )
        ));

        refinementModel.setComponentSets(new ArrayList<>());

        for (TNodeTemplate detectorNode : detectorNodeTemplates) {
            getAllMappingsForDetectorNode(detectorNode, refinementModel).stream()
                .filter(mapping -> mapping.getRefinementElement() instanceof TNodeTemplate)
                .map(mapping -> (TNodeTemplate) mapping.getRefinementElement())
                .forEach(refinementNode ->
                    this.checkComponentMutability(refinementNode, detectorNode, refinementModel)
                );

            ModelUtilities.getIncomingRelationshipTemplates(refinementModel.getDetector(), detectorNode)
                .forEach(relation -> {
                    TNodeTemplate dependantNode = (TNodeTemplate) relation.getSourceElement().getRef();
                    if (refinementModel.getRelationMappings() != null) {
                        refinementModel.getRelationMappings().stream()
                            .filter(relMap -> relMap.getDetectorElement().equals(dependantNode))
                            .filter(relMap -> RefinementUtils.canRedirectRelation(relMap, relation, this.relationshipTypes, this.nodeTypes))
                            .findFirst()
                            .ifPresent(relMap ->
                                addMutabilityMapping(relMap.getDetectorElement(), relMap.getRefinementElement(), refinementModel)
                            );
                    }
                });
        }

        if (refinementModel.getPermutationMappings() == null) {
            this.mutabilityErrorReason = "No permutation mappings could be identified";
            logger.info(this.errorMessage, this.mutabilityErrorReason);
            return false;
        }

        List<String> unmappedDetectorNodes = refinementModel.getDetector().getNodeTemplates().stream()
            .filter(detectorNode -> !isStayPlaceholder(detectorNode, refinementModel))
            .filter(detectorNode -> !permutabilityMappingExistsForDetectorElement(detectorNode, refinementModel))
            .map(HasId::getId)
            .collect(Collectors.toList());

        if (unmappedDetectorNodes.size() > 0) {
            this.mutabilityErrorReason = "There are detector nodes which could not be mapped to a refinement node: "
                + String.join(", ", unmappedDetectorNodes);
            logger.info(this.errorMessage, this.mutabilityErrorReason);
            return false;
        }

        List<String> unmappedRefinementNodes = refinementModel.getRefinementStructure().getNodeTemplates().stream()
            .filter(refinementNode -> !isStayingRefinementElement(refinementNode, refinementModel))
            .filter(refinementNode -> !permutabilityMappingExistsForRefinementNode(refinementNode, refinementModel))
            .map(HasId::getId)
            .collect(Collectors.toList());

        if (unmappedRefinementNodes.size() > 0) {
            this.mutabilityErrorReason = "There are refinement nodes which could not be mapped to a detector node: "
                + String.join(", ", unmappedRefinementNodes);
            logger.info(this.errorMessage, this.mutabilityErrorReason);
            return false;
        }

        List<String> unmappableRelationIds = chekMutabilityOfDetectorRelations(refinementModel);

        if (unmappableRelationIds.size() > 0) {
            this.mutabilityErrorReason = "There are relations that cannot be redirected during the generation: "
                + String.join(", ", unmappableRelationIds);
            logger.info(this.errorMessage, this.mutabilityErrorReason);
            return false;
        }

        return true;
    }

    public  PermutationGenerator();

}