/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package usda.weru.util;

import de.schlichtherle.io.File;
import java.io.FileFilter;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import javax.swing.tree.DefaultMutableTreeNode;

/**
 *
 * @author joelevin
 */
public class FileTreeNode extends DefaultMutableTreeNode{
    
    protected File c_file;
    protected FileFilter c_filter;
    protected Comparator <File> c_comparator;
    protected String c_displayName;
    protected Map <File, FileTreeNode> c_childrenCache;
    private boolean c_createdChildren;

    
    public FileTreeNode(File file) {
        this(file, null);
    }
    
    public FileTreeNode(File file, FileFilter filter) {
        this(file, filter, null);
    }

    public FileTreeNode(File file, FileFilter filter, Comparator<File> comparator) {
        c_file = file;        
        c_filter = filter;
        c_comparator = comparator != null ? comparator : new DefaultComparator();
    }

    public File getFile() {
        return c_file;
    }

    public void setFile(File file){
        if (file == null || !file.equals(c_file)){
            c_file = file;                        
        }
        refreshChildren();
    }

    protected synchronized void initChildren(){
        if (!c_createdChildren){            
            refreshChildren();
        }
    }

    public void refreshChildren(){
        if(c_createdChildren){
            removeAllChildren();
        }
        
        FileTreeNode[] newchildren = createChildren();
        for (FileTreeNode child : newchildren){
            insert(child, children != null ? children.size() : 0);
        }

        c_createdChildren = true;
        
        //keep the cache clean.
        Enumeration e = children();
        while (e.hasMoreElements()){
            Object o = e.nextElement();
            if (!c_childrenCache.containsValue(o)){
                c_childrenCache.values().remove(o);
            }
        }
        
    }

    



    public FileTreeNode[] getChildren(){
        FileTreeNode[] temp = new FileTreeNode[getChildCount()];
        for(int i = 0; i < getChildCount(); i++){
            temp[i] = (FileTreeNode) getChildAt(i);
        }
        return temp;
    }


    private FileTreeNode[] createChildren() {

            File[] files = c_file != null ? c_file.listFiles(c_filter, c_file.getArchiveDetector()) : null;

            if (files == null) {
                files = new File[0];
            }
            else if(files.length == 1 && c_file.isArchive() && files[0].isDirectory()){
                //hide a single directory in a zip file
                files = files[0].listFiles(c_filter, files[0].getArchiveDetector());
                if(files == null){
                    files = new File[0];
                }
            }
            
            if (c_comparator != null){
                Arrays.sort(files, c_comparator);
            }

            FileTreeNode[] children = new FileTreeNode[files.length];

            for (int i = 0; i < files.length; i++){
                children[i] = createChild(files[i], c_filter, c_comparator);
            }            

            return children;
    }

    
    private FileTreeNode createChild(File file, FileFilter filter, Comparator <File> comparator){
        if (c_childrenCache == null){
            c_childrenCache = new HashMap <File, FileTreeNode> ();
        }
        FileTreeNode child = c_childrenCache.get(file);
        if (child == null){
            child = new FileTreeNode(file, c_filter, c_comparator);
            c_childrenCache.put(file, child);
        }
        return child;
    }

    @Override
    public int getChildCount() {
        initChildren();
        return super.getChildCount();
    }


    @Override
    public boolean getAllowsChildren() {
        return c_file != null ? c_file.isDirectory() : false;
    }

    @Override
    public boolean isLeaf() {
        return !getAllowsChildren();
    }

   
    public String getDisplayName(){
        return c_displayName;
    }
    
    public void setDisplayName(String name){
        c_displayName = name;
    }

    @Override
    public String toString() {
        if (c_displayName != null){
            return c_displayName;
        }
        else if (c_file != null){
            return c_file.getName();
        }
        else{
            return super.toString();
        }
    }
    
    private static class DefaultComparator implements Comparator <File>, Serializable{

        @Override
        public int compare(File a, File b) {
            if (a.isDirectory() == b.isDirectory()){
                return a.compareTo(b);
            }            
            else if (a.isDirectory()){
                return -1;
            }
            else{
                return 1;
            }
        }
        
    }

    public FileTreeNode getDecendantNode(File file){
        if (c_file == null){
            return null;
        }
        String currentPath = c_file.getCanOrAbsPath();
        String filePath = file.getCanOrAbsPath();
        if (filePath.startsWith(currentPath) && filePath.length() > currentPath.length()){
            Stack <String> parts = fileParts(new File(filePath.substring(currentPath.length() + 1)));

            return findDecendantNode(this, parts);       
        }
        else{
            return null;
        }
    }
    
    private static FileTreeNode findDecendantNode(FileTreeNode parent, Stack<String> parts){
        Enumeration<FileTreeNode> enumChildren = parent.children();
        String partToFind = parts.pop();
        while (enumChildren.hasMoreElements()){
            FileTreeNode temp = enumChildren.nextElement();

            //does this node match the part?
            if (partToFind.equals(temp.getFile().getName())){
                //part matches

                //is this the final part?
                if (parts.empty()){
                    return temp;
                }
                else{
                    return findDecendantNode(temp, parts);
                }
            }
        }
        return null;
    }


    private static Stack<String> fileParts (File file){
        Stack <String> parts = new Stack <String>();
        while (file != null){
            parts.push(file.getName());

            file = file.getParentFile() != null ? new File(file.getParentFile()) : null;
        }
        return parts;
    }
    
    
} 
    
   


