package de.renew.navigator.io;

import java.io.File;
import java.net.URL;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import CH.ifa.draw.io.CombinationFileFilter;
import de.renew.io.FileFilterCreator;
import de.renew.navigator.models.Directory;
import de.renew.navigator.models.Leaf;
import de.renew.navigator.models.TreeElement;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;


/**
 * @author Konstantin Simon Maria Moellers
 * @version 2015-10-09
 */
public class FilesystemIOLoaderTest {
    private FileFilterBuilder _builder;
    private FilesystemIOLoader _loader;
    private CombinationFileFilter _ff;
    private File _fixtures;
    private File _fig1;
    private File _fig2;

    private static TreeElement findChildByName(Collection<TreeElement> elements, String name) {
        for (TreeElement element : elements) {
            if (element.getName().equals(name)) {
                return element;
            }
        }

        return null;
    }

    @BeforeEach
    public void setUp() throws Exception {
        _builder = mock(FileFilterBuilderImpl.class);
        _loader = new FilesystemIOLoader(_builder);

        // Create file filter.
        _ff = new CombinationFileFilter("Stub");
        _ff.add(FileFilterCreator.createRNWFileFilter());
        when(_builder.buildFileFilter()).thenReturn(_ff);

        // Load fixtures.
        final URL resource = getClass().getResource(getBaseDirectory());
        _fixtures = new File(resource.toURI());
        String pathToFixtures =
            _fixtures.getAbsolutePath() + FileSystems.getDefault().getSeparator();
        _fig1 = new File(pathToFixtures + "fig1.rnw");
        _fig2 = new File(pathToFixtures + "fig2.rnw");

        // Setup for testRefreshPath()
        if (!_fig1.exists() && !_fig2.renameTo(_fig1)) {
            fail(
                "Manual intervention needed: Please rename" + pathToFixtures
                    + "fig2.rnw to fig1.rnw");
        }

    }

    private String getBaseDirectory() {
        return "/" + Paths.get("de", "renew", "navigator", "io", "fixtures");
    }

    @Test
    public void testLoadFile() throws Exception {
        ProgressListener listener = mock(ProgressListener.class);
        Path path = Paths.get(getBaseDirectory(), "testfile.txt");
        final File testFile = new File(path.toString());

        final TreeElement result = _loader.loadPath(testFile, listener);

        verifyNoMoreInteractions(_builder);
        verifyNoMoreInteractions(listener);

        // Assert content of results.
        assertEquals(result.getFile(), testFile);
        assertEquals(result.getName(), testFile.getName());
        assertNull(result.getParent());
        assertTrue(result instanceof Leaf);
    }

    @Test
    public void testLoadDirectory() throws Exception {
        ProgressListener listener = mock(ProgressListener.class);

        final TreeElement result = _loader.loadPath(_fixtures, listener);

        verify(_builder).buildFileFilter();

        // Assert content of results.
        assertEquals(result.getFile(), _fixtures);
        assertEquals(result.getName(), _fixtures.getName());
        assertNull(result.getParent());
        assertTrue(result instanceof Directory);

        final Directory dir = (Directory) result;
        assertEquals(dir.getChildren().size(), _fixtures.listFiles(_ff).length);
        assertEquals(6, dir.getChildren().size());
        assertTrue(dir.isOpened());
        assertNull(dir.getType());

        for (TreeElement child : dir.getChildren()) {
            assertSame(child.getParent(), dir);
        }
    }

    @Test
    void testLoadPath() {
        //given
        ProgressListener mockListener = mock(ProgressListener.class);

        //when/then
        assertThat(_fig1.isFile()).isEqualTo(true);
        assertThat(_loader.loadPath(_fixtures, mockListener)).isInstanceOf(Directory.class);
    }

    @Test
    void testRefreshPath() {
        //given
        ProgressListener mockListener = mock(ProgressListener.class);
        final Directory result = (Directory) _loader.loadPath(_fixtures, mockListener);

        //when
        assertThat(_fig1.renameTo(_fig2)).isEqualTo(true);
        _loader.refreshPath(result, _fixtures, mockListener);

        //then
        assertThat(result.getChildren()).hasSize(6);
        assertThat(findChildByName(result.getChildren(), "fig1.rnw")).isNull();
        assertThat(findChildByName(result.getChildren(), "fig2.rnw")).isNotNull();
    }
}