/*******************************************************************************
 * Copyright (c) 2013 itemis AG (http://www.itemis.eu) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *******************************************************************************/
package org.eclipse.emf.ecore.xcore.scoping.types;

import java.util.List;

import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.xtext.AbstractTypeScope;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.EObjectDescription;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.impl.ImportNormalizer;
import org.eclipse.xtext.util.Strings;

import com.google.common.collect.Iterables;

public class TypeScopeWithWildcardImports extends AbstractXcoreScope
{
  private final ImportNormalizer[][] imports;

  private final AbstractTypeScope typeScope;

  private final AbstractXcoreScope parent;

  public TypeScopeWithWildcardImports(ImportNormalizer[][] imports, AbstractTypeScope typeScope)
  {
    this.imports = imports;
    this.typeScope = typeScope;
    this.parent = null;
  }

  public TypeScopeWithWildcardImports(List<ImportNormalizer> imports, AbstractTypeScope typeScope, AbstractXcoreScope parent)
  {
    this.imports = new ImportNormalizer[][] { imports.toArray(new ImportNormalizer[imports.size()]) };
    this.typeScope = typeScope;
    this.parent = parent;
  }

  @Override
  public IEObjectDescription getSingleElement(QualifiedName name)
  {
    for (int i = 0; i < imports.length; i++)
    {
      ImportNormalizer[] chunk = imports[i];
      IEObjectDescription result = null;
      for (int j = 0; j < chunk.length; j++)
      {
        ImportNormalizer normalizer = chunk[j];
        QualifiedName resolvedName = normalizer.resolve(name);
        if (resolvedName != null)
        {
          IEObjectDescription candidate = typeScope.getSingleElement(resolvedName, true);
          if (candidate != null)
          {
            if (result == null)
            {
              result = candidate;
            }
            else
            {
              return null;
            }
          }
        }
      }
      if (result != null)
      {
        return result;
      }
    }
    return getSingleElementFromParent(name);
  }

  protected IEObjectDescription getSingleElementFromParent(QualifiedName name)
  {
    return parent != null ? parent.getSingleElement(name) : typeScope.getSingleElement(name);
  }

  @Override
  protected void doGetElements(JvmType type, List<IEObjectDescription> result)
  {
    if (type instanceof JvmDeclaredType)
    {
      JvmDeclaredType declaredType = (JvmDeclaredType) type;
      String packageName = declaredType.getPackageName();
      if (!Strings.isEmpty(packageName))
      {
        int packageNameEnd = packageName.length() + 1;
        QualifiedName qualifiedPackageName = QualifiedName.create(Strings.split(packageName, '.'));
        int qualifiedPackageNameSegmentCount = qualifiedPackageName.getSegmentCount();
        QualifiedName withDot = null;
        String withDollar = null;
        for (int i = 0; i < imports.length; i++)
        {
          ImportNormalizer[] chunk = imports[i];
          for (int j = 0; j < chunk.length; j++)
          {
            ImportNormalizer normalizer = chunk[j];
            QualifiedName namespacePrefix = normalizer.getImportedNamespacePrefix();
            if (namespacePrefix.equals(qualifiedPackageName))
            {
              if (withDot == null)
              {
                withDot = QualifiedName.create(Strings.split(type.getQualifiedName('.'), '.'));
                withDollar = type.eContainer() instanceof JvmType ? type.getQualifiedName('$').substring(packageNameEnd) : null;
              }
              result.add(EObjectDescription.create(withDot.skipFirst(qualifiedPackageNameSegmentCount), type));
              if (withDollar != null)
              {
                result.add(EObjectDescription.create(withDollar, type));
              }
            }
          }
        }
      }
      if (parent != null)
      {
        parent.doGetElements(type, result);
      }
      else
      {
        Iterables.addAll(result, typeScope.getElements(type));
      }
    }
  }
}