// Path: src/main/java/com/nfl/glitr/exception/GlitrException.java
// public class GlitrException extends RuntimeException {
// 
//     public GlitrException(String message) {
//         super(message);
//     }
// 
//     public GlitrException(String message, Throwable cause) {
//         super(message, cause);
//     }
// }
// 
// Path: src/main/java/com/nfl/glitr/registry/schema/GlitrFieldDefinition.java
// public class GlitrFieldDefinition extends FieldDefinition {
// 
//     private Set<GlitrMetaDefinition> metaDefinitions = new HashSet<>();
// 
// 
//     public GlitrFieldDefinition(String name, Type type) {
//         super(name, type);
//     }
// 
//     public GlitrFieldDefinition(String name, Set<GlitrMetaDefinition> metaDefinitions) {
//         super(name, null);
//         this.metaDefinitions = metaDefinitions;
//     }
// 
//     public GlitrFieldDefinition(String name, Type type, Set<GlitrMetaDefinition> metaDefinitions) {
//         super(name, type);
//         this.metaDefinitions = metaDefinitions;
//     }
// 
//     public Set<GlitrMetaDefinition> getMetaDefinitions() {
//         return metaDefinitions;
//     }
// 
//     public void setMetaDefinitions(Set<GlitrMetaDefinition> metaDefinitions) {
//         this.metaDefinitions = metaDefinitions;
//     }
// 
//     public void addMetaDefinitions(GlitrMetaDefinition metaDefinition) {
//         if (this.metaDefinitions == null) {
//             this.metaDefinitions = new HashSet<>();
//         }
//         this.metaDefinitions.add(metaDefinition);
//     }
// }
// 
// Path: src/main/java/com/nfl/glitr/registry/schema/GlitrMetaDefinition.java
// public class GlitrMetaDefinition {
// 
//     private String name;
//     private Object value;
// 
// 
//     public GlitrMetaDefinition(String name, Object value) {
//         this.name = name;
//         this.value = value;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public void setName(String name) {
//         this.name = name;
//     }
// 
//     public Object getValue() {
//         return value;
//     }
// 
//     public void setValue(Object value) {
//         this.value = value;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
// 
//         if (o == null || getClass() != o.getClass()) return false;
// 
//         GlitrMetaDefinition that = (GlitrMetaDefinition) o;
// 
//         return new EqualsBuilder()
//                 .append(name, that.name)
//                 .isEquals();
//     }
// 
//     @Override
//     public int hashCode() {
//         return new HashCodeBuilder(17, 37)
//                 .append(name)
//                 .toHashCode();
//     }
// }
// 
// Path: src/main/java/com/nfl/glitr/registry/schema/GraphQLConnectionList.java
// public class GraphQLConnectionList extends GraphQLList {
//     public GraphQLConnectionList(GraphQLType wrappedType) {
//         super(wrappedType);
//     }
// }
// 
// Path: src/main/java/com/nfl/glitr/util/NodeUtil.java
// public static final String COMPLEXITY_FORMULA_KEY = "complexity_formula";
// 
// Path: src/main/java/com/nfl/glitr/util/NodeUtil.java
// public static final String COMPLEXITY_IGNORE_KEY = "complexity_ignore";


import com.nfl.glitr.exception.GlitrException;
import com.nfl.glitr.registry.schema.GlitrFieldDefinition;
import com.nfl.glitr.registry.schema.GlitrMetaDefinition;
import com.nfl.glitr.registry.schema.GraphQLConnectionList;
import graphql.language.*;
import graphql.parser.InvalidSyntaxException;
import graphql.parser.Parser;
import graphql.parser.antlr.GraphqlLexer;
import graphql.schema.*;
import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonToken;
import org.antlr.v4.runtime.InputMismatchException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.nfl.glitr.util.NodeUtil.COMPLEXITY_FORMULA_KEY;
import static com.nfl.glitr.util.NodeUtil.COMPLEXITY_IGNORE_KEY;
import static org.apache.commons.lang3.StringUtils.defaultString;

    private GraphQLFieldDefinition getRootSchemaField(String query, String entryPoint) {
        if (schema == null) {
            return null;
        }

        boolean mutationQuery = isMutation(query);
        GraphQLObjectType rootType = mutationQuery ? schema.getMutationType() : schema.getQueryType();
        return rootType.getFieldDefinition(entryPoint);
    }

    @SuppressWarnings("unchecked")
    private QueryComplexityNode queryScoreDetails(QueryComplexityNode parentNode, int depth, Map<String, Double> nestedContext, Map<String, Object> queryVariables) {
        Map<String, Double> context = refreshQueryContext(nestedContext, parentNode, queryVariables);
        depth = !parentNode.isIgnore() ? (depth + 1) : depth;

        double childScores = 0d;
        for (QueryComplexityNode currentChild : parentNode.getChildren()) {
            QueryComplexityNode childComplexityNode = queryScoreDetails(currentChild, depth , context, queryVariables);
            childScores += childComplexityNode.getTotalWeight();
        }

        double currentNodeScore = 0d;
        if (!parentNode.isIgnore()) {
            Map<String, Double> nodeContext = buildContext(parentNode, nestedContext, depth, childScores, queryVariables);
            currentNodeScore = extractMultiplierFromListField(parentNode, nodeContext, queryVariables);
            parentNode.setWeight(currentNodeScore);
        }
        parentNode.setTotalWeight(getTotalWeight(parentNode, queryVariables, currentNodeScore, childScores));

// Path: src/main/java/com/nfl/glitr/exception/GlitrException.java
// public class GlitrException extends RuntimeException {
// 
//     public GlitrException(String message) {
//         super(message);
//     }
// 
//     public GlitrException(String message, Throwable cause) {
//         super(message, cause);
//     }
// }
// 
// Path: src/main/java/com/nfl/glitr/registry/schema/GlitrFieldDefinition.java
// public class GlitrFieldDefinition extends FieldDefinition {
// 
//     private Set<GlitrMetaDefinition> metaDefinitions = new HashSet<>();
// 
// 
//     public GlitrFieldDefinition(String name, Type type) {
//         super(name, type);
//     }
// 
//     public GlitrFieldDefinition(String name, Set<GlitrMetaDefinition> metaDefinitions) {
//         super(name, null);
//         this.metaDefinitions = metaDefinitions;
//     }
// 
//     public GlitrFieldDefinition(String name, Type type, Set<GlitrMetaDefinition> metaDefinitions) {
//         super(name, type);
//         this.metaDefinitions = metaDefinitions;
//     }
// 
//     public Set<GlitrMetaDefinition> getMetaDefinitions() {
//         return metaDefinitions;
//     }
// 
//     public void setMetaDefinitions(Set<GlitrMetaDefinition> metaDefinitions) {
//         this.metaDefinitions = metaDefinitions;
//     }
// 
//     public void addMetaDefinitions(GlitrMetaDefinition metaDefinition) {
//         if (this.metaDefinitions == null) {
//             this.metaDefinitions = new HashSet<>();
//         }
//         this.metaDefinitions.add(metaDefinition);
//     }
// }
// 
// Path: src/main/java/com/nfl/glitr/registry/schema/GlitrMetaDefinition.java
// public class GlitrMetaDefinition {
// 
//     private String name;
//     private Object value;
// 
// 
//     public GlitrMetaDefinition(String name, Object value) {
//         this.name = name;
//         this.value = value;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public void setName(String name) {
//         this.name = name;
//     }
// 
//     public Object getValue() {
//         return value;
//     }
// 
//     public void setValue(Object value) {
//         this.value = value;
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
// 
//         if (o == null || getClass() != o.getClass()) return false;
// 
//         GlitrMetaDefinition that = (GlitrMetaDefinition) o;
// 
//         return new EqualsBuilder()
//                 .append(name, that.name)
//                 .isEquals();
//     }
// 
//     @Override
//     public int hashCode() {
//         return new HashCodeBuilder(17, 37)
//                 .append(name)
//                 .toHashCode();
//     }
// }
// 
// Path: src/main/java/com/nfl/glitr/registry/schema/GraphQLConnectionList.java
// public class GraphQLConnectionList extends GraphQLList {
//     public GraphQLConnectionList(GraphQLType wrappedType) {
//         super(wrappedType);
//     }
// }
// 
// Path: src/main/java/com/nfl/glitr/util/NodeUtil.java
// public static final String COMPLEXITY_FORMULA_KEY = "complexity_formula";
// 
// Path: src/main/java/com/nfl/glitr/util/NodeUtil.java
// public static final String COMPLEXITY_IGNORE_KEY = "complexity_ignore";


// Path: src/main/java/com/nfl/glitr/calculator/QueryComplexityCalculator.java
import com.nfl.glitr.exception.GlitrException;
import com.nfl.glitr.registry.schema.GlitrFieldDefinition;
import com.nfl.glitr.registry.schema.GlitrMetaDefinition;
import com.nfl.glitr.registry.schema.GraphQLConnectionList;
import graphql.language.*;
import graphql.parser.InvalidSyntaxException;
import graphql.parser.Parser;
import graphql.parser.antlr.GraphqlLexer;
import graphql.schema.*;
import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonToken;
import org.antlr.v4.runtime.InputMismatchException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.nfl.glitr.util.NodeUtil.COMPLEXITY_FORMULA_KEY;
import static com.nfl.glitr.util.NodeUtil.COMPLEXITY_IGNORE_KEY;
import static org.apache.commons.lang3.StringUtils.defaultString;


    private GraphQLFieldDefinition getRootSchemaField(String query, String entryPoint) {
        if (schema == null) {
            return null;
        }

        boolean mutationQuery = isMutation(query);
        GraphQLObjectType rootType = mutationQuery ? schema.getMutationType() : schema.getQueryType();
        return rootType.getFieldDefinition(entryPoint);
    }

    @SuppressWarnings("unchecked")
    private QueryComplexityNode queryScoreDetails(QueryComplexityNode parentNode, int depth, Map<String, Double> nestedContext, Map<String, Object> queryVariables) {
        Map<String, Double> context = refreshQueryContext(nestedContext, parentNode, queryVariables);
        depth = !parentNode.isIgnore() ? (depth + 1) : depth;

        double childScores = 0d;
        for (QueryComplexityNode currentChild : parentNode.getChildren()) {
            QueryComplexityNode childComplexityNode = queryScoreDetails(currentChild, depth , context, queryVariables);
            childScores += childComplexityNode.getTotalWeight();
        }

        double currentNodeScore = 0d;
        if (!parentNode.isIgnore()) {
            Map<String, Double> nodeContext = buildContext(parentNode, nestedContext, depth, childScores, queryVariables);
            currentNodeScore = extractMultiplierFromListField(parentNode, nodeContext, queryVariables);
            parentNode.setWeight(currentNodeScore);
        }
        parentNode.setTotalWeight(getTotalWeight(parentNode, queryVariables, currentNodeScore, childScores));

        return parentNode;