/*
 * Copyright 2003-2011 JetBrains s.r.o.
 *
 * 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 jetbrains.mps.typesystem.newTypesystem.structure;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class Matcher {
  public static <Meta, Target> MatchResult<Meta, Target> match(Type<Meta, Target> type1, Type<Meta, Target> type2) {
    MatchResult<Meta, Target> result = new MatchResult<Meta, Target>();
    if (type1 == type2) return result;
    if (type1 == null || type2 == null) return MatchResult.FALSE;

    //vars
    if (type1.isVar() || type2.isVar()) {
      result.addEquation(type1, type2);
      return result;
    }

    //matching
    if (type1.getMeta() != type2.getMeta()) {
      return MatchResult.FALSE;
    }
    Set<String> referenceRoles = type1.getReferenceRoles();
    referenceRoles.addAll(type2.getReferenceRoles());
    for (String role : referenceRoles) {
      if (type1.getReferent(role) != type2.getReferent(role)) {
        return MatchResult.FALSE;
      }
    }
    //children
    Set<String> childRoles = type1.getChildRoles();
    childRoles.addAll(type2.getChildRoles());
    for (String role : childRoles) {
      List<Type<Meta, Target>> children1 = type1.getChildren(role);
      List<Type<Meta, Target>> children2 = type2.getChildren(role);
      if (children1.size() != children2.size()) {
        return MatchResult.FALSE;
      }
      for (Iterator<Type<Meta, Target>> i1 = children1.iterator(), i2 = children2.iterator(); i1.hasNext() && i2.hasNext();) {
        Type<Meta, Target> child1 = i1.next();
        Type<Meta, Target> child2 = i2.next();
        MatchResult<Meta, Target> childResult = match(child1, child2);
        if (!childResult.isCorrectMatch()) {
          return MatchResult.FALSE;
        } else {
          result.addEquations(childResult);
        }
      }
    }

    return result;
  }
}
