/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.io;

import static net.ranides.assira.junit.NewAssert.*;
import static org.junit.Assert.assertEquals;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;

import net.ranides.assira.collection.query.CQuery;
import org.junit.Assume;
import org.junit.Test;

import net.ranides.assira.system.HostSystem;

/**
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public class PathUtilsTest {
    
    @Test
    public void testGetExtension() {
        assertEquals(".txt", PathUtils.getLongExtension("file.txt"));
        assertEquals(".txt", PathUtils.getLongExtension("\\base\\file.txt"));
        assertEquals(".txt", PathUtils.getLongExtension("/base/file.txt"));
        assertEquals(".log.txt", PathUtils.getLongExtension("/base/file.log.txt"));
    }

    @Test
    public void testGetSimpleName() {
        assertEquals("file", PathUtils.getSimpleName("file.txt"));
        assertEquals("file", PathUtils.getSimpleName("file.info.txt"));

        assertEquals("file", PathUtils.getSimpleName("/ms/file.txt"));
        assertEquals("file", PathUtils.getSimpleName("/ms/file.info.txt"));

        assertEquals("file", PathUtils.getSimpleName("c:/ms/file"));
        assertEquals("file", PathUtils.getSimpleName("c:/ms/file.txt"));
    }

    @Test
    public void testGetSimpleName_W32() {
        Assume.assumeTrue(HostSystem.WINDOWS.detected());

        assertEquals("file", PathUtils.getSimpleName("c:\\ms\\file.txt"));
        assertEquals("file", PathUtils.getSimpleName("c:\\ms\\file.info.txt"));
    }

    @Test
    public void testGetSimpleName_NIX() {
        Assume.assumeFalse(HostSystem.WINDOWS.detected());

        assertEquals("file", PathUtils.getSimpleName("/root/ms/file.info."));
        assertEquals("file", PathUtils.getSimpleName("/root/ms/file.info.txt"));

        assertEquals("file", PathUtils.getSimpleName("/c:/ms/file.info."));
        assertEquals("file", PathUtils.getSimpleName("/c:/ms/file.info.txt"));

        assertEquals("c:\\ms\\file", PathUtils.getSimpleName("c:\\ms\\file.txt"));
        assertEquals("c:\\ms\\file", PathUtils.getSimpleName("c:\\ms\\file.info.txt"));

    }

    @Test
    public void testChangeExtensionString() {
        assertEquals(Paths.get("file.txt"), PathUtils.changeLongExtension("file.txt", ".txt"));
        assertEquals(Paths.get("file.dat"), PathUtils.changeLongExtension("file.txt", ".dat"));
        assertEquals(Paths.get("file.txt.dat"), PathUtils.changeLongExtension("file.txt.info", ".txt.dat"));
        assertEquals(Paths.get("file.dat"), PathUtils.changeLongExtension("file.txt.info", ".dat"));
        assertEquals(Paths.get("file.dat"), PathUtils.changeLongExtension("file", ".dat"));

        assertEquals(Paths.get("base","name","file.txt"), PathUtils.changeLongExtension("base/name/file.txt", ".txt"));
    }

    @Test
    public void testChangeExtensionString_W32() {
        Assume.assumeTrue(HostSystem.WINDOWS.detected());

        assertEquals(Paths.get("base","name","file.dat"), PathUtils.changeLongExtension("base\\name\\file.txt", ".dat"));
        assertEquals(Paths.get("c:","base","name","file.dat"), PathUtils.changeLongExtension("c:\\base\\name\\file.txt", ".dat"));
        assertEquals(Paths.get("/base","name","file.dat"), PathUtils.changeLongExtension("/base/name\\file.txt", ".dat"));
    }

    @Test
    public void testChangeExtension_NIX() {
        Assume.assumeFalse(HostSystem.WINDOWS.detected());

        assertEquals(Paths.get("base","name","file.dat"), PathUtils.changeLongExtension("base/name/file.txt", ".dat"));
        assertEquals(Paths.get("c:","base","name","file.dat"), PathUtils.changeLongExtension("c:/base/name/file.txt", ".dat"));
        assertEquals(Paths.get("/base","name","file.dat"), PathUtils.changeLongExtension("/base/name/file.txt", ".dat"));

        assertEquals(Paths.get("base\\fame\\file.dat"), PathUtils.changeLongExtension("base\\fame\\file.txt", ".dat"));
        assertEquals(Paths.get("base\\name\\file.dat"), PathUtils.changeLongExtension("base\\name\\file.txt", ".dat"));
        assertEquals(Paths.get("c:\\base\\name\\file.dat"), PathUtils.changeLongExtension("c:\\base\\name\\file.txt", ".dat"));
        assertEquals(Paths.get("/base","name\\file.dat"), PathUtils.changeLongExtension("/base/name\\file.txt", ".dat"));
    }

    @Test
    public void testChangeExtensionPath() {
        assertEquals(Paths.get("file.txt"), pce("file.txt", ".txt"));
        assertEquals(Paths.get("file.dat"), pce("file.txt", ".dat"));
        assertEquals(Paths.get("file.txt.dat"), pce("file.txt.info", ".txt.dat"));
        assertEquals(Paths.get("file.dat"), pce("file.txt.info", ".dat"));
        assertEquals(Paths.get("file.dat"), pce("file", ".dat"));

        assertEquals(Paths.get("base","name","file.txt"), pce("base/name/file.txt", ".txt"));
    }

    @Test
    public void testChangeExtensionPath_W32() {
        Assume.assumeTrue(HostSystem.WINDOWS.detected());

        assertEquals(Paths.get("base","name","file.dat"), pce("base\\name\\file.txt", ".dat"));
        assertEquals(Paths.get("c:","base","name","file.dat"), pce("c:\\base\\name\\file.txt", ".dat"));
        assertEquals(Paths.get("/base","name","file.dat"), pce("/base/name\\file.txt", ".dat"));
    }

    @Test
    public void testChangeExtensionPath_NIX() {
        Assume.assumeFalse(HostSystem.WINDOWS.detected());

        assertEquals(Paths.get("base","name","file.dat"), pce("base/name/file.txt", ".dat"));
        assertEquals(Paths.get("c:","base","name","file.dat"), pce("c:/base/name/file.txt", ".dat"));
        assertEquals(Paths.get("/base","name","file.dat"), pce("/base/name/file.txt", ".dat"));

        assertEquals(Paths.get("base\\fame\\file.dat"), pce("base\\fame\\file.txt", ".dat"));
        assertEquals(Paths.get("base\\name\\file.dat"), pce("base\\name\\file.txt", ".dat"));
        assertEquals(Paths.get("c:\\base\\name\\file.dat"), pce("c:\\base\\name\\file.txt", ".dat"));
        assertEquals(Paths.get("/base","name\\file.dat"), pce("/base/name\\file.txt", ".dat"));
    }

    @Test
    public void testChangeSuffix() {
        assertEquals("c:\\ms\\root\\data.exe", PathUtils.changeShortExtension("c:\\ms\\root\\data.if", ".exe").toString() );
    }

    @Test
    public void testFromURL() throws MalformedURLException {
        assertThrows(FileSystemNotFoundException.class, ()->{
            PathUtils.fromURL(new URL("http://ranides.net/index.htm"));
        });
        assertThrows(FileSystemNotFoundException.class, ()->{
            PathUtils.fromURL(new URL("ftp://ranides.net/index.htm"));
        });

        assertThrows(IllegalArgumentException.class, ()->{
            PathUtils.fromURL(new URL("file://c:/ms/index.htm?query"));
        });

        assertEquals(Paths.get("/ranides.net/index.htm"), PathUtils.fromURL(new URL("file:///ranides.net/index.htm")));
    }

    @Test
    public void testFromURL_W32() throws MalformedURLException {
        Assume.assumeTrue(HostSystem.WINDOWS.detected());

        assertEquals(Paths.get("c:/ms/index.htm"), PathUtils.fromURL(new URL("file:///c:/ms/index.htm")));
    }

    @Test
    public void testFromURL_NIX() throws MalformedURLException {
        Assume.assumeFalse(HostSystem.WINDOWS.detected());

        assertEquals(Paths.get("/c:/ms/index.htm"), PathUtils.fromURL(new URL("file:///c:/ms/index.htm")));
        assertEquals(Paths.get("/ranides.net/index.htm"), PathUtils.fromURL(new URL("file:///ranides.net/index.htm")));
    }

    @Test
    public void canonnical_W32() {
        Assume.assumeTrue(HostSystem.WINDOWS.detected());

        assertEquals(Paths.get("c:/ms/Home"), PathUtils.normalize(Paths.get("c:/ms/Home")));
        assertEquals(Paths.get("c:/ms/Home"), PathUtils.normalize(Paths.get("C:\\ms\\home")));

        assertTrue(PathUtils.normalize(Paths.get("C:\\ms\\home")).startsWith(Paths.get("c:/MS")));

        assertEquals(Paths.get("c:/dir/other/end"), PathUtils.normalize(Paths.get("c:/dir/something/../other/./end")));

        assertFalse(Paths.get("c:/dir/something/../other/./end").startsWith("c:/dir/other") );
        assertTrue(PathUtils.normalize(Paths.get("c:/dir/something/../other/./end")).startsWith("c:/dir/other"));

        assertTrue(PathUtils.normalize(Paths.get("other/./end")).isAbsolute());
    }

    @Test
    public void canonnical_NIX() {
        Assume.assumeFalse(HostSystem.WINDOWS.detected());

        assertEquals(Paths.get("/c/ms/Home"), PathUtils.normalize(Paths.get("/c/ms/Home")));

        assertEquals(Paths.get("/root/dir/other/end"), PathUtils.normalize(Paths.get("/root/dir/something/../other/./end")));

        assertFalse(Paths.get("/root/dir/something/../other/./end").startsWith("/root/dir/other") );
        assertTrue(PathUtils.normalize(Paths.get("/root/dir/something/../other/./end")).startsWith("/root/dir/other/end"));

        assertTrue(PathUtils.normalize(Paths.get("other/./end")).isAbsolute());
    }

    @Test
    public void canonnicalString_W32() {
        Assume.assumeTrue(HostSystem.WINDOWS.detected());

        assertEquals("c:\\ms\\Home", PathUtils.normalize("c:/ms/Home"));
        assertNotEquals("c:\\ms\\Home", PathUtils.normalize("C:\\ms\\home"));

        assertTrue(PathUtils.normalize("c:\\ms\\home").startsWith("c:\\ms"));
        assertFalse(PathUtils.normalize("C:\\ms\\home").startsWith("c:\\MS"));

        assertEquals("c:\\dir\\other\\end", PathUtils.normalize("c:/dir/something/../other/./end"));

        assertTrue(PathUtils.normalize("c:/dir/something/../other/./end").startsWith("c:\\dir\\other") );

        assertTrue(new File(PathUtils.normalize("other/./end")).isAbsolute());
    }

    @Test
    public void canonnicalString_NIX() {
        Assume.assumeFalse(HostSystem.WINDOWS.detected());

        assertEquals("/root/ms/Home", PathUtils.normalize("/root/ms/Home"));
        assertNotEquals("/root/ms/Home", PathUtils.normalize("/root/ms/home"));

        assertTrue(PathUtils.normalize("/root/ms/home").startsWith("/root/ms"));
        assertFalse(PathUtils.normalize("/root/ms/home").startsWith("/root/MS"));

        assertEquals("/root/dir/other/end", PathUtils.normalize("/root/dir/something/../other/./end"));

        assertTrue(PathUtils.normalize("/root/dir/something/../other/./end").startsWith("/root/dir/other") );

        assertTrue(new File(PathUtils.normalize("other/./end")).isAbsolute());
    }

    @Test
    public void testCommon_W32() {
        Assume.assumeTrue(HostSystem.WINDOWS.detected());

        Path home = PathUtils.common(Arrays.asList(Paths.get("foo/here/dir/file1"), Paths.get("foo/here/dir/file2"), Paths.get("foo/here/not/file3")));

        assertEquals("foo\\here", home.toString());
        assertEquals("dir\\file1", home.relativize(Paths.get("foo/here/dir/file1")).toString());

        Path a = PathUtils.common(Arrays.asList(Paths.get("d:\\where\\directory\\first"), Paths.get("q:\\where\\directory\\second"), Paths.get("d:\\where\\directory\\third")));
        Path b = PathUtils.common(Arrays.asList(Paths.get("d:\\where\\directory\\first"), Paths.get("d:\\where\\directory\\second"), Paths.get("d:\\where\\directory\\third")));
        Path c = PathUtils.common(Arrays.asList(Paths.get("first"), Paths.get("second"), Paths.get("third")));

        assertEquals("", a.toString());
        assertEquals("d:\\where\\directory", b.toString());
        assertEquals("", c.toString());
    }

    private static Path pce(String path, String ext) {
        return PathUtils.changeLongExtension(Paths.get(path), ext);
    }

    @Test
    public void walk() {
        CQuery<Path> found = PathUtils.walk(Paths.get(".\\src")).fetch();

        assertTrue(found.filter(Files::isDirectory).size() > 20);
        assertTrue(found.discard(Files::isDirectory).size() > 200);
        assertTrue(found.discard(Files::isDirectory).discard(p -> p.toString().endsWith(".java")).size() > 30);
    }
}
