import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { SelectionManager } from '../../../util/SelectionManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { undoable, UndoManager } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { EditableView } from '../../EditableView';
import { SelectedDocView } from '../../selectedDoc';
import { StyleProp } from '../../StyleProvider';
import { OpenWhere } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import './FontIconBox.scss';
import TrailsIcon from './TrailsIcon';
import { InkTool } from '../../../../fields/InkField';

export enum ButtonType {
    TextButton = 'textBtn',
    MenuButton = 'menuBtn',
    DropdownList = 'dropdownList',
    ClickButton = 'clickBtn',
    ToggleButton = 'toggleBtn',
    ColorButton = 'colorBtn',
    ToolButton = 'toolBtn',
    MultiToggleButton = 'multiToggleBtn',
    NumberSliderButton = 'numSliderBtn',
    NumberDropdownButton = 'numDropdownBtn',
    NumberInlineButton = 'numInlineBtn',
    EditableText = 'editableText',
}

export interface ButtonProps extends FieldViewProps {
    type?: ButtonType;
}
@observer
export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(FontIconBox, fieldKey);
    }

    constructor(props: ButtonProps) {
        super(props);
        makeObservable(this);
    }
    //
    // This controls whether fontIconButtons will display labels under their icons or not
    //
    public static get ShowIconLabels() {
        return BoolCast(Doc.UserDoc()._showLabel);
    }
    public static set ShowIconLabels(show: boolean) {
        Doc.UserDoc()._showLabel = show;
    }

    @observable noTooltip = false;

    showTemplate = (dragFactory: Doc) => this._props.addDocTab(dragFactory, OpenWhere.addRight);
    specificContextMenu = (): void => {
        const dragFactory = DocCast(this.layoutDoc.dragFactory);
        if (!Doc.noviceMode && dragFactory) {
            ContextMenu.Instance.addItem({ description: 'Show Template', event: () => this.showTemplate(dragFactory), icon: 'tag' });
        }
    };

    /**
     * this chooses the appropriate title for the label
     * if the Document is a template, then we use the title of the data doc that it renders
     * otherwise, we use the Document's title itself.
     */
    @computed get label() {
        return StrCast(this.Document.isTemplateDoc ? this.dataDoc.title : this.Document.title);
    }
    Icon = (color: string, iconFalse?: boolean) => {
        let icon;
        if (iconFalse) {
            icon = StrCast(this.dataDoc[this.fieldKey ?? 'iconFalse'] ?? this.dataDoc.icon, 'user') as any;
            if (icon) return <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
            else return null;
        }
        icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any;
        return !icon ? null : icon === 'pres-trail' ? TrailsIcon(color) : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />;
    };
    @computed get dropdown() {
        return BoolCast(this.Document.dropDownOpen);
    }
    @computed get buttonList() {
        return StrListCast(this.Document.btnList);
    }
    @computed get type() {
        return StrCast(this.Document.btnType);
    }

    /**
     * Types of buttons in dash:
     * - Main menu button (LHS)
     * - Tool button
     * - Expandable button (CollectionLinearView)
     * - Button inside of CollectionLinearView vs. outside of CollectionLinearView
     * - Action button
     * - Dropdown button
     * - Color button
     * - Dropdown list
     * - Number button
     **/

    _batch: UndoManager.Batch | undefined = undefined;
    /**
     * Number button
     */
    @computed get numberDropdown() {
        let type: NumberDropdownType;
        switch (this.type) {
            case ButtonType.NumberDropdownButton:
                type = 'dropdown';
                break;
            case ButtonType.NumberInlineButton:
                type = 'input';
                break;
            case ButtonType.NumberSliderButton:
            default:
                type = 'slider';
                break;
        }
        const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, self: this.Document, value, _readOnly_: value === undefined });
        const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
        // Script for checking the outcome of the toggle
        const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3)));

        return (
            <NumberDropdown
                color={color}
                background={SettingsManager.userBackgroundColor}
                numberDropdownType={type}
                showPlusMinus={false}
                tooltip={this.label}
                type={Type.PRIM}
                min={NumCast(this.dataDoc.numBtnMin, 0)}
                max={NumCast(this.dataDoc.numBtnMax, 100)}
                number={checkResult}
                setNumber={undoable(value => numScript(value), `${this.Document.title} button set from list`)}
                fillWidth
            />
        );
    }

    dropdownItemDown = (e: React.PointerEvent, value: string | number) => {
        setupMoveUpEvents(
            this,
            e,
            (e: PointerEvent) => {
                return ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, self: this.Document, value: { doc: value, e } }).result;
            },
            emptyFunction,
            emptyFunction
        );
        return false;
    };

    /**
     * Dropdown list
     */
    @computed get dropdownListButton() {
        const script = ScriptCast(this.Document.script);

        let noviceList: string[] = [];
        let text: string | undefined;
        let getStyle: (val: string) => any = () => {};
        let icon: IconProp = 'caret-down';
        const isViewDropdown = script?.script.originalScript.startsWith('setView');
        if (isViewDropdown) {
            const selected = SelectionManager.Docs;
            if (selected.lastElement()) {
                if (StrCast(selected.lastElement().type) === DocumentType.COL) {
                    text = StrCast(selected.lastElement()._type_collection);
                } else {
                    if (selected.length > 1) {
                        text = selected.length + ' selected';
                    } else {
                        text = Utils.cleanDocumentType(StrCast(selected.lastElement().type) as DocumentType);
                        icon = Doc.toIcon(selected.lastElement());
                    }
                    return (
                        <Popup
                            icon={<FontAwesomeIcon size={'1x'} icon={icon} />}
                            text={text}
                            type={Type.TERT}
                            color={SettingsManager.userColor}
                            background={SettingsManager.userVariantColor}
                            popup={<SelectedDocView selectedDocs={selected} />}
                            fillWidth
                        />
                    );
                }
            } else {
                return <Button text="None Selected" type={Type.TERT} color={SettingsManager.userColor} background={SettingsManager.userVariantColor} fillWidth inactive />;
            }
            noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
        } else {
            text = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result;
            // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
            getStyle = (val: string) => ({ fontFamily: val });
        }

        // Get items to place into the list
        const list: IListItemProps[] = this.buttonList
            .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
            .map(value => ({
                text: typeof value === 'string' ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
                val: value,
                style: getStyle(value),
                // shortcut: '#',
            }));

        return (
            <Dropdown
                selectedVal={text}
                setSelectedVal={undoable(value => script.script.run({ this: this.Document, self: this.Document, value }), `dropdown select ${this.label}`)}
                color={SettingsManager.userColor}
                background={SettingsManager.userVariantColor}
                type={Type.TERT}
                closeOnSelect={false}
                dropdownType={DropdownType.SELECT}
                onItemDown={this.dropdownItemDown}
                items={list}
                tooltip={this.label}
                fillWidth
            />
        );
    }

    @computed get colorScript() {
        return ScriptCast(this.Document.script);
    }

    colorBatch: UndoManager.Batch | undefined;
    /**
     * Color button
     */
    @computed get colorButton() {
        const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
        const curColor = this.colorScript?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent';
        const tooltip: string = StrCast(this.Document.toolTip);

        return (
            <ColorPicker
                setSelectedColor={value => {
                    if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
                    this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false });
                }}
                setFinalColor={value => {
                    this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false });
                    this.colorBatch?.end();
                    this.colorBatch = undefined;
                }}
                defaultPickerType="Classic"
                selectedColor={curColor}
                type={Type.PRIM}
                color={color}
                background={SettingsManager.userBackgroundColor}
                icon={this.Icon(color)!}
                tooltip={tooltip}
                label={this.label}
            />
        );
    }
    @computed get multiToggleButton() {
        // Determine the type of toggle button
        const tooltip: string = StrCast(this.Document.toolTip);

        const script = ScriptCast(this.Document.onClick)?.script;
        const toggleStatus = script?.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result;
        // Colors
        const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
        const items = DocListCast(this.dataDoc.data);
        const multiDoc = this.Document;
        return (
            <MultiToggle
                tooltip={`Toggle ${tooltip}`}
                type={Type.PRIM}
                color={color}
                onPointerDown={e => script && !toggleStatus && setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => script.run({ this: multiDoc, value: undefined, _readOnly_: false }))}
                isToggle={script ? true : false}
                toggleStatus={toggleStatus}
                //background={SettingsManager.userBackgroundColor}
                label={this.label}
                items={DocListCast(this.dataDoc.data).map(item => ({
                    icon: <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={StrCast(item.icon) as any} color={color} />,
                    tooltip: StrCast(item.toolTip),
                    val: StrCast(item.toolType),
                }))}
                selectedVal={StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType ?? StrCast(multiDoc.toolType))}
                setSelectedVal={(val: string | number) => {
                    const itemDoc = items.find(item => item.toolType === val);
                    itemDoc && ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: val, _readOnly_: false });
                }}
            />
        );
    }

    @observable _hackToRecompute = 0; // bcz: ugh ... <Toggle>'s toggleStatus initializes but doesn't track its value after a click.  so a click that does nothing to the toggle state will toggle the button anyway.  this forces the Toggle to re-read the ToggleStatus value.

    @computed get toggleButton() {
        // Determine the type of toggle button
        const buttonText = StrCast(this.dataDoc.buttonText);
        const tooltip = StrCast(this.Document.toolTip);

        const script = ScriptCast(this.Document.onClick);
        const double = ScriptCast(this.Document.onDoubleClick);
        const toggleStatus = script?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? false;
        // Colors
        const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
        const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);

        return (
            <Toggle
                tooltip={`Toggle ${tooltip}`}
                toggleType={ToggleType.BUTTON}
                type={Type.PRIM}
                toggleStatus={toggleStatus}
                text={buttonText}
                color={color}
                // background={SettingsManager.userBackgroundColor}
                icon={this.Icon(color)!}
                label={this.label}
                onPointerDown={e =>
                    setupMoveUpEvents(
                        this,
                        e,
                        returnTrue,
                        emptyFunction,
                        action((e, doubleTap) => {
                            (!doubleTap || !double) && script?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
                            doubleTap && double?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false });
                            this._hackToRecompute = this._hackToRecompute + 1;
                        })
                    )
                }
            />
        );
    }

    /**
     * Default
     */
    @computed get defaultButton() {
        const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
        const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);
        const tooltip: string = StrCast(this.Document.toolTip);

        return <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label} />;
    }

    @computed get editableText() {
        // Script for running the toggle
        const script = ScriptCast(this.Document.script);
        // Function to run the script
        const checkResult = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result;

        const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.Document, self: this.Document, value, _readOnly_: false }).result;

        return <EditableText editing={false} setEditing={(editing: boolean) => {}} />;

        return (
            <div className="menuButton editableText">
                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} />
                <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}>
                    <EditableView GetValue={() => script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
                </div>
            </div>
        );
    }

    renderButton = () => {
        const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
        const tooltip = StrCast(this.Document.toolTip);
        const scriptFunc = () => ScriptCast(this.Document.onClick)?.script.run({ this: this.Document, self: this.Document, _readOnly_: false });
        const btnProps = { tooltip, icon: this.Icon(color)!, label: this.label };
        // prettier-ignore
        switch (this.type) {
            case ButtonType.NumberDropdownButton: 
            case ButtonType.NumberInlineButton:
            case ButtonType.NumberSliderButton: return this.numberDropdown;
            case ButtonType.EditableText:       return this.editableText; 
            case ButtonType.DropdownList:       return this.dropdownListButton;
            case ButtonType.ColorButton:        return this.colorButton; 
            case ButtonType.MultiToggleButton:  return this.multiToggleButton;
            case ButtonType.ToggleButton:       return this.toggleButton;
            case ButtonType.ClickButton:return <IconButton {...btnProps} size={Size.MEDIUM} color={color} />;
            case ButtonType.ToolButton: return <IconButton {...btnProps} size={Size.LARGE} color={color} />;
            case ButtonType.TextButton: return <Button     {...btnProps} color={color} 
                        background={SettingsManager.userBackgroundColor} text={StrCast(this.dataDoc.buttonText)}/>;
            case ButtonType.MenuButton: return <IconButton {...btnProps} color={color} 
                        background={SettingsManager.userBackgroundColor} size={Size.LARGE} tooltipPlacement='right' onPointerDown={scriptFunc} />; 
        }
        return this.defaultButton;
    };

    render() {
        return (
            <div style={{ margin: 'auto', width: '100%' }} onContextMenu={this.specificContextMenu}>
                {this.renderButton()}
            </div>
        );
    }
}
