import TreeViewRequest from './TreeViewRequest.svelte';
import { TreeViewModel } from './TreeViewModel';
import type { Trace } from './Primitive.svelte';
import type DocumentWorker from './Document.svelte';

const ROW_HEIGHT = 24;
const MAX_STICKIES: number = 5;
export default class TreeViewState {
	private doc: DocumentWorker;
	private initialRequest: TreeViewRequest[];
	private activeRequest: Map<string, TreeViewRequest> = new Map();
	private activeEntries: Map<string, TreeViewModel[]> = new Map();

	public total_height: number = $state(100);
	public filler_height: number = $state(0);
	public entries: TreeViewModel[] = $state([]);
	public stickies: TreeViewModel[] = $state([]);

	private scrollY = 0;
	private height = 100;

	constructor(doc: DocumentWorker) {
		this.initialRequest = [TreeViewRequest.TRAILER];
		this.initialRequest = this.initialRequest.concat(
			TreeViewRequest.fromPageCount(+doc.getNumberOfPages())
		);
		this.doc = doc;
		this.updateTreeViewRequest([this.initialRequest[0]]).then(() => this.updateTreeView(0, 1080));
	}

	public getEntryCount() {
		let count = 0;
		this.activeEntries.forEach((value) => {
			count += value.length - 1;
		});
		return this.initialRequest.length + count;
	}

	public getEntries(start: number, end: number): [TreeViewModel[], TreeViewModel[]] {
		let stickies: TreeViewModel[] = [];
		let totalIndex = 0;
		const result: TreeViewModel[] = [];
		for (const request of this.initialRequest) {
			const entries = this.activeEntries.get(request.key);
			if (entries) {
				for (let i = 0; i < entries.length; i++) {
					if (totalIndex > 0 && totalIndex == start) {
						stickies = this.findParents(entries, i);
					}
					if (totalIndex >= start) {
						const entry = entries[i];
						result.push(entry);
					}
					totalIndex += 1;
					if (totalIndex >= end) {
						return [result, stickies];
					}
				}
			} else {
				if (totalIndex >= start) {
					result.push(
						new TreeViewModel(
							0,
							request.key,
							'Dictionary',
							'-',
							'-',
							true,
							false,
							[{ key: request.key, last_jump: request.key } as Trace],
						)
					);
				}
				totalIndex += 1;
				if (totalIndex >= end) {
					return [result, stickies];
				}
			}
		}
		return [result, stickies];
	}

	private findParents(entries: TreeViewModel[], start: number): TreeViewModel[] {
		const parents = [];
		let lastDepth = entries[start].depth;
		for (let i = start; i >= 0; i--) {
			const entry = entries[i];
			if (entry.depth < lastDepth) {
				parents.push(entry);
				if (entry.depth == 0) {
					return parents.reverse().slice(0, MAX_STICKIES);
				}
				lastDepth = entry.depth;
			}
		}
		return parents.reverse().slice(0, MAX_STICKIES);
	}

	public async reload() {
		await this.loadTreeView(this.scrollY, this.height);
	}

	public async loadTreeView(scrollY: number, height: number) {
		const activeRequests = Array.from(this.activeRequest.values());
		await this.updateTreeViewRequest(activeRequests);
		await this.updateTreeView(scrollY, height);
	}

	public async updateTreeView(scrollY: number, height: number) {

		const firstEntry = Math.floor(scrollY / ROW_HEIGHT);
		const lastEntry = Math.ceil((scrollY + height) / ROW_HEIGHT);
		this.total_height = this.getEntryCount() * ROW_HEIGHT;
		this.filler_height = firstEntry * ROW_HEIGHT;
		const entriesAndStickies = this.getEntries(firstEntry, lastEntry);
		this.entries = entriesAndStickies[0];
		this.stickies = entriesAndStickies[1];
		this.scrollY = scrollY;
		this.height = height;
	}

	public getRoot(): TreeViewRequest[] {
		const _roots: TreeViewRequest[] = [];
		this.initialRequest.forEach((root) => {
			_roots.push(root.clone());
		});
		return _roots;
	}

	public async updateTreeViewRequest(treeViewRequests: TreeViewRequest[]) {
		const result = await this.doc.getPrimTreeByPath(treeViewRequests);
		for (let i = 0; i < treeViewRequests.length; i++) {
			const request = treeViewRequests[i];
			if (request.expand) {
				this.activeRequest.set(request.key, request);
			} else {
				this.activeRequest.delete(request.key);
			}
			const prim = result.filter((r) => r.trace[0].key == request.key);
			if (prim) {
				this.activeEntries.set(request.key, prim);
			}
		}
	}

	public async expandTree(path: string[]) {
		if (path.length == 0) {
			console.error('Empty path');
			return;
		}
		let root = this.activeRequest.get(path[0]);
		if (!root) {
			root = this.getRoot().find((root) => root.key === path[0]);
			if (!root) {
				console.error('Root not found for path: ' + path);
				return;
			}
		}
		root.setExpand(true);
		let node = root;
		for (const key of path.slice(1, path.length)) {
			const _node: TreeViewRequest | undefined = node.getChild(key);
			if (_node) {
				_node.setExpand(true);
				node = _node;
			} else {
				node = node.addChild(key);
			}
		}
		await this.updateTreeViewRequest([root]);
	}

	public async collapseTree(path: string[]) {
		if (path.length == 0) {
			console.error('Empty path');
			return;
		}
		if (path.length == 1) {
			this.activeEntries.delete(path[0]);
			return;
		}
		const root: TreeViewRequest | undefined = this.activeRequest.get(path[0]);
		if (!root) {
			console.error('Root not found for path: ' + path);
			return;
		}
		let node: TreeViewRequest | undefined = root;
		let parent: TreeViewRequest | undefined;
		for (const key of path.slice(1, path.length)) {
			parent = node;
			node = parent?.getChild(key);
		}
		if (node && parent) {
			parent.children = parent.children.filter(child => child.key !== node?.key);
			await this.updateTreeViewRequest([root]);
		}
	}
}
