/*
 * Copyright 2015 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp;

import com.google.javascript.rhino.TypeI;

/**
 * The different strategies for matching the {@code TypeI} of nodes.
 */
public enum TypeMatchingStrategy {

  /**
   * Matches type or any subtype. Matches types with different nullability/voidability. Allows loose
   * matches.
   */
  LOOSE(true, true, true),

  /**
   * Matches type or any subtype. Does not match types with different nullability/voidability.
   * Allows loose matches.
   */
  STRICT_NULLABILITY(true, false, true),

  /**
   * Matches type or any subtype. Does not match types with different nullability/voidability.
   * Does not allow loose matches.
   */
  SUBTYPES(true, false, false),

  /**
   * Does not match subtypes. Does not match types with different nullability/voidability. Does not
   * allow loose matches.
   */
  EXACT(false, false, false);

  private final boolean allowSubtypes;
  private final boolean ignoreNullability;
  private final boolean allowLooseMatches;

  private TypeMatchingStrategy(
      boolean allowSubtypes, boolean ignoreNullability, boolean allowLooseMatches) {
    this.allowSubtypes = allowSubtypes;
    this.ignoreNullability = ignoreNullability;
    this.allowLooseMatches = allowLooseMatches;
  }

  public MatchResult match(TypeI templateType, TypeI type) {
    if (templateType.isUnknownType()) {
      // If the template type is '?', then any type is a match and this is not considered a loose
      // match.
      return new MatchResult(true, false);
    }

    if (type == null || type.isUnknownType() || type.isTop()) {
      return new MatchResult(allowLooseMatches, allowLooseMatches);
    }

    if (allowSubtypes) {
      if (ignoreNullability) {
        type = type.restrictByNotNullOrUndefined();
      }
      if (type.isSubtypeOf(templateType)) {
        return new MatchResult(true, false);
      }
    }

    boolean nullableMismatch = templateType.isNullable() != type.isNullable();
    boolean voidableMismatch = templateType.isVoidable() != type.isVoidable();
    if (!ignoreNullability && (nullableMismatch || voidableMismatch)) {
      return new MatchResult(false, false);
    }

    return new MatchResult(type.isEquivalentTo(templateType), false);
  }

  /**
   * The result of comparing two different {@code TypeI} instances.
   */
  public static class MatchResult {
    private final boolean isMatch;
    private final boolean isLooseMatch;

    public MatchResult(boolean isMatch, boolean isLooseMatch) {
      this.isMatch = isMatch;
      this.isLooseMatch = isLooseMatch;
    }

    public boolean isMatch() {
      return isMatch;
    }

    public boolean isLooseMatch() {
      return isLooseMatch;
    }
  }
}

@@ -2081,36 +2081,32 @@
 h.%0A      return 
-new 
 MatchResult(true
@@ -2096,37 +2096,30 @@
  MatchResult
-(true, false)
+.MATCH
 ;%0A    %7D%0A%0A   
@@ -2192,32 +2192,16 @@
  return 
-new MatchResult(
 allowLoo
@@ -2213,28 +2213,57 @@
 ches
-, allowLoose
+ ? MatchResult.LOOSE_MATCH : 
 Match
+R
 es
-)
+ult.NO_MATCH
 ;%0A  
@@ -2434,36 +2434,32 @@
 %0A        return 
-new 
 MatchResult(true
@@ -2457,21 +2457,14 @@
 sult
-(true, false)
+.MATCH
 ;%0A  
@@ -2711,36 +2711,32 @@
  %7B%0A      return 
-new 
 MatchResult(fals
@@ -2734,22 +2734,17 @@
 sult
-(false, false)
+.NO_MATCH
 ;%0A  
@@ -2763,24 +2763,8 @@
 urn 
-new MatchResult(
 type
@@ -2792,24 +2792,59 @@
 ateType)
-, false)
+ ? MatchResult.MATCH : MatchResult.NO_MATCH
 ;%0A  %7D%0A%0A 
@@ -2935,20 +2935,12 @@
 lic 
-static class
+enum
  Mat
@@ -2958,219 +2958,46 @@
     
-private final boolean isMatch;%0A    private final boolean isLooseMatch;%0A%0A    public MatchResult(boolean isMatch, boolean isLooseMatch) %7B%0A      this.isMatch = isMatch;%0A      this.isLooseMatch = isLooseMatch;%0A    %7D
+MATCH,%0A    NO_MATCH,%0A    LOOSE_MATCH;%0A
 %0A%0A  
@@ -3038,23 +3038,32 @@
  return 
-isMatch
+this != NO_MATCH
 ;%0A    %7D%0A
@@ -3112,28 +3112,35 @@
  return 
-isLooseMatch
+this == LOOSE_MATCH
 ;%0A    %7D%0A

Make MatchResult an enum