import Combine from "./Combine";
import BaseUIElement from "../BaseUIElement";
import {Translation} from "../i18n/Translation";
import {FixedUiElement} from "./FixedUiElement";
import Title from "./Title";
import List from "./List";
import Hash from "../../Logic/Web/Hash";
import Link from "./Link";
import {Utils} from "../../Utils";

export default class TableOfContents extends Combine {

    private readonly titles: Title[]

    constructor(elements: Combine | Title[], options?: {
        noTopLevel: false | boolean,
        maxDepth?: number
    }) {
        let titles: Title[]
        if (elements instanceof Combine) {
            titles = TableOfContents.getTitles(elements.getElements())
        } else {
            titles = elements
        }

        let els: { level: number, content: BaseUIElement }[] = []
        for (const title of titles) {
            let content: BaseUIElement
            if (title.title instanceof Translation) {
                content = title.title.Clone()
            } else if (title.title instanceof FixedUiElement) {
                content = title.title
            } else if (Utils.runningFromConsole) {
                content = new FixedUiElement(title.AsMarkdown())
            } else {
                content = new FixedUiElement(title.title.ConstructElement().innerText)
            }

            const vis = new Link(content, "#" + title.id)

            Hash.hash.addCallbackAndRun(h => {
                if (h === title.id) {
                    vis.SetClass("font-bold")
                } else {
                    vis.RemoveClass("font-bold")
                }
            })
            els.push({level: title.level, content: vis})

        }
        const minLevel = Math.min(...els.map(e => e.level))
        if (options?.noTopLevel) {
            els = els.filter(e => e.level !== minLevel)
        }

        if (options?.maxDepth) {
            els = els.filter(e => e.level <= (options.maxDepth + minLevel))
        }


        super(TableOfContents.mergeLevel(els).map(el => el.SetClass("mt-2")));
        this.SetClass("flex flex-col")
        this.titles = titles;
    }

    private static getTitles(elements: BaseUIElement[]): Title[] {
        const titles = []
        for (const uiElement of elements) {
            if (uiElement instanceof Combine) {
                titles.push(...TableOfContents.getTitles(uiElement.getElements()))
            } else if (uiElement instanceof Title) {
                titles.push(uiElement)
            }
        }
        return titles
    }

    private static mergeLevel(elements: { level: number, content: BaseUIElement }[]): BaseUIElement[] {
        const maxLevel = Math.max(...elements.map(e => e.level))
        const minLevel = Math.min(...elements.map(e => e.level))
        if (maxLevel === minLevel) {
            return elements.map(e => e.content)
        }
        const result: { level: number, content: BaseUIElement } [] = []
        let running: BaseUIElement[] = []
        for (const element of elements) {
            if (element.level === maxLevel) {
                running.push(element.content)
                continue
            }
            if (running.length !== undefined) {
                result.push({
                    content: new List(running),
                    level: maxLevel - 1
                })
                running = []
            }
            result.push(element)
        }
        if (running.length !== undefined) {
            result.push({
                content: new List(running),
                level: maxLevel - 1
            })
        }

        return TableOfContents.mergeLevel(result)
    }

    AsMarkdown(): string {
        const depthIcons = ["1.", "  -", "    +", "      *"]
        const lines = ["## Table of contents\n"];
        const minLevel = Math.min(...this.titles.map(t => t.level))
        for (const title of this.titles) {
            const prefix = depthIcons[title.level - minLevel] ?? "        ~"
            const text = title.title.AsMarkdown().replace("\n", "")
            const link = title.id
            lines.push(prefix + " [" + text + "](#" + link + ")")
        }

        return lines.join("\n") + "\n\n"
    }
}