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

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.apache.pdfbox.contentstream.PDContentStream;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDStream;

import com.knecon.fforesight.service.viewerdoc.ContentStreams;
import com.knecon.fforesight.service.viewerdoc.model.OperatorWithArguments;
import com.knecon.fforesight.service.viewerdoc.pdf.ClassifiedContentStream;
import com.knecon.fforesight.service.viewerdoc.pdf.SinglePDContentStream;

import lombok.experimental.UtilityClass;

@UtilityClass
public class ContentStreamUtility {

    public static List<OperatorWithArguments> parseLeadingOperators(PDContentStream contentStream,
                                                                    int numberOfOperatorsToRead) throws IOException {

        List<COSBase> arguments = new ArrayList<>();
        PDFStreamParser parser = new PDFStreamParser(contentStream);
        List<OperatorWithArguments> operatorsWithArguments = new LinkedList<>();
        for (int i = 0; i < numberOfOperatorsToRead; ) {
            Object token = parser.parseNextToken();
            if (token == null) {
                break;
            }
            if (token instanceof Operator operator) {
                operatorsWithArguments.add(new OperatorWithArguments(operator, arguments));
                arguments = new ArrayList<>();
                i++;
            } else {
                arguments.add((COSBase) token);
            }

        }
        return operatorsWithArguments;
    }


    public static Optional<COSName> findLayoutGridOCGName(PDPage page) {

        Optional<COSName> layoutGridOCGName = Optional.empty();
        var resourceIterator = page.getResources().getPropertiesNames();
        for (COSName cosName : resourceIterator) {
            COSBase cosBase = page.getResources().getProperties(cosName).getCOSObject().getDictionaryObject(COSName.NAME);
            if (cosBase instanceof COSString string) {
                if (ContentStreams.KNECON_LAYOUT.name().equals(string.getString())) {
                    layoutGridOCGName = Optional.of(cosName);
                }
            }
        }
        return layoutGridOCGName;
    }


    public static List<PDStream> removeLayerFromContentStreams(Set<ContentStreams.Identifier> layers, List<ClassifiedContentStream> classifiers) {

        return classifiers.stream()
                .filter(classifiedContentStream -> !layers.contains(classifiedContentStream.classification()))
                .map(ClassifiedContentStream::contentStream)
                .map(SinglePDContentStream::getPdStream)
                .toList();
    }

}
