<script lang="ts">
	import { onMount } from 'svelte';
	import { UploadOutline } from 'flowbite-svelte-icons';
	import * as monaco from 'monaco-editor';
	import type ContentModel from '../models/ContentModel.svelte';
	import { opLookup } from '../models/OperatorList';
	import { OperatorList } from '../models/OperatorList.js';
	import type { PDFOperator } from '../models/OperatorList';

	monaco.languages.register({ id: 'pdf-content-stream' });

	// Define a custom theme for PDF content stream highlighting
	monaco.editor.defineTheme('forge-dark', {
		base: 'vs-dark',
		inherit: true,
		rules: [
			{ token: 'comment', foreground: '#6A9955' },           // Green for comments
			{ token: 'operator', foreground: '#bd4fdb' },          // Purple for graphics state operators
			{ token: 'color-operator', foreground: '#61AFEF' },     // Blue for color operators
			{ token: 'text-positioning', foreground: '#2c9f9f' },   // Dark cyan for text positioning
			{ token: 'text-render', foreground: '#4cdcdc' },        // Light cyan for text rendering
			{ token: 'text-state', foreground: '#2c9f9f' },         // Dark cyan for text state
			{ token: 'path-construct', foreground: '#ffa500' },     // Orange for path construction
			{ token: 'path-paint', foreground: '#ff6b6b' },         // Reddish for path painting
			{ token: 'named-element', foreground: '#e1e1e1' },      // Light gray for named elements
			{ token: 'hex-string', foreground: '#E06C75' },         // Red for hex strings
			{ token: 'number', foreground: '#e1e1e1' },             // Light gray for numbers
			{ token: 'matrix', foreground: '#ffd700' },             // Gold for matrices
			{ token: 'marked-content', foreground: '#98c379' }     // Green for marked content
		],
		colors: {
			'editor.background': '#1E1F22FF'
		}
	});

	monaco.languages.setMonarchTokensProvider('pdf-content-stream', {
		tokenizer: {
			root: [
				// Comments
				[/%.*$/, 'comment'],
				
				// Numbers
				[/[+-]?\d*\.?\d+([eE][-+]?\d+)?/, 'number'],
				
				// Hex strings
				[/<[0-9A-Fa-f]+>/, 'hex-string'],
				
				// Operators
				[/[A-Za-z'"][A-Za-z'*]*/, {
					cases: Object.fromEntries(
						Object.values(opLookup)
							.filter(op => op.keyword !== null)
							.map(op => [
								// Escape special regex characters in the keyword
								op.keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
								op.class
							])
					)
				}]
			]
		}
	});

	let {
		height,
		save,
		contents
	}: {
		height: number;
		save: (updated_data: string) => void;
		contents: ContentModel;
	} = $props();

	let editorContainer: HTMLElement;
	let editor: monaco.editor.IStandaloneCodeEditor;

	let isEdited = $state(false);
	let lastContents: ContentModel | undefined = $state(undefined);

	onMount(() => {
		editor = monaco.editor.create(editorContainer, {
			value: '',
			language: 'pdf-content-stream',
			theme: 'forge-dark',
			minimap: { enabled: false },
			scrollBeyondLastLine: false,
			fontSize: 14,
			automaticLayout: true
		});

		// Add change listener to detect edits
		editor.onDidChangeModelContent(() => {
			isEdited = true;
		});

		loadContents(contents);

		return () => {
			editor.dispose();
		};
	});

	$effect(() => {
		loadContents(contents);
	});

	function loadContents(contents: ContentModel | undefined) {
		if (!contents || !editor) return;

		if (lastContents === contents) {
			return;
		}

		lastContents = contents;
		editor.setValue(contents.toDisplay());
		isEdited = false;
		// Add decorations for operator ranges if available
		if (contents.opList && contents.opList.fnArray.length > 0) {
			const decorations = [];
			for (let i = 0; i < contents.opList.fnArray.length; i++) {
				const fnId = contents.opList.fnArray[i];
				const range = contents.opList.rangeArray[i];
				const operator = opLookup[fnId];

				if (operator && range) {
					const startPos = editor.getModel()!.getPositionAt(range[0]);
					const endPos = editor.getModel()!.getPositionAt(range[1]);
					const monacoRange = new monaco.Range(
						startPos.lineNumber,
						startPos.column,
						endPos.lineNumber,
						endPos.column);
					if (fnId == 91) {
						console.log(range, monacoRange)
					}
					decorations.push({
						range: monacoRange,
						options: {
							className: operator.class,
							hoverMessage: {value: OperatorList.formatOperatorToMarkdown(fnId)},
						}
					});
				}
			}
			editor.createDecorationsCollection(decorations);
		}
	}
</script>

<div class="relative">
	<div bind:this={editorContainer} style:height={height + "px"}></div>
	{#if isEdited}
		<button onclick={() => save(editor.getValue())} class="save-button">
			<UploadOutline size="xl" />
		</button>
	{/if}
</div>

<style lang="postcss">
    .save-button {
        @apply p-2 bg-forge-sec text-forge-text border-t border-forge-bound cursor-pointer;
        position: absolute;
        right: 2em;
        bottom: 0px;
        width: 48px;
        height: 48px;
    }

    .save-button:hover {
        background-color: #3c3d41;
    }

</style>
