// Part of the Chili3d Project, under the AGPL-3.0 License.
// See LICENSE file in the project root for full license information.

import {
    Binding,
    type CommandKeys,
    CommandStore,
    Config,
    type IApplication,
    type ICommand,
    type IConverter,
    type IView,
    Localize,
    Logger,
    ObservableCollection,
    PubSub,
    Result,
    type Ribbon,
    type RibbonCommand,
    type RibbonGroup,
    type RibbonTab,
} from "@chili3d/core";
import { collection, createIcon, div, label, span, svg } from "@chili3d/element";
import { CommandContext } from "./commandContext";
import style from "./ribbon.module.css";
import { RibbonButton } from "./ribbonButton";
import { RibbonStack } from "./ribbonStack";

export const QuickButton = (command: ICommand) => {
    const data = CommandStore.getComandData(command);
    if (!data) {
        Logger.warn("commandData is undefined");
        return span({ textContent: "null" });
    }

    const icon = createIcon(data.icon);
    icon.classList.add(style.icon);
    return span(
        {
            title: new Localize(`command.${data.key}`),
            onclick: () => PubSub.default.pub("executeCommand", data.key),
        },
        icon,
    );
};

class ViewActiveConverter implements IConverter<IView> {
    constructor(
        readonly target: IView,
        readonly style: string,
        readonly activeStyle: string,
    ) {}

    convert(value: IView): Result<string> {
        return Result.ok(this.target === value ? `${this.style} ${this.activeStyle}` : this.style);
    }
}

class ActivedRibbonTabConverter implements IConverter<RibbonTab> {
    constructor(
        readonly tab: RibbonTab,
        readonly style: string,
        readonly activeStyle: string,
    ) {}

    convert(value: RibbonTab): Result<string> {
        return Result.ok(this.tab === value ? `${this.style} ${this.activeStyle}` : this.style);
    }
}

class DisplayConverter implements IConverter<RibbonTab> {
    constructor(readonly tab: RibbonTab) {}

    convert(value: RibbonTab): Result<string> {
        return Result.ok(this.tab === value ? "" : "none");
    }
}

export class RibbonUI extends HTMLElement {
    private readonly _commandContext = div({ className: style.commandContextPanel });
    private commandContext?: CommandContext;

    constructor(
        readonly app: IApplication,
        readonly dataContent: Ribbon,
    ) {
        super();
        this.className = style.root;
        this.append(this.header(), this.ribbonTabs(), this._commandContext);
    }

    private header() {
        return div({ className: style.titleBar }, this.leftPanel(), this.rightPanel());
    }

    private leftPanel() {
        return div(
            { className: style.left },
            div(
                { className: style.appIcon },
                span({ id: "appName", textContent: "BidWright Model" }),
            ),
            div(
                { className: style.ribbonTitlePanel },
                collection({
                    className: style.quickCommands,
                    sources: this.dataContent.quickCommands,
                    template: (command: CommandKeys) => QuickButton(command as any),
                }),
                span({ className: style.split }),
                this.createRibbonHeader(),
            ),
        );
    }

    private createRibbonHeader() {
        return collection({
            sources: this.dataContent.tabs,
            template: (tab: RibbonTab) => {
                const converter = new ActivedRibbonTabConverter(tab, style.tabHeader, style.activedTab);
                return label({
                    className: new Binding(this.dataContent, "activeTab", converter),
                    textContent: new Localize(tab.tabName),
                    onclick: () => {
                        this.dataContent.activeTab = tab;
                    },
                });
            },
        });
    }

    private centerPanel() {
        return div(
            { className: style.center },
            collection({
                className: style.views,
                sources: this.app.views,
                template: (view) => this.createViewItem(view),
            }),
        );
    }

    private createViewItem(view: IView) {
        return div(
            {
                className: new Binding(
                    this.app,
                    "activeView",
                    new ViewActiveConverter(view, style.tab, style.active),
                ),
                onclick: () => {
                    this.app.activeView = view;
                },
            },
            div({ className: style.name }, span({ textContent: new Binding(view.document, "name") })),
            svg({
                className: style.close,
                icon: "icon-times",
                onclick: (e) => {
                    e.stopPropagation();
                    view.close();
                },
            }),
        );
    }

    private rightPanel() {
        return div({ className: style.right });
    }

    private ribbonTabs() {
        return collection({
            className: style.tabContentPanel,
            sources: this.dataContent.tabs,
            template: (tab: RibbonTab) => this.ribbonTab(tab),
        });
    }

    private ribbonTab(tab: RibbonTab) {
        return collection({
            className: style.groupPanel,
            sources: tab.groups,
            style: {
                display: new Binding(this.dataContent, "activeTab", new DisplayConverter(tab)),
            },
            template: (group: RibbonGroup) => this.ribbonGroup(group),
        });
    }

    private ribbonGroup(group: RibbonGroup) {
        return div(
            { className: style.ribbonGroup },
            collection({
                sources: group.items,
                className: style.content,
                template: (item) => this.ribbonButton(item),
            }),
            label({ className: style.header, textContent: new Localize(group.groupName) }),
        );
    }

    private ribbonButton(item: RibbonCommand) {
        if (typeof item === "string") {
            return RibbonButton.fromCommandName(item, "large")!;
        } else if (item instanceof ObservableCollection) {
            const stack = new RibbonStack();
            item.forEach((b) => {
                const button = RibbonButton.fromCommandName(b, "small");
                if (button) stack.append(button);
            });
            return stack;
        } else {
            return new RibbonButton(item.command, item.icon, "large", item.onClick, item.display);
        }
    }

    connectedCallback(): void {
        PubSub.default.sub("openCommandContext", this.openContext);
        PubSub.default.sub("closeCommandContext", this.closeContext);
        Config.instance.onPropertyChanged(this.handleConfigChanged);
    }

    disconnectedCallback(): void {
        PubSub.default.remove("openCommandContext", this.openContext);
        PubSub.default.remove("closeCommandContext", this.closeContext);
        Config.instance.removePropertyChanged(this.handleConfigChanged);
    }

    private readonly handleConfigChanged = (prop: keyof Config) => {
        if (prop === "navigation3D") {
            this.querySelectorAll(customElements.getName(RibbonButton)!).forEach((x) => {
                (x as RibbonButton).updateShortcut();
            });
        }
    };

    private readonly openContext = (command: ICommand) => {
        if (this.commandContext) {
            this.closeContext();
        }
        this.commandContext = new CommandContext(command);
        this._commandContext.append(this.commandContext);
    };

    private readonly closeContext = () => {
        this.commandContext?.remove();
        this.commandContext?.dispose();
        this.commandContext = undefined;
        this._commandContext.innerHTML = "";
    };
}

customElements.define("chili-ribbon", RibbonUI);
