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

import org.junit.jupiter.api.Test;
import org.xbib.files.ftp.FTP;
import org.xbib.files.ftp.FTPClientConfig;
import org.xbib.files.ftp.FTPFile;
import org.xbib.files.ftp.FTPFileFilters;
import org.xbib.files.ftp.FTPListParseEngine;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Comparator;
import org.xbib.files.ftp.parser.MLSxEntryParser;
import org.xbib.files.ftp.parser.UnixFTPEntryParser;

/**
 * Attempt comparison of LIST and MLSD listings
 */
public class MLSDComparison {

    static final String DOWNLOAD_DIR = "build/ftptest";

    private final Comparator<FTPFile> cmp = (o1, o2) -> {
        String n1 = o1.getName();
        String n2 = o2.getName();
        return n1.compareTo(n2);
    };

    @Test
    public void testFile() throws Exception {
        File path = new File(DOWNLOAD_DIR);
        FilenameFilter filter = (dir, name) -> name.endsWith("_mlsd.txt");
        File[] files = path.listFiles(filter);
        if (files == null) {
            return;
        }
        for (File mlsd : files) {
            InputStream is = new FileInputStream(mlsd);
            FTPListParseEngine engine = new FTPListParseEngine(MLSxEntryParser.getInstance());
            engine.readServerList(is, FTP.DEFAULT_CONTROL_ENCODING);
            FTPFile[] mlsds = engine.getFiles(FTPFileFilters.ALL);
            is.close();
            File list = new File(mlsd.getParentFile(), mlsd.getName().replace("_mlsd", "_list"));
            is = new FileInputStream(list);
            FTPClientConfig cfg = new FTPClientConfig();
            cfg.setServerTimeZoneId("GMT");
            UnixFTPEntryParser parser = new UnixFTPEntryParser(cfg);
            engine = new FTPListParseEngine(parser);
            engine.readServerList(is, FTP.DEFAULT_CONTROL_ENCODING);
            FTPFile[] lists = engine.getFiles(FTPFileFilters.ALL);
            is.close();
            compareSortedLists(mlsds, lists);
        }
    }

    private void compareSortedLists(FTPFile[] lst, FTPFile[] mlst) {
        Arrays.sort(lst, cmp);
        Arrays.sort(mlst, cmp);
        FTPFile first, second;
        int firstl = lst.length;
        int secondl = mlst.length;
        int one = 0, two = 0;
        first = lst[one++];
        second = mlst[two++];
        int cmp;
        while (one < firstl || two < secondl) {
            String rl1 = first.getRawListing();
            String rl2 = second.getRawListing();
            cmp = first.getName().compareTo(second.getName());
            if (cmp == 0) {
                if (first.getName().endsWith("HEADER.html")) {
                    cmp = 0;
                }
                if (!areEquivalent(first, second)) {
                    long tdiff = first.getTimestamp().toEpochSecond() - second.getTimestamp().toEpochSecond();
                }
                if (one < firstl) {
                    first = lst[one++];
                }
                if (two < secondl) {
                    second = mlst[two++];
                }
            } else if (cmp < 0) {
                if (!first.getName().startsWith(".")) { // skip hidden files
                    System.out.println("1: " + rl1);
                }
                if (one < firstl) {
                    first = lst[one++];
                }
            } else {
                System.out.println("2: " + rl2);
                if (two < secondl) {
                    second = mlst[two++];
                }
            }
        }
    }

    /**
     * Compare two instances to see if they are the same,
     * ignoring any uninitialised fields.
     *
     * @param a first instance
     * @param b second instance
     * @return true if the initialised fields are the same
     */
    public boolean areEquivalent(FTPFile a, FTPFile b) {
        return a.getName().equals(b.getName()) &&
                        areSame(a.getSize(), b.getSize(), -1L) &&
                        areSame(a.getTimestamp(), b.getTimestamp());
    }

    private boolean areSame(ZonedDateTime a, ZonedDateTime b) {
        return a.equals(b);
    }

    private boolean areSame(long a, long b, long d) {
        return a == d || b == d || a == b;
    }
}
