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, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
import { ScriptField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, 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 { DocComponent } 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';

export enum ButtonType {
    TextButton = 'textBtn',
    MenuButton = 'menuBtn',
    DropdownList = 'dropdownList',
    DropdownButton = 'dropdownBtn',
    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 DocComponent<ButtonProps>() {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(FontIconBox, fieldKey);
    }
    @observable noTooltip = false;
    showTemplate = (): void => {
        const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
        dragFactory && this.props.addDocTab(dragFactory, OpenWhere.addRight);
    };
    dragAsTemplate = (): void => {
        this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
    };
    useAsPrototype = (): void => {
        this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)');
    };

    specificContextMenu = (): void => {
        if (!Doc.noviceMode) {
            const cm = ContextMenu.Instance;
            cm.addItem({ description: 'Show Template', event: this.showTemplate, icon: 'tag' });
            cm.addItem({ description: 'Use as Render Template', event: this.dragAsTemplate, icon: 'tag' });
            cm.addItem({ description: 'Use as Prototype', event: this.useAsPrototype, icon: 'tag' });
        }
    };

    // Determining UI Specs
    @computed get label() {
        return StrCast(this.rootDoc.icon_label, StrCast(this.rootDoc.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.rootDoc.dropDownOpen);
    }
    @computed get buttonList() {
        return StrListCast(this.rootDoc.btnList);
    }
    @computed get type() {
        return StrCast(this.rootDoc.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.rootDoc.script).script.run({ this: this.layoutDoc, self: this.rootDoc, value, _readOnly_: value === undefined });
        const color = this.props.styleProvider?.(this.rootDoc, 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.rootDoc.numBtnMin, 0)}
                max={NumCast(this.rootDoc.numBtnMax, 100)}
                number={checkResult}
                setNumber={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)}
                fillWidth
            />
        );
    }

    /**
     * Dropdown button
     */
    @computed get dropdownButton() {
        const active: string = StrCast(this.rootDoc.dropDownOpen);
        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
        return (
            <div
                className={`menuButton ${this.type} ${active}`}
                style={{ color, backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                onClick={action(() => {
                    this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen;
                    this.noTooltip = this.rootDoc.dropDownOpen;
                    Doc.UnBrushAllDocs();
                })}>
                {this.Icon(color)}
                {!this.label || !Doc.GetShowIconLabels() ? null : (
                    <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}>
                        {' '}
                        {this.label}{' '}
                    </div>
                )}
                <div className="menuButton-dropdown" style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
                    <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
                </div>
                {this.rootDoc.dropDownOpen ? <div className="menuButton-dropdownBox">{/* DROPDOWN BOX CONTENTS */}</div> : null}
            </div>
        );
    }

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

    /**
     * Dropdown list
     */
    @computed get dropdownListButton() {
        const script = ScriptCast(this.rootDoc.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.Stacking, CollectionViewType.NoteTaking];
        } else {
            text = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, 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),
                onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
                // shortcut: '#',
            }));

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

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

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

        return (
            <ColorPicker
                setSelectedColor={value => {
                    if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
                    this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false });
                }}
                setFinalColor={value => {
                    this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, 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.rootDoc.toolTip);

        const script = ScriptCast(this.rootDoc.onClick);
        const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
        // Colors
        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
        const items = DocListCast(this.rootDoc.data);
        return (
            <MultiToggle
                tooltip={`Toggle ${tooltip}`}
                type={Type.PRIM}
                color={color}
                background={SettingsManager.userBackgroundColor}
                label={this.label}
                items={DocListCast(this.rootDoc.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)}
                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 });
                }}
            />
        );
    }

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

        const script = ScriptCast(this.rootDoc.onClick);
        const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false;
        // Colors
        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
        const backgroundColor = this.props.styleProvider?.(this.rootDoc, 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={() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })}
            />
        );
    }

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

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

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

        const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.layoutDoc, self: this.rootDoc, 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.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} />
                </div>
            </div>
        );
    }

    render() {
        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
        const tooltip = StrCast(this.rootDoc.toolTip);
        const scriptFunc = () => ScriptCast(this.rootDoc.onClick)?.script.run({ this: this.layoutDoc, self: this.rootDoc, _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.DropdownButton:     return this.dropdownButton;
            case ButtonType.MultiToggleButton:  return this.multiToggleButton;
            case ButtonType.ToggleButton:       return this.toggleButton;
            case ButtonType.ClickButton:
            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.rootDoc.buttonText)}/>;
            case ButtonType.MenuButton: return <IconButton {...btnProps} color={color} 
            background={SettingsManager.userBackgroundColor}  size={Size.LARGE} tooltipPlacement='right' onPointerDown={scriptFunc} />; 
        }
        return this.defaultButton;
    }
}
