package click.kamil.springstatemachineexporter.analysis.pipeline;

/**
 * Shared depth and fallback budget for analysis traversals. Replaces scattered magic-number limits.
 */
public final class ResolutionBudget {

    public static final int DEFAULT_METHOD_RETURN = 20;
    public static final int DEFAULT_ACCESSOR_INLINE = 25;
    public static final int DEFAULT_DATAFLOW = 50;
    public static final int DEFAULT_UNWRAP = 5;
    public static final int DEFAULT_CONSTRUCTOR = 10;
    public static final int DEFAULT_RETURNED_CIC = 50;
    public static final int DEFAULT_GLOBAL_FIELD_SCAN = 10;

    private final int methodReturnDepth;
    private final int accessorInlineDepth;
    private final int dataflowDepth;
    private final int unwrapDepth;
    private final int constructorDepth;
    private final int returnedCicDepth;
    private final int globalFieldScanLimit;
    private final boolean allowGlobalFieldScan;

    private ResolutionBudget(
            int methodReturnDepth,
            int accessorInlineDepth,
            int dataflowDepth,
            int unwrapDepth,
            int constructorDepth,
            int returnedCicDepth,
            int globalFieldScanLimit,
            boolean allowGlobalFieldScan) {
        this.methodReturnDepth = methodReturnDepth;
        this.accessorInlineDepth = accessorInlineDepth;
        this.dataflowDepth = dataflowDepth;
        this.unwrapDepth = unwrapDepth;
        this.constructorDepth = constructorDepth;
        this.returnedCicDepth = returnedCicDepth;
        this.globalFieldScanLimit = globalFieldScanLimit;
        this.allowGlobalFieldScan = allowGlobalFieldScan;
    }

    public static ResolutionBudget defaults() {
        return new ResolutionBudget(
                DEFAULT_METHOD_RETURN,
                DEFAULT_ACCESSOR_INLINE,
                DEFAULT_DATAFLOW,
                DEFAULT_UNWRAP,
                DEFAULT_CONSTRUCTOR,
                DEFAULT_RETURNED_CIC,
                DEFAULT_GLOBAL_FIELD_SCAN,
                false);
    }

    public static ResolutionBudget withGlobalFieldScan() {
        return defaults().enableGlobalFieldScan();
    }

    public ResolutionBudget enableGlobalFieldScan() {
        return new ResolutionBudget(
                methodReturnDepth,
                accessorInlineDepth,
                dataflowDepth,
                unwrapDepth,
                constructorDepth,
                returnedCicDepth,
                globalFieldScanLimit,
                true);
    }

    public ResolutionBudget child() {
        return new ResolutionBudget(
                methodReturnDepth - 1,
                accessorInlineDepth - 1,
                dataflowDepth - 1,
                unwrapDepth,
                constructorDepth - 1,
                returnedCicDepth - 1,
                globalFieldScanLimit,
                allowGlobalFieldScan);
    }

    public boolean isMethodReturnExhausted(int depth) {
        return depth > methodReturnDepth;
    }

    public boolean isAccessorInlineExhausted(int depth) {
        return depth > accessorInlineDepth;
    }

    public boolean isDataflowExhausted(int depth) {
        return depth > dataflowDepth;
    }

    public boolean isUnwrapExhausted(int depth) {
        return depth > unwrapDepth;
    }

    public boolean isConstructorExhausted(int depth) {
        return depth > constructorDepth;
    }

    public boolean isReturnedCicExhausted(int depth) {
        return depth > returnedCicDepth;
    }

    public int globalFieldScanLimit() {
        return globalFieldScanLimit;
    }

    public boolean allowGlobalFieldScan() {
        return allowGlobalFieldScan;
    }
}
