package org.xbib.files.ftp.test.parser;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
import org.xbib.files.ftp.FTPFile;
import org.xbib.files.ftp.FTPListParseEngine;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Locale;
import org.xbib.files.ftp.parser.ConfigurableFTPFileEntryParserImpl;
import org.xbib.files.ftp.parser.VMSFTPEntryParser;
import org.xbib.files.ftp.parser.VMSVersioningFTPEntryParser;

public class VMSFTPEntryParserTest {

    private static final String[] badsamples =
            {

                    "1-JUN.LIS;2              9/9           JUN-2-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,)",
                    "1-JUN.LIS;2              a/9           2-JUN-98 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,)",
                    "DATA.DIR; 1              1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (,RWED,RWED,RE)",
                    "120196.TXT;1           118/126        14-APR-1997 12:45:27 PM  [GROUP,OWNER]    (RWED,,RWED,RE)",
                    "30CHARBAR.TXT;1         11/18          2-JUN-1998 08:38:42  [GROUP-1,OWNER]    (RWED,RWED,RWED,RE)",
                    "A.;2                    18/18          1-JUL-1998 08:43:20  [GROUP,OWNER]    (RWED2,RWED,RWED,RE)",
                    "AA.;2                  152/153        13-FED-1997 08:13:43  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
                    "Directory USER1:[TEMP]\r\n\r\n",
                    "\r\nTotal 14 files"
            };

    private static final String[] goodsamples =
            {
                    "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
                    "1-JUN.LIS;3              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,)",
                    "1-JUN.LIS;2              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,)",
                    "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [TRANSLATED]     (,RWED,RWED,RE)",
                    "120196.TXT;1           118/126        14-APR-1997 12:45:27  [GROUP,OWNER]    (RWED,,RWED,RE)",
                    "30CHARBAR.TXT;1         11/18          2-JUN-1998 08:38:42  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
                    "A.;2                    18/18          1-JUL-1998 08:43:20  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
                    "AA.;2                  152/153        13-FEB-1997 08:13:43  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
                    "UCX$REXECD_STARTUP.LOG;1098\r\n" +
                            "                         4/15         24-FEB-2003 13:17:24  [POSTWARE,LP]    (RWED,RWED,RE,)",
                    "UNARCHIVE.COM;1          2/15          7-JUL-1997 16:37:45  [POSTWARE,LP]    (RWE,RWE,RWE,RE)",
                    "UNXMERGE.COM;15          1/15         20-AUG-1996 13:59:50  [POSTWARE,LP]    (RWE,RWE,RWE,RE)",
                    "UNXTEMP.COM;7            1/15         15-AUG-1996 14:10:38  [POSTWARE,LP]    (RWE,RWE,RWE,RE)",
                    "UNZIP_AND_ATTACH_FILES.COM;12\r\n" +
                            "                        14/15         24-JUL-2002 14:35:40  [TRANSLATED]    (RWE,RWE,RWE,RE)",
                    "UNZIP_AND_ATTACH_FILES.SAV;1\r\n" +
                            "                        14/15         17-JAN-2002 11:13:53  [POSTWARE,LP]    (RWE,RWED,RWE,RE)",
                    "FREEWARE40.DIR;1        27/36" +
                            "         16-FEB-1999 10:01:46  [AP_HTTPD,APACHE$WWW                               (RWE,RWE,RE,RE)",
                    "1-JUN.LIS;1              9/9           2-jun-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
            };

    private static final String fullListing = "Directory USER1:[TEMP]\r\n\r\n" +
            "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)\r\n" +
            "2-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,)\r\n" +
            "3-JUN.LIS;1              9/9           3-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,)\r\n" +
            "3-JUN.LIS;4              9/9           7-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,)\r\n" +
            "3-JUN.LIS;2              9/9           4-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,)\r\n" +
            "3-JUN.LIS;3              9/9           6-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,)\r\n" +
            "\r\nTotal 6 files";

    @Test
    public void testFirstGoodListing() {
        VMSFTPEntryParser parser = new VMSFTPEntryParser();
        for (String test : goodsamples) {
            FTPFile f = parser.parseFTPEntry(test);
            assertNotNull(f, "Failed to parse " + test);
        }
    }

    @Test
    public void testBadListing() {
        VMSFTPEntryParser parser = new VMSFTPEntryParser();
        for (String test : badsamples) {
            try {
                FTPFile f = parser.parseFTPEntry(test);
                assertNull(nullFileOrNullDate(f), "Should have Failed to parse <" + test + ">");
            } catch (DateTimeParseException e) {
                //
            }
        }
    }

    private FTPFile nullFileOrNullDate(FTPFile f) {
        if (f == null) {
            return null;
        }
        if (f.getTimestamp() == null) {
            return null;
        }
        return f;
    }

    @Test
    public void testWholeListParse() throws IOException {
        VMSFTPEntryParser parser = new VMSFTPEntryParser();
        parser.configure(null);
        FTPListParseEngine engine = new FTPListParseEngine(parser);
        engine.readServerList(
                new ByteArrayInputStream(fullListing.getBytes()), null); // use default encoding
        FTPFile[] files = engine.getFiles();
        assertEquals(6, files.length);
        assertFileInListing(files, "2-JUN.LIS");
        assertFileInListing(files, "3-JUN.LIS");
        assertFileInListing(files, "1-JUN.LIS");
        assertFileNotInListing(files, "1-JUN.LIS;1");
    }

    @Test
    public void testWholeListParseWithVersioning() throws IOException {
        VMSFTPEntryParser parser = new VMSVersioningFTPEntryParser();
        parser.configure(null);
        FTPListParseEngine engine = new FTPListParseEngine(parser);
        engine.readServerList(
                new ByteArrayInputStream(fullListing.getBytes()), null); // use default encoding
        FTPFile[] files = engine.getFiles();
        assertEquals(3, files.length);
        assertFileInListing(files, "1-JUN.LIS;1");
        assertFileInListing(files, "2-JUN.LIS;1");
        assertFileInListing(files, "3-JUN.LIS;4");
        assertFileNotInListing(files, "3-JUN.LIS;1");
        assertFileNotInListing(files, "3-JUN.LIS");
    }

    public void assertFileInListing(FTPFile[] listing, String name) {
        for (FTPFile element : listing) {
            if (name.equals(element.getName())) {
                return;
            }
        }
        fail("File " + name + " not found in supplied listing");
    }

    public void assertFileNotInListing(FTPFile[] listing, String name) {
        for (FTPFile element : listing) {
            if (name.equals(element.getName())) {
                fail("Unexpected File " + name + " found in supplied listing");
            }
        }
    }

    @Test
    public void testParseFieldsOnDirectory() throws Exception {
        ConfigurableFTPFileEntryParserImpl parser = new VMSFTPEntryParser();
        parser.configure(null);
        FTPFile dir = parser.parseFTPEntry(
                "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)");
        assertTrue(dir.isDirectory(), "Should be a directory.");
        assertEquals("DATA.DIR", dir.getName());
        assertEquals(512, dir.getSize());
        assertNotNull(dir.getTimestamp());
        assertEquals("Tue Jun 02 07:32:04 1998", dir.getTimestamp().format(df));
        assertEquals("GROUP", dir.getGroup());
        assertEquals("OWNER", dir.getUser());
        checkPermisions(dir, 0775);
        dir = parser.parseFTPEntry("DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [TRANSLATED]    (RWED,RWED,,RE)");
        assertTrue(dir.isDirectory(), "Should be a directory.");
        assertEquals("DATA.DIR", dir.getName());
        assertEquals(512, dir.getSize());
        assertEquals("Tue Jun 02 07:32:04 1998", dir.getTimestamp().format(df));
        assertNull(dir.getGroup());
        assertEquals("TRANSLATED", dir.getUser());
        checkPermisions(dir, 0705);
    }

    static final DateTimeFormatter df = DateTimeFormatter
            .ofPattern("EEE MMM dd HH:mm:ss yyyy")
            .withZone(ZoneId.of("UTC"))
            .withLocale(Locale.US);

    @Test
    public void testParseFieldsOnFile() throws Exception {
        ConfigurableFTPFileEntryParserImpl parser = new VMSFTPEntryParser();
        parser.configure(null);
        FTPFile file = parser.parseFTPEntry("1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RW,R)");
        assertTrue(file.isFile(), "Should be a file.");
        assertEquals("1-JUN.LIS", file.getName());
        assertEquals(9 * 512, file.getSize());
        assertNotNull(file.getTimestamp());
        assertEquals("Tue Jun 02 07:32:04 1998", file.getTimestamp().format(df));
        assertEquals("GROUP", file.getGroup());
        assertEquals("OWNER", file.getUser());
        checkPermisions(file, 0764);
        file = parser.parseFTPEntry("1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [TRANSLATED]    (RWED,RD,,)");
        assertTrue(file.isFile(), "Should be a file.");
        assertEquals("1-JUN.LIS", file.getName());
        assertEquals(9 * 512, file.getSize());
        assertEquals("Tue Jun 02 07:32:04 1998", file.getTimestamp().format(df));
        assertNull(file.getGroup());
        assertEquals("TRANSLATED", file.getUser());
        checkPermisions(file, 0400);
    }

    private void checkPermisions(FTPFile dir, int octalPerm) {
        int permMask = 1 << 8;
        assertEquals(dir.hasPermission(FTPFile.USER_ACCESS,
                FTPFile.READ_PERMISSION), ((permMask & octalPerm) != 0), "Owner should not have read permission.");
        permMask >>= 1;
        assertEquals(dir.hasPermission(FTPFile.USER_ACCESS,
                FTPFile.WRITE_PERMISSION), ((permMask & octalPerm) != 0), "Owner should not have write permission.");
        permMask >>= 1;
        assertEquals(dir.hasPermission(FTPFile.USER_ACCESS,
                FTPFile.EXECUTE_PERMISSION), ((permMask & octalPerm) != 0), "Owner should not have execute permission.");
        permMask >>= 1;
        assertEquals(dir.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION), ((permMask & octalPerm) != 0), "Group should not have read permission.");
        permMask >>= 1;
        assertEquals(dir.hasPermission(FTPFile.GROUP_ACCESS,
                FTPFile.WRITE_PERMISSION), ((permMask & octalPerm) != 0), "Group should not have write permission.");
        permMask >>= 1;
        assertEquals(dir.hasPermission(FTPFile.GROUP_ACCESS,
                FTPFile.EXECUTE_PERMISSION), ((permMask & octalPerm) != 0), "Group should not have execute permission.");
        permMask >>= 1;
        assertEquals(dir.hasPermission(FTPFile.WORLD_ACCESS,
                FTPFile.READ_PERMISSION), ((permMask & octalPerm) != 0), "World should not have read permission.");
        permMask >>= 1;
        assertEquals(dir.hasPermission(FTPFile.WORLD_ACCESS,
                FTPFile.WRITE_PERMISSION), ((permMask & octalPerm) != 0), "World should not have write permission.");
        permMask >>= 1;
        assertEquals(dir.hasPermission(FTPFile.WORLD_ACCESS,
                FTPFile.EXECUTE_PERMISSION), ((permMask & octalPerm) != 0), "World should not have execute permission.");
    }
}
