/*
 * Copyright 2003-2016 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.idea.core.psi.impl.file;

import com.intellij.lang.Language;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.FileViewProviderFactory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.SingleRootFileViewProvider;
import com.intellij.testFramework.LightVirtualFile;
import jetbrains.mps.fileTypes.MPSFileTypeFactory;
import jetbrains.mps.fileTypes.MPSLanguage;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.idea.core.psi.MPSNodeFileViewProvider;
import jetbrains.mps.idea.core.psi.MPSSingleRootFileViewProvider;
import jetbrains.mps.idea.core.psi.impl.MPSPsiModel;
import jetbrains.mps.idea.core.psi.impl.MPSPsiProvider;
import jetbrains.mps.idea.core.psi.impl.MPSPsiRootNode;
import jetbrains.mps.nodefs.MPSNodeVirtualFile;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.smodel.SModelFileTracker;
import jetbrains.mps.util.Computable;
import jetbrains.mps.vfs.FileSystem;
import jetbrains.mps.vfs.IFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.module.SRepository;

/**
 * User: fyodor
 * Date: 2/5/13
 */
public class MPSFileViewProviderFactory implements FileViewProviderFactory {
  @Override
  public FileViewProvider createFileViewProvider(@NotNull VirtualFile file, Language language, @NotNull final PsiManager manager, boolean physical) {
//    return new MyFileViewProvider(manager, file, physical);
    if (!(file instanceof MPSNodeVirtualFile)) {
      // todo revert to exception here, we should never be called with files like .mps (de-register their type from being MPSLanguage)
//      throw new IllegalStateException();
      return null;
    }
    return new MPSNodeFileViewProvider(manager, (MPSNodeVirtualFile) file);
  }

  private static class MyFileViewProvider extends MPSSingleRootFileViewProvider {
    public MyFileViewProvider(PsiManager manager, VirtualFile file, boolean physical) {
      super(manager, file, physical);
    }

    private MyFileViewProvider(PsiManager manager, VirtualFile copy, boolean b, Language baseLanguage) {
      super(manager, copy, b, baseLanguage);
    }

    @NotNull
    @Override
    public Language getBaseLanguage() {
      return MPSLanguage.INSTANCE;
    }

    @NotNull
    @Override
    public SingleRootFileViewProvider createCopy(@NotNull VirtualFile copy) {
      // TODO proper copy?
      return new MyFileViewProvider(getManager(), copy, false, getBaseLanguage());
    }

    @Override
    protected PsiFile createFile() {
      final VirtualFile virtualFile = getVirtualFile() instanceof LightVirtualFile ? ((LightVirtualFile) getVirtualFile()).getOriginalFile() : getVirtualFile();
      if (virtualFile == null ||
        (virtualFile.getFileType() != MPSFileTypeFactory.MPS_FILE_TYPE
          && virtualFile.getFileType() != MPSFileTypeFactory.MPS_ROOT_FILE_TYPE)) {
        return null;
      }
      final IFile modelFile = virtualFile.getFileType() == MPSFileTypeFactory.MPS_ROOT_FILE_TYPE
        ? FileSystem.getInstance().getFileByPath(virtualFile.getParent().getPath())
        : FileSystem.getInstance().getFileByPath(virtualFile.getPath());

      SRepository repository = ProjectHelper.getProjectRepository(getManager().getProject());
      PsiFile psiFile = new ModelAccessHelper(repository.getModelAccess()).runReadAction(new Computable<PsiFile>() {
        @Override
        public PsiFile compute() {
          SModel descr = SModelFileTracker.getInstance(repository).findModel(modelFile);
          if (descr != null) {
            // force loading the model and updating the PSI tree at this time
            MPSPsiProvider mpsPsiProvider = MPSPsiProvider.getInstance(getManager().getProject());
            MPSPsiModel psiModel = mpsPsiProvider.getPsi(descr);

            if (virtualFile.getFileType() == MPSFileTypeFactory.MPS_ROOT_FILE_TYPE) {
              for (PsiElement element : psiModel.getChildren()) {
                if (((MPSPsiRootNode) element).getVirtualFile().equals(virtualFile) || ((MPSPsiRootNode) element).getVirtualFile().getName().equals(virtualFile.getNameWithoutExtension())) {
                  return (MPSPsiRootNode) element;
                }
              }
              return null;
            }

            return new FileSourcePsiFile(MyFileViewProvider.this, descr.getReference(), descr.getModelName());
          }
          return null;
        }
      });
      return psiFile;
    }
  }
}
