/*
* Copyright 2003-2012 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.ui;

import com.intellij.facet.FacetManager;
import com.intellij.ide.projectView.impl.ProjectAbstractTreeStructureBase;
import com.intellij.ide.projectView.impl.ProjectTreeStructure;
import com.intellij.ide.projectView.impl.ProjectViewPane;
import com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode;
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
import com.intellij.ide.projectView.impl.nodes.PsiFileNode;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import jetbrains.mps.fileTypes.MPSFileTypeFactory;
import jetbrains.mps.ide.platform.refactoring.ModelElementTargetChooser;
import jetbrains.mps.ide.platform.refactoring.NodeLocation;
import jetbrains.mps.ide.platform.refactoring.NodeLocation.NodeLocationChild;
import jetbrains.mps.ide.platform.refactoring.NodeLocation.NodeLocationRoot;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.idea.core.MPSDataKeys;
import jetbrains.mps.idea.core.facet.MPSConfigurationBean;
import jetbrains.mps.idea.core.facet.MPSFacet;
import jetbrains.mps.idea.core.facet.MPSFacetType;
import jetbrains.mps.idea.core.projectView.MPSPsiElementTreeNode;
import jetbrains.mps.idea.core.projectView.MPSPsiModelTreeNode;
import jetbrains.mps.persistence.DefaultModelRoot;
import jetbrains.mps.smodel.SModelFileTracker;
import jetbrains.mps.vfs.IFile;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.persistence.ModelRoot;

import javax.swing.JComponent;
import java.util.ArrayList;
import java.util.Set;

public class ModelOrNodeChooser extends ProjectViewPane implements ModelElementTargetChooser {
  private Project myProject;
  private JComponent myComponent;

  public ModelOrNodeChooser(Project project) {
    super(project);
    myProject = project;
    myComponent = createComponent();
  }

  @Override
  public JComponent getComponent() {
    return myComponent;
  }


  protected ProjectAbstractTreeStructureBase createStructure() {
    return new ProjectTreeStructure(myProject, ID) {
      public Object[] getChildElements(Object element) {
        ArrayList<Object> result = new ArrayList<Object>();
        for (Object o : super.getChildElements(element)) {
          if (o instanceof PsiFileNode) {
            PsiFileNode fileNode = (PsiFileNode) o;
            VirtualFile vFile = fileNode.getVirtualFile();
            if (vFile != null && vFile.getFileType().equals(MPSFileTypeFactory.MPS_FILE_TYPE)) {
              result.add(o);
            }
          } else if (o instanceof MPSPsiElementTreeNode || o instanceof MPSPsiModelTreeNode) {
            result.add(o);
          } else if (o instanceof PsiDirectoryNode) {
            VirtualFile virtualFile = ((PsiDirectoryNode) o).getVirtualFile();
            if (virtualFile != null && isModelRootOrParent(virtualFile)) {
              result.add(o);
            }
          } else if (o instanceof ProjectViewModuleNode && hasModelRoots(((ProjectViewModuleNode) o).getValue())) {
            result.add(o);
          }
        }
        return result.toArray();
      }
    };

  }

  private boolean hasModelRoots(Module module) {
    if (module == null) return false;
    MPSFacet mpsFacet = FacetManager.getInstance(module).getFacetByType(MPSFacetType.ID);
    if (mpsFacet == null || !mpsFacet.wasInitialized()) return false;

    MPSConfigurationBean configurationBean = mpsFacet.getConfiguration().getBean();
    return configurationBean != null && !(configurationBean.getModelRoots().isEmpty());
  }

  private boolean isModelRootOrParent(VirtualFile virtualFile) {
    if (!(virtualFile.isDirectory())) return false;

    Module module = ModuleUtil.findModuleForFile(virtualFile, myProject);
    if (module == null) return false;
    MPSFacet mpsFacet = FacetManager.getInstance(module).getFacetByType(MPSFacetType.ID);
    if (mpsFacet == null || !(mpsFacet.wasInitialized())) return false;

    MPSConfigurationBean configurationBean = mpsFacet.getConfiguration().getBean();
    if (configurationBean == null) return false;

    String url = virtualFile.getUrl();
    if (!LocalFileSystem.PROTOCOL.equals(VirtualFileManager.extractProtocol(url))) return false;

    String path = VirtualFileManager.extractPath(url);

    for (ModelRoot mr : configurationBean.getModelRoots()) {
      if (!(mr instanceof DefaultModelRoot)) continue;
      for (String sourceRoot : ((DefaultModelRoot) mr).getFiles(DefaultModelRoot.SOURCE_ROOTS)) {
        if (sourceRoot.startsWith(path)) return true;
        // TODO this gives some unneeded items (like src_gen when src is source root)
        if (path.startsWith(sourceRoot)) return true;
      }
    }
    return false;
  }

  @Override
  public NodeLocation getSelectedObject() {
    SRepository repository = ProjectHelper.getProjectRepository(myProject);
    if ((getSelectedNode() != null) && (getSelectedNode().getUserObject() instanceof MPSPsiElementTreeNode)) {
      return new NodeLocationChild(((MPSPsiElementTreeNode) (getSelectedNode().getUserObject())).getValue().getSNodeReference().resolve(repository));
    } else {
      Set<IFile> models = MPSDataKeys.MODEL_FILES.getData(this);
      if (models != null && models.size() == 1) {
        return new NodeLocationRoot(SModelFileTracker.getInstance(repository).findModel(models.iterator().next()));
      }
      // we could handle the case when we haven't got a model
      // perhaps, in plugin every directory (under module with MPS facet) should be transparently made into model
      // we could create the model right here (we're in EDT here)
      // This way we would allow move nodes into a package where no mps roots are present yet
    }
    return null;
  }
}
