/* eslint-disable react/jsx-props-no-spreading */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { StopEvent, returnFalse, returnOne, returnTrue, returnZero } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { StyleProp } from '../StyleProp';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';

enum cardMode {
    PRACTICE = 'practice',
    STAR = 'star',
    QUIZ = 'quiz',
}
enum practiceVal {
    MISSED = 'missed',
    CORRECT = 'correct',
}
@observer
export class CollectionCarouselView extends CollectionSubView() {
    private _dropDisposer?: DragManager.DragDropDisposer;
    get practiceField() { return this.fieldKey + "_practice"; } // prettier-ignore
    get starField()     { return this.fieldKey + "_star"; } // prettier-ignore

    constructor(props: SubCollectionViewProps) {
        super(props);
        makeObservable(this);
    }

    componentWillUnmount() {
        this._dropDisposer?.();
    }

    protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
        this._dropDisposer?.();
        if (ele) {
            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
        }
    };

    @computed get carouselItems() {
        return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK);
    }
    @computed get marginX() {
        return NumCast(this.layoutDoc.caption_xMargin, 50);
    }

    move = (dir: number) => {
        const moveToCardWithField = (match: (doc: Doc) => boolean): boolean => {
            let startInd = (NumCast(this.layoutDoc._carousel_index) + dir) % this.carouselItems.length;
            while (!match(this.carouselItems?.[startInd].layout) && (startInd + dir + this.carouselItems.length) % this.carouselItems.length !== this.layoutDoc._carousel_index) {
                startInd = (startInd + dir + this.carouselItems.length) % this.carouselItems.length;
            }
            if (match(this.carouselItems?.[startInd].layout)) {
                this.layoutDoc._carousel_index = startInd;
                return true;
            }
            return match(this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)].layout);
        };
        switch (StrCast(this.layoutDoc.filterOp)) {
            case cardMode.STAR:  // go to a flashcard that is starred, skip the ones that aren't
                if (!moveToCardWithField((doc: Doc) => !!doc[this.starField])) {
                    this.layoutDoc.filterOp = undefined;  // if there aren't any starred, show all cards
                }
                break;
            case cardMode.PRACTICE: // go to a new index that is missed, skip the ones that are correct
                if (!moveToCardWithField((doc: Doc) => doc[this.practiceField] !== practiceVal.CORRECT)) {
                    this.layoutDoc.filterOp = undefined;  // if all of the cards are correct, show all cards and exit practice mode

                    this.carouselItems.forEach(item => { // reset all the practice values
                        item.layout[this.practiceField] = undefined;
                    });
                }
                break;
            default: moveToCardWithField(returnTrue);
        } // prettier-ignore
    };

    /**
     * Goes to the next Doc in the stack subject to the currently selected filter option.
     */
    advance = (e: React.MouseEvent) => {
        e.stopPropagation();
        this.move(1);
    };

    /**
     * Goes to the previous Doc in the stack subject to the currently selected filter option.
     */
    goback = (e: React.MouseEvent) => {
        e.stopPropagation();
        this.move(-1);
    };

    /*
     * Stars the document when the star button is pressed.
     */
    star = (e: React.MouseEvent) => {
        e.stopPropagation();
        const curDoc = this.carouselItems[NumCast(this.layoutDoc._carousel_index)];
        curDoc.layout[this.starField] = curDoc.layout[this.starField] ? undefined : true;
    };

    /*
     * Sets a flashcard to either missed or correct depending on if they got the question right in practice mode.
     */
    setPracticeVal = (e: React.MouseEvent, val: string) => {
        e.stopPropagation();
        const curDoc = this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)];
        curDoc.layout[this.practiceField] = val;
        this.advance(e);
    };

    captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string) => {
        // first look for properties on the document in the carousel, then fallback to properties on the container
        const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
        return childValue ?? this._props.styleProvider?.(this.layoutDoc, captionProps, property);
    };
    panelHeight = () => this._props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0);
    onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
    onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
    captionWidth = () => this._props.PanelWidth() - 2 * this.marginX;
    specificMenu = (): void => {
        const cm = ContextMenu.Instance;

        const revealOptions = cm.findByDescription('Filter Flashcards');
        const revealItems: ContextMenuProps[] = revealOptions && 'subitems' in revealOptions ? revealOptions.subitems : [];
        revealItems.push({description: 'All',           event: () => {this.layoutDoc.filterOp = undefined;},         icon: 'layer-group',}); // prettier-ignore
        revealItems.push({description: 'Star',          event: () => {this.layoutDoc.filterOp = cardMode.STAR;},     icon: 'star',}); // prettier-ignore
        revealItems.push({description: 'Practice Mode', event: () => {this.layoutDoc.filterOp = cardMode.PRACTICE;}, icon: 'check',}); // prettier-ignore
        revealItems.push({description: 'Quiz Cards',    event: () => {this.layoutDoc.filterOp = cardMode.QUIZ;},     icon: 'pencil',}); // prettier-ignore
        !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' });
    };
    @computed get content() {
        const index = NumCast(this.layoutDoc._carousel_index);
        const curDoc = this.carouselItems?.[index];
        const captionProps = { ...this._props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
        const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption);
        return !(curDoc?.layout instanceof Doc) ? null : (
            <>
                <div className="collectionCarouselView-image" key="image">
                    <DocumentView
                        {...this._props}
                        NativeWidth={returnZero}
                        NativeHeight={returnZero}
                        fitWidth={undefined}
                        setContentViewBox={undefined}
                        onDoubleClickScript={this.onContentDoubleClick}
                        onClickScript={this.onContentClick}
                        isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive}
                        isContentActive={(this._props.childContentsActive ?? this._props.isContentActive() === false) ? returnFalse : emptyFunction}
                        hideCaptions={!!carouselShowsCaptions} // hide captions if the carousel is configured to show the captions
                        renderDepth={this._props.renderDepth + 1}
                        LayoutTemplate={this._props.childLayoutTemplate}
                        LayoutTemplateString={this._props.childLayoutString}
                        Document={curDoc.layout}
                        TemplateDataDocument={DocCast(curDoc.layout.resolvedDataDoc)}
                        PanelHeight={this.panelHeight}
                    />
                </div>
                {!carouselShowsCaptions ? null : (
                    <div
                        className="collectionCarouselView-caption"
                        key="caption"
                        onWheel={StopEvent}
                        style={{
                            borderRadius: this._props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding) as string,
                            marginRight: this.marginX,
                            marginLeft: this.marginX,
                            width: `calc(100% - ${this.marginX * 2}px)`,
                        }}>
                        <FormattedTextBox key={index} {...captionProps} fieldKey={carouselShowsCaptions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} TemplateDataDocument={undefined} />
                    </div>
                )}
            </>
        );
    }
    @computed get buttons() {
        if (!this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)]) return null;
        return (
            <>
                <div key="back" className="carouselView-back" onClick={this.goback}>
                    <FontAwesomeIcon icon="chevron-left" size="2x" />
                </div>
                <div key="fwd" className="carouselView-fwd" onClick={this.advance}>
                    <FontAwesomeIcon icon="chevron-right" size="2x" />
                </div>
                <div key="star" className="carouselView-star" onClick={this.star}>
                    <FontAwesomeIcon icon="star" color={this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)].layout[this.starField] ? 'yellow' : 'gray'} size="1x" />
                </div>
                <div key="remove" className="carouselView-remove" onClick={e => this.setPracticeVal(e, practiceVal.MISSED)} style={{ visibility: this.layoutDoc.filterOp === cardMode.PRACTICE ? 'visible' : 'hidden' }}>
                    <FontAwesomeIcon icon="xmark" color="red" size="1x" />
                </div>
                <div key="check" className="carouselView-check" onClick={e => this.setPracticeVal(e, practiceVal.CORRECT)} style={{ visibility: this.layoutDoc.filterOp === cardMode.PRACTICE ? 'visible' : 'hidden' }}>
                    <FontAwesomeIcon icon="check" color="green" size="1x" />
                </div>
            </>
        );
    }

    render() {
        return (
            <div
                className="collectionCarouselView-outer"
                ref={this.createDashEventsTarget}
                onContextMenu={this.specificMenu}
                style={{
                    background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string,
                    color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string,
                }}>
                {this.content}
                {/* Displays a message to the user to add more flashcards if they are in practice mode and no flashcards are there. */}
                <p
                    style={{
                        display: !this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)] && this.layoutDoc.filterOp === cardMode.PRACTICE ? 'flex' : 'none',
                        justifyContent: 'center',
                        alignItems: 'center',
                        height: '100%',
                        zIndex: '-1',
                    }}>
                    Add flashcards!
                </p>
                {/* Displays a message to the user that a flashcard was recently missed if they had previously gotten it wrong. */}
                <p
                    style={{
                        color: 'red',
                        zIndex: '999',
                        position: 'relative',
                        left: '10px',
                        top: '10px',
                        display: this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)] ? (this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)].layout[this.practiceField] === practiceVal.MISSED ? 'block' : 'none') : 'none',
                    }}>
                    Recently missed!
                </p>
                {this.Document._chromeHidden ? null : this.buttons}
            </div>
        );
    }
}
