/*******************************************************************************
 * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
 ******************************************************************************/ 
package org.jboss.tools.common.model.util;

import java.util.*;

import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.JavaModelException;
import org.jboss.tools.common.model.*;
import org.jboss.tools.common.model.filesystems.FileSystemsHelper;
import org.jboss.tools.common.model.filesystems.XFileObject;
import org.jboss.tools.common.model.plugin.ModelPlugin;

public class AccessibleClasses implements ISimpleTree {
    private static final int JAVA = 1;
    private static final int PROJECT = 2;
    private int mode = 2;
    private XModel model;
    IJavaProject javaProject;
    private Map<String,SortedSet<String>> map = null;
    private static SortedSet<String> EMPTY = new TreeSet<String>();
    private Comparator<String> comparator = new ACComparator<String>();

    public AccessibleClasses(XModel model, boolean useproject) {
        this(model, (useproject && model != null) ? 3 : 1);
    }

    protected XModel getModel() {
        return model;
    }

    public AccessibleClasses(XModel model, int mode) {
        this.model = (XModel)model;
        javaProject = EclipseResourceUtil.getJavaProject(EclipseResourceUtil.getProject(model.getRoot()));
        this.mode = mode;
        map = ((mode & PROJECT) != 0) ? new HashMap<String,SortedSet<String>>() : null;
    }

    public String[] getContent(String packagename) {
        if(map == null) return AccessibleJava.getInstance().getContent(packagename);
        SortedSet<String> o = map.get(packagename);
        if(o == null) {
            load(packagename);
            o = map.get(packagename);
        }
        if(o == EMPTY) return null;
        String[] r = o.toArray(new String[o.size()]);
        Arrays.sort(r, comparator);
        return r;
    }

    protected void load(String packagename) {
        SortedSet<String> list = new TreeSet<String>();
        String[] s = ((mode & JAVA) == 0) ? null :
                     AccessibleJava.getInstance().getContent(packagename);
        if(s != null) for (int i = 0; i < s.length; i++) list.add(s[i]);
        boolean exists = buildPackage(list, packagename);
        if(s == null && !exists) {
            map.put(packagename, EMPTY);
        } else {
            map.put(packagename, list);
        }
    }

    private static String exts = ".class.bo.java.cls."; //$NON-NLS-1$

    protected String extensions() {
        return exts;
    }

    private boolean buildPackage(SortedSet<String> list, String packagename) {
    	String pkg = packagename.toLowerCase();
        if(pkg.endsWith(".cvs")) return false; //$NON-NLS-1$
        if(pkg.endsWith(".svn")) return false; //$NON-NLS-1$
        boolean exists = false;
        XModelObject fs = FileSystemsHelper.getFileSystems(model);
        if(fs == null) return false;
        try {
        	exists = getChildren(list, packagename);
        } catch (JavaModelException e) {
        	ModelPlugin.getPluginLog().logError(e);
        }
        return exists;
    }
    
    private boolean getChildren(SortedSet<String> list, String packagename) throws JavaModelException {
    	if(javaProject == null || !javaProject.exists()) return false;
        boolean exists = false;
        boolean r = "%root%".equals(packagename); //$NON-NLS-1$
        String jp = (r) ? "" : packagename; //$NON-NLS-1$
    	IPackageFragmentRoot[] rs = javaProject.getPackageFragmentRoots();
    	for (int i = 0; i < rs.length; i++) {
    		IParent pf = (r) ? rs[i] : rs[i].getPackageFragment(packagename);
    		if(pf == null || !((IJavaElement)pf).exists()) continue;
    		exists = true;
    		IJavaElement[] cs = pf.getChildren();
    		process(list, cs, jp);
    		if(!r) process2(list, rs[i].getChildren(), jp);
    	}
    	return exists;
    }
    
    private void process(SortedSet<String> list, IJavaElement[] cs, String jp) throws JavaModelException {
        for (int j = 0; j < cs.length; j++) {
            boolean isp = cs[j] instanceof IPackageFragment;
            String n = cs[j].getElementName();
            if(n.length() == 0 && isp) {
            	process(list, ((IPackageFragment)cs[j]).getChildren(), jp);
            } else {
            	if(isp) {
            		if(n.indexOf('.') >= 0) continue;
            		n += "."; //$NON-NLS-1$
            	} else {
            		int d = n.lastIndexOf('.');
            		if(d >= 0) {
            			String ext = n.substring(d + 1);
            			n = n.substring(0, d);
            			if(!extensions().contains("." + ext + ".")) continue; //$NON-NLS-1$ //$NON-NLS-2$
            		}
            	}
            	if(accepts(jp, n)) {
            		list.add(n);
            	}
            }
        }
    }
    private void process2(SortedSet<String> list, IJavaElement[] cs, String jp) throws JavaModelException {
        for (int j = 0; j < cs.length; j++) {
        	String n = cs[j].getElementName();
        	if(!n.startsWith(jp + ".")) continue; //$NON-NLS-1$
        	n = n.substring(jp.length() + 1);
            boolean isp = cs[j] instanceof IPackageFragment;
        	if(isp) {
        		if(n.indexOf('.') >= 0) continue;
        		n += "."; //$NON-NLS-1$
        	} else {
        		int d = n.lastIndexOf('.');
        		if(d >= 0) {
        			String ext = n.substring(d + 1);
        			n = n.substring(0, d);
        			if(!extensions().contains("." + ext + ".")) continue; //$NON-NLS-1$ //$NON-NLS-2$
        		}
        	}
        	if(accepts(jp, n)) {
        		list.add(n);
        	}
        }
    }

    protected boolean accepts(String packagename, String n) {
        return !n.equalsIgnoreCase("CVS.") && !n.equalsIgnoreCase("svn."); //$NON-NLS-1$ //$NON-NLS-2$
    }

}

class ACComparator<T> implements Comparator<T> {
    public int compare(Object o1, Object o2) {
        String s1 = (String)o1;
        String s2 = (String)o2;
        if(s1.endsWith(".") && !s2.endsWith(".")) return -1; //$NON-NLS-1$ //$NON-NLS-2$
        if(s2.endsWith(".") && !s1.endsWith(".")) return 1; //$NON-NLS-1$ //$NON-NLS-2$
        return s1.compareToIgnoreCase(s2);
    }

    public boolean equals(Object obj) {
        return this == obj;
    }

}

