/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.commons.io.monitor;

import java.io.File;
import java.io.FileFilter;

import junit.framework.TestCase;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;

/**
 * {@link FileAlterationObserver} Test Case.
 */
public abstract class AbstractMonitorTestCase extends TestCase {

    /** File observer */
    protected FileAlterationObserver observer;

    /** Listener which collects file changes */
    protected CollectionFileListener listener;

    /** Test diretory name */
    protected String testDirName = null;

    /** Directory for test files */
    protected File testDir;

    /** Time in milliseconds to pause in tests */
    protected long pauseTime = 100L;

    /**
     * Construct a new test case.
     *
     * @param name The name of the test
     */
    public AbstractMonitorTestCase(String name) {
        super(name);
    }

    @Override
    protected void setUp() throws Exception {
        testDir = new File(new File("."), testDirName);
        if (testDir.exists()) {
            FileUtils.cleanDirectory(testDir);
        } else {
            testDir.mkdir();
        }

        IOFileFilter files = FileFilterUtils.fileFileFilter();
        IOFileFilter javaSuffix = FileFilterUtils.suffixFileFilter(".java");
        IOFileFilter fileFilter = FileFilterUtils.and(files, javaSuffix);
        
        IOFileFilter directories = FileFilterUtils.directoryFileFilter();
        IOFileFilter visible = HiddenFileFilter.VISIBLE;
        IOFileFilter dirFilter = FileFilterUtils.and(directories, visible);

        IOFileFilter filter = FileFilterUtils.or(dirFilter, fileFilter);
        
        createObserver(testDir, filter);
    }

    /**
     * Create a {@link FileAlterationObserver}.
     * 
     * @param file The directory to observe
     * @param fileFilter The file filter to apply
     */
    protected void createObserver(File file, FileFilter fileFilter) {
        observer = new FileAlterationObserver(file, fileFilter);
        observer.addListener(listener);
        observer.addListener(new FileAlterationListenerAdaptor());
        try {
            observer.initialize();
        } catch (Exception e) {
            fail("Observer init() threw " + e);
        }
    }

    @Override
    protected void tearDown() throws Exception {
        FileUtils.deleteDirectory(testDir);
    }

    /**
     * Check all the Collections are empty
     */
    protected void checkCollectionsEmpty(String label) {
        checkCollectionSizes("EMPTY-" + label, 0, 0, 0, 0, 0, 0);
    }

    /**
     * Check all the Collections have the expected sizes.
     */
    protected void checkCollectionSizes(String label, int dirCreate, int dirChange, int dirDelete, int fileCreate, int fileChange, int fileDelete) {
        label = label + "[" + listener.getCreatedDirectories().size() +
                        " " + listener.getChangedDirectories().size() +
                        " " + listener.getDeletedDirectories().size() +
                        " " + listener.getCreatedFiles().size() +
                        " " + listener.getChangedFiles().size() +
                        " " + listener.getDeletedFiles().size() + "]";
        assertEquals(label + ": No. of directories created",  dirCreate,  listener.getCreatedDirectories().size());
        assertEquals(label + ": No. of directories changed",  dirChange,  listener.getChangedDirectories().size());
        assertEquals(label + ": No. of directories deleted",  dirDelete,  listener.getDeletedDirectories().size());
        assertEquals(label + ": No. of files created", fileCreate, listener.getCreatedFiles().size());
        assertEquals(label + ": No. of files changed", fileChange, listener.getChangedFiles().size());
        assertEquals(label + ": No. of files deleted", fileDelete, listener.getDeletedFiles().size());
    }

    /**
     * Either creates a file if it doesn't exist or updates the last modified date/time
     * if it does.
     *
     * @param file The file to touch
     * @return The file
     */
    protected File touch(File file) {
        long lastModified = file.exists() ? file.lastModified() : 0;
        try {
            FileUtils.touch(file);
            file = new File(file.getParent(), file.getName());
            while (lastModified == file.lastModified()) {
                sleepHandleInterruped(pauseTime);
                FileUtils.touch(file);
                file = new File(file.getParent(), file.getName());
            }
        } catch (Exception e) {
            fail("Touching " + file + ": " + e);
        }
        sleepHandleInterruped(pauseTime);
        return file;
    }

    /**
     * Thread.sleep(timeInMilliseconds) - ignore InterruptedException
     */
    protected void sleepHandleInterruped(long timeInMilliseconds) {
        try {
            Thread.sleep(timeInMilliseconds);
        } catch(InterruptedException ie) {
            // ignore
        }
    }
}
