package com.knecon.fforesight.service.viewerdoc.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.File;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.junit.jupiter.api.Test;

import com.knecon.fforesight.service.viewerdoc.ContentStreams;
import com.knecon.fforesight.service.viewerdoc.pdf.ClassifiedContentStream;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ContentStreamClassifierTest {

    @Test
    @SneakyThrows
    public void testClassification() {

        File pdfFile = new File(Thread.currentThread().getContextClassLoader().getResource("viewerDocLayers.pdf").getFile());

        try (PDDocument document = Loader.loadPDF(pdfFile)) {

            PDPage page = document.getPage(0);

            List<ClassifiedContentStream> classifieds = ContentStreamClassifier.getClassifiedContentStreams(page);

            logContentStreamClassifications(classifieds);

            assertEquals(11, classifieds.size());
            assertEquals(ContentStreams.ESCAPE_START, classifieds.get(0).classification());
            for (int i = 1; i < 9; i++) {
                assertEquals(ContentStreams.OTHER, classifieds.get(i).classification());
            }
            assertEquals(ContentStreams.ESCAPE_END, classifieds.get(9).classification());
            assertEquals(ContentStreams.KNECON_LAYOUT, classifieds.get(10).classification());
            assertTrue(ContentStreamClassifier.areAllContentStreamsEscaped(classifieds));
        }
    }


    @Test
    @SneakyThrows
    public void testRemoveLayoutLayer() {

        File pdfFile = new File(Thread.currentThread().getContextClassLoader().getResource("viewerDocLayers.pdf").getFile());
        File tmpFile = Files.createTempFile("removedLayout", ".pdf").toFile();

        try (PDDocument document = Loader.loadPDF(pdfFile)) {

            PDPage page = document.getPage(0);

            List<ClassifiedContentStream> classifieds = ContentStreamClassifier.getClassifiedContentStreams(page);
            page.setContents(ContentStreamUtility.removeLayerFromContentStreams(Set.of(ContentStreams.KNECON_LAYOUT), classifieds));

            document.save(tmpFile);
        }
        try (PDDocument document2 = Loader.loadPDF(tmpFile)) {

            PDPage page2 = document2.getPage(0);

            List<ClassifiedContentStream> classifieds2 = ContentStreamClassifier.getClassifiedContentStreams(page2);

            logContentStreamClassifications(classifieds2);

            assertEquals(10, classifieds2.size());
            assertEquals(ContentStreams.ESCAPE_START, classifieds2.get(0).classification());
            for (int i = 1; i < 9; i++) {
                assertEquals(ContentStreams.OTHER, classifieds2.get(i).classification());
            }
            assertEquals(ContentStreams.ESCAPE_END, classifieds2.get(9).classification());
            assertTrue(ContentStreamClassifier.areAllContentStreamsEscaped(classifieds2));
        }
        assert tmpFile.delete();
    }


    @Test
    @SneakyThrows
    public void testClassificationForOldLayers() {

        File pdfFile = new File(Thread.currentThread().getContextClassLoader().getResource("oldViewerDocLayers.pdf").getFile());
        try (PDDocument document = Loader.loadPDF(pdfFile)) {

            PDPage page = document.getPage(0);

            List<ClassifiedContentStream> classifieds = ContentStreamClassifier.getClassifiedContentStreams(page);

            logContentStreamClassifications(classifieds);

            assertEquals(11, classifieds.size());
            assertEquals(ContentStreams.ESCAPE_START, classifieds.get(0).classification());
            for (int i = 1; i < 9; i++) {
                assertEquals(ContentStreams.OTHER, classifieds.get(i).classification());
            }
            assertEquals(ContentStreams.ESCAPE_END, classifieds.get(9).classification());
            assertEquals(ContentStreams.KNECON_LAYOUT, classifieds.get(10).classification());
            assertTrue(ContentStreamClassifier.areAllContentStreamsEscaped(classifieds));
        }
    }


    private static void logContentStreamClassifications(List<ClassifiedContentStream> classifieds) {

        log.info("number of content streams: {}", classifieds.size());
        log.info("Classifications: {}", classifieds.stream()//
                .map(ClassifiedContentStream::classification)//
                .map(ContentStreams.Identifier::cosName)//
                .map(COSName::getName)//
                .collect(Collectors.joining(", ")));
    }

}
