import {invoke} from "@tauri-apps/api/core";
import TreeViewRequest from "./TreeViewRequest.svelte";
import {TreeViewModel} from "./TreeViewModel";
import type {Trace} from "./Primitive.svelte";
import type FileViewState from "./FileViewState.svelte";

const ROW_HEIGHT = 24;
const MAX_STICKIES: number = 5;
export default class TreeViewState {

    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;
    file_id: string;

    constructor(fState: FileViewState) {

        this.initialRequest = [TreeViewRequest.TRAILER];
        this.initialRequest = this.initialRequest.concat(TreeViewRequest.fromPageCount(+fState.file.page_count));
        this.file_id = fState.file.id;
        this.updateTreeViewRequest([this.initialRequest[0]]);

    }

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

    public getEntries(start: number, end: number): [TreeViewModel[], TreeViewModel[]] {
        let stickies: TreeViewModel[] = [];
        let totalIndex = 0;
        let result: TreeViewModel[] = [];
        for (let request of this.initialRequest) {
            let entries = this.activeEntries.get(request.key);
            if (entries) {
                for (let i = 0; i < entries.length; i++) {
                    let entry = entries[i];
                    if (totalIndex > 0 && totalIndex == start) {
                        stickies = this.findParents(entries, i);
                    }
                    if (totalIndex >= start) {
                        result.push(entry);
                    }
                    totalIndex += 1;
                    if (totalIndex >= end + 5) {
                        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],
                            true,
                        ))
                }
                totalIndex += 1;
                if (totalIndex >= end) {
                    return [result, stickies];
                }
            }
        }
        return [result, stickies];
    }

    private findParents(entries: TreeViewModel[], start: number): TreeViewModel[] {

        let parents = [];
        let lastDepth = entries[start].depth;
        for (let i = start; i >= 0; i--) {
            let 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 toView(request: TreeViewRequest): TreeViewModel {
        return new TreeViewModel(
            0,
            request.key,
            "Dictionary",
            "-",
            "-",
            true,
            false,
            [{key: request.key, last_jump: request.key} as Trace],
            false,
        );
    }

    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) {
        let firstEntry = Math.floor(scrollY / ROW_HEIGHT);
        let lastEntry = Math.ceil((scrollY + height) / ROW_HEIGHT);
        this.total_height = this.getEntryCount() * ROW_HEIGHT;
        this.filler_height = firstEntry * ROW_HEIGHT;
        let entriesAndStickies = this.getEntries(firstEntry, lastEntry);
        this.entries = entriesAndStickies[0];
        this.stickies = entriesAndStickies[1];
        this.scrollY = scrollY;
        this.height = height;
    }

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

    public async updateTreeViewRequest(treeViewRequests: TreeViewRequest[]) {
        let result = await invoke<TreeViewModel[]>("get_prim_tree_by_path", {
            id: this.file_id,
            paths: treeViewRequests,
        });
        for (let i = 0; i < treeViewRequests.length; i++) {
            let request = treeViewRequests[i];
            if (request.expand) {
                this.activeRequest.set(request.key, request);
            } else {
                this.activeRequest.delete(request.key);
            }
            let prim = result.filter(r => r.path[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 (let key of path.slice(1, path.length)) {
            let _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;
        }
        let root: TreeViewRequest | undefined = this.activeRequest.get(path[0]);
        if (!root) {
            console.error("Root not found for path: " + path);
            return;
        }
        let node: TreeViewRequest | undefined = root;
        for (let key of path.slice(1, path.length)) {
            node = node?.getChild(key);
        }
        if (node) {
            node.setExpand(false);
            await this.updateTreeViewRequest([root]);
        }
    }
}
