/*
 * @(#)DiskIterator.java
 *
 * Copyright (c) 2002-2003 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 */

package com.jscape.util;

import java.io.File;

/**
 * This class provides Enumeration-like interface to files on filesystem.
 */

public class DiskIterator
        implements java.util.Enumeration {

    public static final boolean DEBUG = false;

    public static final int MAX_RECURSE = 100;
    public static final int MAX_FILES_IN_DIR = 1000;

    String dir = null;
    String currdir = null;
    int hier = 0;
    int[] pos = new int[MAX_RECURSE];

    // fileListCache
//    String [][] flc = new String[MAX_RECURSE][MAX_FILES_IN_DIR];


    boolean recurse, dirs, matchdirs;
    String filter;

    boolean hasMore = false;
    File nextFile = null;
    boolean consumed = true;


    String[] list = null;


    /**
     * Creates DiskIterator to iterate from <code>rootdir</code> and return files matching <code>filter</code>.
     * If <code>recurse</code> is true, DiskIterator will recurse <code>rootdir</code> subdirectories,
     * and if <code>dirs</code> is true, nextElement will return directory entries too.
     * If matchdirs is true, patterns will be matched using absolute path instead of filenames only. You may use this in conjunction with *
     * wildcard ; for example *.java will match all .java files in all dirs.
     * Wildcards allowed are ? (any character) and *(any number of any characters), and | (multiple patterns separator).
     */
    public DiskIterator(String rootdir, String filter, boolean recurse,
                        boolean dirs, boolean matchdirs) {
        File tmp = new File(rootdir);
        try {
            dir = tmp.getCanonicalPath();
        } catch (Exception e) {
            System.err.println(
                    "getCanonicalPath caused problem in DiskIterator, using getPath");
            dir = tmp.getAbsolutePath();
        }
        currdir = dir;
        for (int i = 0;
             i < MAX_RECURSE;
             i++) {
            pos[i] = 0;
        }
        this.filter = filter;
        this.recurse = recurse;
        this.dirs = dirs;
        this.matchdirs = matchdirs;
        File currf = new File(dir);
        list = currf.list();
        if (DEBUG) {
            try {
                System.out.print("DiskIterator : " + currf.getCanonicalPath());
            } catch (Exception e) {
                System.err.println(
                        "getCanonicalPath caused problem in DiskIterator, using getPath");
                System.out.println("Created : " + currf.getAbsolutePath());
            }
            System.out.print(recurse ? " recurse " : "");
            System.out.print(dirs ? " dirs " : "");
            System.out.print(matchdirs ? " matchdirs " : "");
            System.out.print(", ");
            System.out.print(list.length);
            System.out.println(" files found");
            System.out.println("Search pattern: " + filter);
        }
    }

    /**
     * Returns next file as defined in constructor. Note that return value is java.io.JFile.
     */
    public Object nextElement() {
        if (consumed) {
            doNext();
        }
        consumed = true;
        return nextFile;
    }

    public boolean hasMoreElements() {
        if (consumed) {
            doNext();
        }
        return hasMore;
    }

    private void doNext() {
        if (DEBUG) {
            System.out.println("Entered doNext : " + currdir);
        }

        while (hier != -1 && consumed == true) {
            if (DEBUG) {
                System.out.println("Looping : " + currdir);
                System.out.println("Hier : " + Integer.toString(hier));
                System.out.println("Search pattern: " + filter);
                System.out.println("Pos  : " + Integer.toString(pos[hier]));
            }
            // root dir is not returned.
            if (pos[hier] == list.length) {
                hier--;
                if (hier < 0) {
                    break;
                }
                if (currdir.endsWith(File.separator)) {
                    currdir =
                            currdir.substring(0,
                                              currdir.length()
                                                      - File.separator.length());
                }
                currdir =
                        currdir.substring(0, currdir.lastIndexOf(File.separator));
                File currf = new File(currdir);
                list = currf.list();
                continue;
            }
            String fn = list[pos[hier]];
//            System.out.println(currdir);
            File f = new File(currdir + File.separator + list[pos[hier]]);
            if (DEBUG) {
                System.out.println("Processing  : " + currdir + File.separator
                                           + list[pos[hier]]);
            }

            if (f.isDirectory()) {
                if (!recurse && !dirs) {
                    // skip this one
                    pos[hier] = pos[hier] + 1;
                    continue;
                } else if (recurse && !dirs) {
//descend without returning this dir
                    try {
                        currdir = f.getCanonicalPath();
                    } catch (Exception e) {
                        System.err.println(
                                "getCanonicalPath caused problem in DiskIterator, using getPath");
                        currdir = f.getAbsolutePath();
                    }

                    pos[hier] = pos[hier] + 1;
                    hier++;
                    pos[hier] = 0;
                    list = (new File(currdir)).list();
                    continue;
                } else if (!recurse && dirs) {
                    // dont descend but return the dir
                    pos[hier] = pos[hier] + 1;
                    nextFile = f;
                    consumed = false;
                    hasMore = true;
                } else {
                    // return the dir and descend
                    try {
                        currdir = f.getCanonicalPath();
                    } catch (Exception e) {
                        currdir = f.getAbsolutePath();
                    }
                    pos[hier] = pos[hier] + 1;
                    nextFile = f;
                    consumed = false;
                    hasMore = true;
                    hier++;
                    pos[hier] = 0;
                    list = (new File(currdir)).list();
                    return;
                }
            } else {
                boolean doit = false;
                if (matchdirs) {
                    doit = match(filter, f.getPath());
                } else {
                    doit = match(filter, f.getName());
                }
                if (doit) {
                    pos[hier] = pos[hier] + 1;
                    nextFile = f;
                    consumed = false;
                    hasMore = true;
                    return;
                } else {
                    pos[hier] = pos[hier] + 1;
                    continue;
                }
            }

        }
        if (hier == -1) {
            nextFile = null;
            hasMore = false;
            consumed = false;
        }
    }

    /// Checks whether a string matches a given wildcard pattern.
    // Only does ? and *, and multiple patterns separated by |.
    public boolean match(String pattern, String string) {

        for (int p = 0; true; ++p) {
            for (int s = 0; true; ++p, ++s) {
                boolean sEnd = (s >= string.length());
                boolean pEnd = (p >= pattern.length() || pattern.charAt(p) == '|');
                if (sEnd && pEnd) {
                    return true;
                }
                if (sEnd || pEnd) {
                    break;
                }
                if (pattern.charAt(p) == '?') {
                    continue;
                }
                if (pattern.charAt(p) == '*') {
                    int i;
                    ++p;
                    for (i = string.length();
                         i >= s;
                         --i) {
                        if (match(pattern.substring(p),
                                  string.substring(i)))  /* not quite right */ {
                            return true;
                        }
                    }
                    break;
                }
                if (pattern.charAt(p) != string.charAt(s)) {
                    break;
                }
            }
            p = pattern.indexOf('|', p);
            if (p == -1) {
                return false;
            }
        }
    }

}
