/*
 * Copyright 2003-2014 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 org.jetbrains.mps.util;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import org.jetbrains.mps.openapi.language.SConcept;
import org.jetbrains.mps.openapi.language.SInterfaceConcept;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;

/**
 * Iterates hierarchy of concepts, breadth-first. Iterator doesn't support removals.
 * Return values are not unique, e.g. if an interface is implemented by few concepts, it is reported several times.
 * Wrap with {@link org.jetbrains.mps.util.UniqueIterator} if necessary.
 *
 * Given
 *  ConceptA implements I1, I2 and
 *  ConceptB extends ConceptA implements I3, I4;
 *  Interface I3 extends I5;
 *  Interface I5 extends I1;
 *
 *  and ConceptB as starting point, yields:
 *   { ConceptB, I3, I4 } - level 0 for ConceptB, { ConceptA, I1, I2 } - level 1 from ConceptA , { I5 } level 1 from I3, {I1} - level 2 from I5.
 *
 * Note, this class is similar to {@link org.jetbrains.mps.openapi.language.SConceptUtil}, except for explicit walk algorithm and OOP-friendly style.
 *
 * @author Artem Tikhomirov
 */
public class BreadthConceptHierarchyIterator implements Iterable<SAbstractConcept>, Iterator<SAbstractConcept>{
  private final Deque<SAbstractConcept> myQueue = new ArrayDeque<SAbstractConcept>();
  private final SAbstractConcept myStart;

  public BreadthConceptHierarchyIterator(@NotNull SAbstractConcept start) {
    myStart = start;
  }

  @Override
  public Iterator<SAbstractConcept> iterator() {
    myQueue.clear();
    myQueue.add(myStart);
    return this;
  }

  @Override
  public boolean hasNext() {
    return !myQueue.isEmpty();
  }

  @Override
  public SAbstractConcept next() {
    SAbstractConcept rv = myQueue.removeFirst();
    final Iterable<SInterfaceConcept> superInterfaces;
    final SConcept parentConcept;
    if (rv instanceof SInterfaceConcept) {
      superInterfaces = ((SInterfaceConcept) rv).getSuperInterfaces();
      parentConcept = null;
    } else {
      assert rv instanceof SConcept;
      parentConcept = ((SConcept) rv).getSuperConcept();
      superInterfaces = ((SConcept) rv).getSuperInterfaces();
    }
    // for a concept, implemented interfaces  are deemed as 'this' level, while extended concept constitutes jump to a next depth
    for (SAbstractConcept i : superInterfaces) {
      myQueue.addLast(i);
    }
    if (parentConcept != null) {
      myQueue.addLast(parentConcept);
    }
    return rv;
  }

  @Override
  public void remove() {
    throw new UnsupportedOperationException();
  }
}
