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

import java.util.HashMap;
import java.util.HashSet;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;

import pl.zalewski.mikolaj.symbdev.SymbdevPlugin;
import pl.zalewski.mikolaj.symbdev.properties.SymbianPropertiesFile;

/**
 * These class manages the information associated with a given MMP
 * and stored in the .symbbuild file.
 * 
 * @author mikolaj
 */
public class MMPSupplementSupport implements IAutoEntriesMapper {

    public static final String SUPPLEMENT_ROOT=SymbdevPlugin.getUniqueIdentifier()+".mmpsupplement.";
    public static final String PROP_ADDED_FILES="addedfiles"; 
    public static final String PROP_ADDED_DIRS="addeddirs"; 
    public static final String PROP_DONT_ADD="dontadd";
    
    static final String EMPTY_DIR="/empty/";
    static final IPath EMPTY_DIR_PATH=new Path(EMPTY_DIR);
    
    public MMPSupplementSupport(IFile file)
    	throws CoreException
    {
        fProperties=SymbianPropertiesFile.getForProject(file.getProject());
        fPath=file.getProjectRelativePath();
        StringBuffer b;
        fMmpPathEncoded=encodePath(fPath);
        loadSupplementSourceDirectories();
        for (int i=0; i<fDirPaths.length; i++)
            loadDirectoryCache(fDirPaths[i]);
        setDirCache(fAutoDontAdd, fAutoDontAddInv, EMPTY_DIR_PATH,
                loadDirectoryEntriesForType(PROP_DONT_ADD, EMPTY_DIR_PATH));
    }
    
    static public IPath[] getSupplementedMMPs(IProject project)
    	throws CoreException
    {
        SymbianPropertiesFile prop=SymbianPropertiesFile.getForProject(project);
        String[] items=prop.getSubProperties(SUPPLEMENT_ROOT);
        IPath[] ret=new IPath[items.length];
        for (int i=0; i<items.length; i++)
            ret[i]=decodePath(items[i]);
        return ret;
    }
        
    public void addSupplementSourceDirectory(IPath path) {
        clearSourceDirectoryProp(path);
        fProperties.setProperty(getSupplementDirProp(path), "");
        loadSupplementSourceDirectories();
        loadDirectoryCache(path);
    }
    
    public void removeSupplementSourceDirectory(IPath path) {
        clearDirectoryCache(path);
        clearSourceDirectoryProp(path);
        loadSupplementSourceDirectories();
    }
    
    public IPath[] getSupplementSourceDirectories() {
        return (IPath[])fDirPaths.clone();
    }
    
    public boolean isSelectedDir(IPath path) {
        if (fSelectedDirs==null) {
            fSelectedDirs=new HashSet();
            for (int i=0; i<fDirPaths.length; i++)
                fSelectedDirs.add(fDirPaths[i]);
        }
        return fSelectedDirs.contains(path);
    }

    //===================
    //    AUTO FILES
    //===================
    public IPath[] getAutoFilesForDirectory(IPath directory) {
        return (IPath[])((IPath[])fAutoFilesCache.get(directory)).clone();
    }
        
    public void setSupplementDirectoryAutoFiles(IPath directory, IPath[] paths) {
        setDirectoryEntiresForType(PROP_ADDED_FILES,
                fAutoFilesCache, fAutoFilesInvCache,
                directory, paths);
    }
    
    public void addSupplementDirectoryAutoFile(IPath directory, IPath file) {
        IPath[] paths=getAutoFilesForDirectory(directory);
        IPath[] newpaths=addPathToArray(paths, file);
        setSupplementDirectoryAutoFiles(directory, newpaths);
    }
    
    public void removeSupplementDirectoryAutoFile(IPath directory, IPath file) {
        IPath[] paths=getAutoFilesForDirectory(directory);
        IPath[] newpaths=removePathFromArray(paths, file);
        if (newpaths!=null)
            setSupplementDirectoryAutoFiles(directory, newpaths);
    }
    
    public IPath getDirectoryForAutoFile(IPath file) {
        return (IPath)fAutoFilesInvCache.get(file);
    }
    
    //===============
    //   AUTO DIRS
    //===============
    public IPath[] getAutoDirsForDirectory(IPath directory) {
        return (IPath[])((IPath[])fAutoDirsCache.get(directory)).clone();
    }
    
    public void setSupplementDirectoryAutoDirs(IPath directory, IPath[] paths) {
        setDirectoryEntiresForType(PROP_ADDED_DIRS,
                fAutoDirsCache, fAutoDirsInvCache,
                directory, paths);
    }
    
    public void addSupplementDirectoryAutoDir(IPath directory, IPath file) {
        IPath[] paths=getAutoDirsForDirectory(directory);
        IPath[] newpaths=addPathToArray(paths, file);
        setSupplementDirectoryAutoDirs(directory, newpaths);
    }
    
    public void removeSupplementDirectoryAutoDir(IPath directory, IPath file) {
        IPath[] paths=getAutoDirsForDirectory(directory);
        IPath[] newpaths=removePathFromArray(paths, file);
        if (newpaths!=null)
            setSupplementDirectoryAutoDirs(directory, newpaths);
    }

    public IPath getDirectoryForAutoDir(IPath dir) {
        return (IPath)fAutoDirsInvCache.get(dir);
    }    
    
    //===================================
    //    FILES/DIRECTORIES NOT TO ADD
    //===================================
    protected IPath[] getSupplementDontAdd() {
        return (IPath[])((IPath[])fAutoDontAdd.get(EMPTY_DIR_PATH)).clone();
    }
    
    public void setSupplementDontAdd(IPath[] paths) {
        setDirectoryEntiresForType(PROP_DONT_ADD, fAutoDontAdd,
                fAutoDontAddInv, new Path(EMPTY_DIR), paths);
    }
    
    public void addSupplementDontAdd(IPath file) {
        IPath[] paths=getSupplementDontAdd();
        IPath[] newpaths=addPathToArray(paths, file);
        setSupplementDontAdd(newpaths);
    }
    
    public void removeSupplementDontAdd(IPath file) {
        IPath[] paths=getSupplementDontAdd();
        IPath[] newpaths=removePathFromArray(paths, file);
        if (newpaths!=null)
            setSupplementDontAdd(newpaths);
    }

    public boolean isDontAdd(IPath path) {
        return fAutoDontAddInv.get(path)!=null;
    }
    
    //==================
    
    static protected String encodePath(IPath path) {
        String string=path.toString();
        string=string.replaceAll("%", "%0025");
        string=string.replaceAll("\\.", "%002E");
        string=string.replaceAll("=", "%003D");
        return string;
    }
    
    static protected IPath decodePath(String str) {
        str=str.replaceAll("%002E", "\\.");
        str=str.replaceAll("%003D", "=");
        str=str.replaceAll("%0025", "%");
        return new Path(str);
    }

    protected void loadSupplementSourceDirectories() {
        String prefix=SUPPLEMENT_ROOT+fMmpPathEncoded+".dirs.";
        String[] dirs=fProperties.getSubProperties(prefix);
        int count=0;
        for (int i=0; i<dirs.length; i++)
            if (!dirs[i].startsWith("/"))
                count++;
        fDirPaths=new IPath[count];
        int pos=0;
        for (int i=0; i<dirs.length; i++)
            if (!dirs[i].startsWith("/"))
                fDirPaths[pos++]=decodePath(dirs[i]);
        fSelectedDirs=null;   // clear cache
    }

    protected void clearSourceDirectoryProp(IPath path) {
        String prefix=getSupplementDirProp(path);
        fProperties.removeProperty(prefix);
        String[] items=fProperties.getProperties(prefix+'.');
        for (int i=0; i<items.length; i++)
            fProperties.removeProperty(items[i]);
        fAutoDirsCache.remove(path);
        fAutoFilesCache.remove(path);
    }
    
    protected void setDirectoryEntiresForType(String type,
            HashMap cache, HashMap invCache, IPath directory,
            IPath[] paths) {
        String prop=getSupplementDirProp(directory)+"."+type;
        StringBuffer value=new StringBuffer();
        for (int i=0; i<paths.length; i++) {
            if (i>0)
                value.append(',');
            value.append(encodePath(paths[i]));
        }
        fProperties.setProperty(prop, value.toString());
        clearDirCacheForType(cache, invCache, directory);
        setDirCache(cache, invCache, directory, paths);
    }
    
    protected void setDirCache(HashMap cache, HashMap invCache, IPath directory, IPath[] entries) {
        cache.put(directory, entries);
        for (int i=0; i<entries.length; i++)
            invCache.put(entries[i], directory);
    }
    
    protected IPath[] loadDirectoryEntriesForType(String type, IPath directory) {
        String prop=getSupplementDirProp(directory)+"."+type;
        String items=fProperties.getProperty(prop, "");
        if (items.length()==0)
            return new IPath[0];
        String[] strings=items.split(",");
        IPath[] ret=new IPath[strings.length];
        for (int i=0; i<ret.length; i++)
            ret[i]=decodePath(strings[i]);
        return ret;
    }
    
    protected void loadDirectoryCache(IPath directory) {
        setDirCache(fAutoFilesCache, fAutoFilesInvCache, directory,
                loadDirectoryEntriesForType(PROP_ADDED_FILES, directory));
        setDirCache(fAutoDirsCache, fAutoDirsInvCache, directory,
                loadDirectoryEntriesForType(PROP_ADDED_DIRS, directory));
    }
    
    protected void clearDirCacheForType(HashMap cache, HashMap invCache, IPath path) {
        Object o=cache.get(path);
        IPath[] entries=(IPath[])o;
        for (int i=0; i<entries.length; i++)
            invCache.remove(entries[i]);
        cache.remove(path);
    }
    
    protected void clearDirectoryCache(IPath directory) {
        clearDirCacheForType(fAutoFilesCache, fAutoFilesInvCache, directory);
        clearDirCacheForType(fAutoDirsCache, fAutoDirsInvCache, directory);        
    }

    protected String getSupplementDirProp(IPath dir) {
        String direnc=encodePath(dir);
        return SUPPLEMENT_ROOT+fMmpPathEncoded+".dirs."+direnc;
    }
    
    protected IPath[] addPathToArray(IPath[] paths, IPath file) {
        IPath[] newpaths=new IPath[paths.length+1];
        for (int i=0; i<paths.length; i++)
            newpaths[i]=paths[i];
        newpaths[paths.length]=file;
        return newpaths;
    }
    
    protected IPath[] removePathFromArray(IPath[] paths, IPath file) {
        boolean found=false;
        for (int i=0; i<paths.length; i++) {
            if (!found && paths[i].equals(file)) {
                found=true;
                continue;
            }
            if (found)
                paths[i-1]=paths[i];
        }
        
        if (!found)
            return null;
        
        IPath[] newpaths;
        newpaths=new IPath[paths.length-1];
        System.arraycopy(paths, 0, newpaths, 0, paths.length-1);
        return newpaths;
    }

    SymbianPropertiesFile fProperties;
    IPath fPath;
    String fMmpPathEncoded;
    IPath[] fDirPaths;
    HashSet fSelectedDirs;
    
    /**
     * An IPath -> IPath[] mapping of directories to files added
     * automatically for this directory
     */
    protected HashMap fAutoFilesCache=new HashMap();
    /**
     * An IPath -> IPath mapping of files added automatically to
     * the directory it was added because
     */
    protected HashMap fAutoFilesInvCache=new HashMap();
    
    /**
     * An IPath -> IPath[] mapping of directories added automatically for
     * each source directory
     */
    protected HashMap fAutoDirsCache=new HashMap();
    protected HashMap fAutoDirsInvCache=new HashMap();
    /**
     * An IPath -> IPath[] mapping of files/directories not to be added
     * automatically for each source directory
     */
    protected HashMap fAutoDontAdd=new HashMap();
    protected HashMap fAutoDontAddInv=new HashMap();
}
