// SPDX-FileCopyrightText: 2022 James R. Barlow
// SPDX-License-Identifier: MPL-2.0

#pragma once

#include <iostream>

#include "pikepdf.h"

#include <nanobind/trampoline.h>
#include <qpdf/QPDFTokenizer.hh>

// Used to implement pikepdf.StreamParser, which can be subclassed to implement
// custom parsing.
class PyParserCallbacks : public QPDFObjectHandle::ParserCallbacks {
public:
    NB_TRAMPOLINE(QPDFObjectHandle::ParserCallbacks, 2);
    virtual ~PyParserCallbacks() = default;

    void handleObject(QPDFObjectHandle obj, size_t offset, size_t length) override;
    void handleEOF() override;
};

class ContentStreamInstruction {
public:
    ContentStreamInstruction(ObjectList operands, QPDFObjectHandle operator_);
    virtual ~ContentStreamInstruction() = default;

    friend std::ostream &operator<<(std::ostream &os, ContentStreamInstruction &csi);

    ObjectList operands;
    QPDFObjectHandle operator_;
};

class ContentStreamInlineImage {
public:
    ContentStreamInlineImage(ObjectList image_metadata, QPDFObjectHandle image_data)
        : image_metadata(image_metadata), image_data(image_data)
    {
    }
    virtual ~ContentStreamInlineImage() = default;

    friend std::ostream &operator<<(std::ostream &os, ContentStreamInstruction &csi);

    ObjectList image_metadata;
    QPDFObjectHandle image_data;

    py::list get_operands() const;
    QPDFObjectHandle get_operator() const;

    py::object get_inline_image() const;
};

// Used for parse_content_stream. Handles each object by grouping into operands
// and operators. The whole parse stream can be retrieved at once.
class OperandGrouper : public QPDFObjectHandle::ParserCallbacks {
public:
    OperandGrouper(const std::string &operators);
    void handleObject(QPDFObjectHandle obj) override;
    void handleEOF() override;

    py::list getInstructions() const;
    std::string getWarning() const;

private:
    std::set<std::string> whitelist;
    std::vector<QPDFObjectHandle> tokens;
    bool parsing_inline_image;
    std::vector<QPDFObjectHandle> inline_metadata;
    py::list instructions;
    uint count;
    std::string warning;
};

// unparse the list of instructions generated by an OperandGrouper
py::bytes unparse_content_stream(py::iterable contentstream);
