/*
 * Copyright 2004-2009 the Seasar Foundation and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.seasar.mayaa.impl.util;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * @author Taro Kato (Gluegent, Inc.)
 */
public class FileSearchIterator implements Iterator, Runnable {

    private File _root;
    private FilenameFilter _filenameFilter;
    private boolean _done = false;
    private File _current;
    private Thread _stepFindThread;
    private boolean _gotNext = false;

    private volatile boolean _doFindNext;
    private volatile boolean _doFindNextDone;

    public FileSearchIterator(File rootDir) {
        this(rootDir, null);
    }

    public FileSearchIterator(File rootDir, FilenameFilter filenameFilter) {
        _root = rootDir;
        _filenameFilter = filenameFilter;
    }

    public File getRoot() {
        return _root;
    }

    public FilenameFilter getFilenameFilter() {
        return _filenameFilter;
    }

    protected void startThread() {
        if (_stepFindThread == null) {
            _done = false;
            _doFindNext = false;
            _stepFindThread = new Thread(this);
            _stepFindThread.setName("fileSearch:root=" + _root + ":filter=" + _filenameFilter);
            _stepFindThread.setDaemon(true);
            _stepFindThread.start();
        }
    }

    protected void stopThread() {
        _stepFindThread = null;
    }

    public boolean hasNext() {
        if (_done && _current == null) {
            return false;
        }
        startThread();
        _doFindNext = true;
        while (_done == false && _doFindNextDone == false) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                break;
            }
        }
        boolean found = _doFindNextDone;
        _doFindNext = false;
        while (_done == false && _doFindNextDone == true) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                break;
            }
        }
        if (_done && found == false) {
            return false;
        }
        _gotNext = found;
        return found;
    }

    public Object next() {
        if (_gotNext == false) {
            if (hasNext() == false) {
                throw new NoSuchElementException();
            }
        }
        _gotNext = false;
        return _current;
    }

    private Thread _internalCurrentThread;

    public void run() {
        try {
            _done = false;
            _current = null;
            _internalCurrentThread = Thread.currentThread();
            findFile(_root, _root);
        } catch(InterruptedException e) {
            // no operation
        }
        _done = true;
    }

    protected void findFile(File root, File dir)
            throws InterruptedException {
        File[] files;
        if (_filenameFilter == null) {
            files = dir.listFiles();
        } else {
            files = dir.listFiles(_filenameFilter);
        }
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            if (file.isFile() && file.isHidden() == false) {
                while (_doFindNext == false
                        && _internalCurrentThread == _stepFindThread) {
                    Thread.sleep(1);
                }
                if (_internalCurrentThread != _stepFindThread) {
                    return;
                }
                _current = file;
                _doFindNextDone = true;
                while (_doFindNext &&
                        _internalCurrentThread == _stepFindThread) {
                    Thread.sleep(1);
                }
                _doFindNextDone = false;
                if (_internalCurrentThread != _stepFindThread) {
                    return;
                }
            }
        }
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            if (file.isDirectory()) {
                findFile(root, file);
                if (_internalCurrentThread != _stepFindThread) {
                    return;
                }
            }
        }
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }

    protected void finalize() throws Throwable {
        stopThread();
        super.finalize();
    }

}
