import {getDocument, type PDFDocumentProxy, GlobalWorkerOptions} from "pdfjs-dist";
import type { PrimitiveModel } from './PrimitiveModel';
import type TreeViewRequest from './TreeViewRequest.svelte';
import type { TreeViewModel } from './TreeViewModel';
import type XRefTable from './XRefTable';
import ContentModel from './ContentModel.svelte';
import { OperatorList } from './OperatorList';

export default class DocumentWorker {

    doc: PDFDocumentProxy;

    constructor(doc: PDFDocumentProxy) {
        this.doc = doc;
    }
    
    static async load(data: string | ArrayBuffer) {
        console.log("Initializing pdf worker")
        GlobalWorkerOptions.workerSrc = '/pdf.worker.min.mjs';
        const loadingTask = getDocument({ data });
        const doc = await loadingTask.promise;
        return new DocumentWorker(doc);
    }

    public async save() {
        return this.doc.saveDocument();
    }

    public async getTitle(): Promise<string | undefined> {
        let title = this.doc._pdfInfo.Title;
        console.log(this.doc._pdfInfo);
        if (title && title !== "") {
            return title;
        }
        const metadata = await this.doc.getMetadata()

        title = metadata?.info?.Title;
        return title === "" ? undefined : title;
    }

    public getNumberOfPages(): number {
        return this.doc.numPages;
    }

    public async getPrimByPath(path: string): Promise<PrimitiveModel> {
        return await this.doc.getPrimitiveByPath(path);
    }

    public async getPrimTreeByPath(request: TreeViewRequest[]): Promise<TreeViewModel[]> {
        
        return await this.doc.getPrimitiveTree(request);
    }

    public async getXrefEntries(): Promise<XRefTable> {
        
        return await this.doc.getXRefEntries();
    }

    public async getStreamAsString(path: string): Promise<string> {
        return await this.doc.getStreamAsString(path);
    }

    public async getContents(pageNumber: number): Promise<ContentModel> {
        try {
            const pathStem = `/Page${pageNumber}/Contents`;
            const contents = await this.doc.getPrimitiveByPath(pathStem);
            const promises = [];
            if (contents && contents.ptype === "Array") {
                for (const child of contents.children) {
                    promises.push(this.doc.getStreamAsString(`${pathStem}/${child.key}/Data`));
                }
            } else {
                promises.push(this.doc.getStreamAsString(`${pathStem}/Data`));
            }
            const parts = await Promise.all(promises);
            return new ContentModel(parts, await this.getOperatorList(pageNumber));
        } catch (error: unknown) {
            console.error(error);
            return new ContentModel([], new OperatorList([], [], []));
        }
    }

    public dispose() {
        this.doc.destroy();
    }

    public async getImageAsUrl(path: string): Promise<string> {
        
        const imageData = await this.doc.getImageDataByPath(path);

        const canvas = document.createElement('canvas');
        canvas.width = imageData.width;
        canvas.height = imageData.height;

        const ctx = canvas.getContext('2d');
        if (!ctx) {
            throw new Error('Could not get canvas context');
        }
        ctx.fillStyle = 'white';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.putImageData(new ImageData(imageData.data, imageData.width, imageData.height), 0, 0);

        return canvas.toDataURL('image/png');
    }

    public async getOperatorList(num: number) {
        const page = await this.doc.getPage(num);
        return await page.getOperatorList();
    }

    public async renderPage(num: number, canvas: HTMLCanvasElement) {
        const page = await this.doc.getPage(num);
        const scale = 1.5;
        const viewport = page.getViewport({ scale: scale, });
        canvas.height = viewport.height;
        canvas.width = viewport.width;
        canvas.style.width = Math.floor(canvas.width) + "px";
        canvas.style.height =  Math.floor(canvas.height) + "px";
        const context = canvas.getContext("2d");
        if (!context) return "";
        const transform =  [1, 0, 0, 1, 0, 0];
        // viewport = page.getViewport({ scale: 0.7, offsetX: canvas.width / 4, offsetY: canvas.height / 4});

        const renderContext = {
            canvasContext: context,
            transform: transform,
            viewport: viewport,
        };
        const renderTask = page.render(renderContext);

        context.fillStyle = '#ffffff';
        context.fillRect(viewport.offsetX, viewport.offsetY, viewport.width, viewport.height);
        await renderTask.promise
    }

}