/*
 * Created on 2004-11-20
 *
 * Part of the Symbian development plug-in for Eclipse
 * Copyright (C) Mikołaj Zalewski, 2004
 */
package pl.zalewski.mikolaj.symbdev.core.mmpmodel;

import java.util.HashMap;
import java.util.Iterator;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ISaveContext;
import org.eclipse.core.resources.ISaveParticipant;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;

import pl.zalewski.mikolaj.symbdev.SymbdevPlugin;
import pl.zalewski.mikolaj.symbdev.SymbianPrjNature;
import pl.zalewski.mikolaj.symbdev.core.internal.mmpmodel.FileMMPModel;
import pl.zalewski.mikolaj.symbdev.core.internal.mmpmodel.MMPDocumentProvider;
import pl.zalewski.mikolaj.symbdev.core.internal.mmpmodel.MMPSupplementSupport;

/**
 * The Symbian build information is stored in *.MMP files which are referenced
 * from the bld.inf file. This class provides access to these information
 * 
 * @author mikolaj
 */
public class BldInfModel {
    public static final String PROBLEM_MARKER_ID="pl.zalewski.mikolaj.symbdev.core.problem";
    
    static {
        try {
	        ResourcesPlugin.getWorkspace().addSaveParticipant(SymbdevPlugin.getDefault(),
	                new ModelsSaveParticipant());
        }
        catch (CoreException e) {
            SymbdevPlugin.log(e);
        }
    }
    
    public BldInfModel() {
        super();
    }
    
    /**
     * Returns the MMP model for a specified file. The project containg
     * the file must have a SymbianPrjNature nature (to get the SDK root
     * configuration)
     * @param file The MMP file.
     * @return The model for the specified
     */
    static public IMMPModel getMMPModelForFile(IFile file) throws CoreException
    {
        if (!file.getProject().hasNature(SymbianPrjNature.NATURE_ID))
            return null;
        FileMMPModel m=(FileMMPModel)fModelsForFile.get(file);
        if (m==null)
        {
            fDocProvider.connect(new FileEditorInput(file));
        	IDocument doc=fDocProvider.getDocument(new FileEditorInput(file));
        	fDocProvider.getAnnotationModel(new FileEditorInput(file)).
        		connect(doc);   // the saveDocument will fail if this is not done
        	m=new FileMMPModel(doc, file);
        	fModelsForFile.put(file, m);
        }
        return m;
    }
    
    static public IDocumentProvider getDocumentProvider() {
        return fDocProvider;
    }
    
    /**
     * Called after the MMP text editor disconnects from the file
     * @param input The input of the editor
     * @param errorShell A shell used as a parent in case of an error dialog
     */
    static public void editorDisconnected(IEditorInput input, Shell errorShell) {
        if (!(input instanceof IFileEditorInput))
            return;
        if (!isFileOpenedInEditor((IFileEditorInput)input)) {
            // this file is used only by the model. If it is still
            // dirty then the user has chosen not to save it
            FileMMPModel m=getModelForFile(((IFileEditorInput)input).getFile());
            
            try {
                if (m!=null)
                    m.resetDocument();
            }
            catch (CoreException e) {
                SymbdevPlugin.showError(e, "Error while reseting the document",
                        errorShell);
            }            
        }
    }
    
    /**
     * Prepares the models for a build:
     * <ul>
     *   <li>if a MMP file has source directories, the model is
     * loaded to checks if some source needs to be added</li> 
     *   <li>if the models are not opened in an editor then they are
     * saved</li>
     * </ul>
     * @param project The project to be built
     */
    static public void prepareForBuild(IProject project) throws CoreException {
        IPath[] mmps=MMPSupplementSupport.getSupplementedMMPs(project);
        for (int i=0; i<mmps.length; i++) {
            IResource file=project.findMember(mmps[i]);
            if (file!=null && file instanceof IFile)
                getMMPModelForFile((IFile)file);
        }
        
        saveModelsNotInEditor(project);
    }
    
    static protected void saveModelsNotInEditor(IProject project)
    	throws CoreException
    { 
        Iterator it=fModelsForFile.keySet().iterator();
        while (it.hasNext()) {
            IFile file=(IFile)it.next();
            if (project!=null && file.getProject()!=project)
                continue;
            FileEditorInput input=new FileEditorInput(file);
            if (!isFileOpenedInEditor(input) && fDocProvider.canSaveDocument(input)) {
                // If a document is not opened in an editor, the user
                // will not be asked if he wants to save. We will
                // automatically save it 
                FileMMPModel m=(FileMMPModel)fModelsForFile.get(file);
                fDocProvider.saveDocument(new NullProgressMonitor(),
                        input, m.getDocument(), true);
            }
        }
    }
    
    static protected FileMMPModel getModelForFile(IFile file) {
        return (FileMMPModel)fModelsForFile.get(file);        
    }

    static protected boolean isFileOpenedInEditor(IFileEditorInput input)
    {
        int useCount=fDocProvider.getUseCount(input);
        if (useCount==0)
            return false;
        
        if (useCount==1) {
            IFile file=((IFileEditorInput)input).getFile();
            FileMMPModel m=getModelForFile(file);
            if (m!=null) return false;
            	else return true;
        } else return true;        
    }
    
    static protected boolean isFileOpenedInEditor(IFile file) {
        return isFileOpenedInEditor(new FileEditorInput(file));
    }
    
    static class ModelsSaveParticipant implements ISaveParticipant {

        public void doneSaving(ISaveContext context) {
        }

        public void prepareToSave(ISaveContext context) throws CoreException {
        }

        public void rollback(ISaveContext context) {
        }

        public void saving(ISaveContext context) throws CoreException {
            BldInfModel.saveModelsNotInEditor(null);
        }
        
    }
    
    static MMPDocumentProvider fDocProvider=new MMPDocumentProvider();
    static HashMap fModelsForFile=new HashMap();
}
