import {VariableUiElement} from "../Base/VariableUIElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import Table from "../Base/Table";
import Combine from "../Base/Combine";
import {FixedUiElement} from "../Base/FixedUiElement";
import {Utils} from "../../Utils";
import BaseUIElement from "../BaseUIElement";
import Svg from "../../Svg";

export default class Histogram<T> extends VariableUiElement {

    private static defaultPalette = [
        "#ff5858",
        "#ffad48",
        "#ffff59",
        "#56bd56",
        "#63a9ff",
        "#9d62d9",
        "#fa61fa"
    ]

    constructor(values: UIEventSource<string[]>,
                title: string | BaseUIElement,
                countTitle: string | BaseUIElement,
                options?: {
                    assignColor?: (t0: string) => string,
                    sortMode?: "name" | "name-rev" | "count" | "count-rev"
                }
    ) {
        const sortMode = new UIEventSource<"name" | "name-rev" | "count" | "count-rev">(options?.sortMode ?? "name")
        const sortName = new VariableUiElement(sortMode.map(m => {
            switch (m) {
                case "name":
                    return Svg.up_svg()
                case "name-rev":
                    return Svg.up_svg().SetStyle("transform: rotate(180deg)")
                default:
                    return Svg.circle_svg()
            }
        }))
        const titleHeader = new Combine([sortName.SetClass("w-4 mr-2"), title]).SetClass("flex")
            .onClick(() => {
                if (sortMode.data === "name") {
                    sortMode.setData("name-rev")
                } else {
                    sortMode.setData("name")
                }
            })

        const sortCount = new VariableUiElement(sortMode.map(m => {
            switch (m) {
                case "count":
                    return Svg.up_svg()
                case "count-rev":
                    return Svg.up_svg().SetStyle("transform: rotate(180deg)")
                default:
                    return Svg.circle_svg()
            }
        }))


        const countHeader = new Combine([sortCount.SetClass("w-4 mr-2"), countTitle]).SetClass("flex").onClick(() => {
            if (sortMode.data === "count-rev") {
                sortMode.setData("count")
            } else {
                sortMode.setData("count-rev")
            }
        })

        const header = [
            titleHeader,
            countHeader
        ]

        super(values.map(values => {

            if (values === undefined) {
                return undefined;
            }

            values = Utils.NoNull(values)

            const counts = new Map<string, number>()
            for (const value of values) {
                const c = counts.get(value) ?? 0;
                counts.set(value, c + 1);
            }

            const keys = Array.from(counts.keys());

            switch (sortMode.data) {
                case "name":
                    keys.sort()
                    break;
                case "name-rev":
                    keys.sort().reverse()
                    break;
                case "count":
                    keys.sort((k0, k1) => counts.get(k0) - counts.get(k1))
                    break;
                case "count-rev":
                    keys.sort((k0, k1) => counts.get(k1) - counts.get(k0))
                    break;
            }

            const max = Math.max(...Array.from(counts.values()))

            const fallbackColor = (keyValue: string) => {
                const index = keys.indexOf(keyValue)
                return Histogram.defaultPalette[index % Histogram.defaultPalette.length]
            };
            let actualAssignColor = undefined;
            if (options?.assignColor === undefined) {
                actualAssignColor = fallbackColor;
            } else {
                actualAssignColor = (keyValue: string) => {
                    return options.assignColor(keyValue) ?? fallbackColor(keyValue)
                }
            }

            return new Table(
                header,
                keys.map(key => [
                    key,
                    new Combine([
                        new Combine([new FixedUiElement("" + counts.get(key)).SetClass("font-bold rounded-full block")])
                            .SetClass("flex justify-center rounded border border-black")
                            .SetStyle(`background: ${actualAssignColor(key)}; width: ${100 * counts.get(key) / max}%`)
                    ]).SetClass("block w-full")

                ]),
                keys.map(_ => ["width: 20%"])
            ).SetClass("w-full zebra-table");
        }, [sortMode]));
    }
}