import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { StopEvent, emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DragManager } from '../../util/DragManager';
import { StyleProp } from '../StyleProvider';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView } from './CollectionSubView';

@observer
export class CollectionCarouselView extends CollectionSubView() {
    private _dropDisposer?: DragManager.DragDropDisposer;

    constructor(props: any) {
        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);
        }
    };

    /**
     * Goes to the next flashcard in the stack and filters
     * based on the the currently selected option.
     */
    advance = (e: React.MouseEvent) => {
        e.stopPropagation();
        this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + 1) % this.childLayoutPairs.length;
        var startInd = this.layoutDoc._carousel_index;

        // if the star filter is selected
        if (this.layoutDoc[`filterOp`] == 'star') {
            // go to a flashcard that is starred, skip the ones that aren't
            while (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`] && (startInd + 1) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) {
                startInd = (startInd + 1) % this.childLayoutPairs.length;
            }
            this.layoutDoc._carousel_index = startInd;
            // if there aren't any starred, show all cards
            if (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`]) {
                this.layoutDoc[`filterOp`] = 'all';
            }
        }

        // if the practice filter is selected
        if (this.layoutDoc[`filterOp`] == 'practice') {
            // go to a new index that is missed, skip the ones that are correct
            while (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct' && (startInd + 1) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) {
                startInd = (startInd + 1) % this.childLayoutPairs.length;
            }
            this.layoutDoc._carousel_index = startInd;

            // if the user has gone through all of the cards and gotten them all correct, show all cards and exit practice mode
            if (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct') {
                this.layoutDoc[`filterOp`] = 'all';

                // set all the cards to missed
                for (var i = 0; i < this.childLayoutPairs.length; i++) {
                    const curDoc = this.childLayoutPairs?.[NumCast(i)];
                    curDoc.layout[`${this.fieldKey}_missed`] = undefined;
                }
            }
        }
    };

    /**
     * Goes to the previous flashcard in the stack and filters
     * based on the the currently selected option.
     */
    goback = (e: React.MouseEvent) => {
        e.stopPropagation();
        this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;

        var startInd = this.layoutDoc._carousel_index;

        // if the star filter is selected
        if (this.layoutDoc[`filterOp`] == 'star') {
            // go to a new index that is starred, skip the ones that aren't
            while (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`] && (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) {
                startInd = (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
            }
            this.layoutDoc._carousel_index = startInd;
            // if there aren't any starred, show all cards
            if (!this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_star`]) {
                this.layoutDoc[`filterOp`] = 'all';
            }
        }

        // if the practice filter is selected
        if (this.layoutDoc[`filterOp`] == 'practice') {
            // go to a new index that is missed, skip the ones that are correct
            while (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct' && (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length != this.layoutDoc._carousel_index) {
                startInd = (startInd - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
            }

            this.layoutDoc._carousel_index = startInd;

            // See all flashcards when finish going through practice mode and set all of the flashcards back to
            if (this.childLayoutPairs?.[NumCast(startInd)].layout[`${this.fieldKey}_missed`] == 'correct') {
                this.layoutDoc[`filterOp`] = 'all';

                for (var i = 0; i < this.childLayoutPairs.length; i++) {
                    const curDoc = this.childLayoutPairs?.[NumCast(i)];
                    curDoc.layout[`${this.fieldKey}_missed`] = undefined;
                }
            }
        }
    };

    /*
     * Stars the document when the star button is pressed.
     */
    star = (e: React.MouseEvent) => {
        e.stopPropagation();
        const curDoc = this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)];
        if (!curDoc) return;
        if (curDoc.layout[`${this.fieldKey}_star`] == undefined) curDoc.layout[`${this.fieldKey}_star`] = true;
        else curDoc.layout[`${this.fieldKey}_star`] = !curDoc.layout[`${this.fieldKey}_star`];
    };

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

    captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string): any => {
        // 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);
    @computed get marginX() {
        return NumCast(this.layoutDoc.caption_xMargin, 50);
    }
    captionWidth = () => this._props.PanelWidth() - 2 * this.marginX;
    @computed get content() {
        const index = NumCast(this.layoutDoc._carousel_index);
        const curDoc = this.childLayoutPairs?.[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}
                        layout_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),
                            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.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)]) return;
        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.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)].layout[`${this.fieldKey}_star`] ? 'yellow' : 'gray'} size={'1x'} />
                </div>
                <div key="remove" className="carouselView-remove" onClick={e => this.missed(e, 'missed')} style={{ visibility: this.layoutDoc[`filterOp`] == 'practice' ? 'visible' : 'hidden' }}>
                    <FontAwesomeIcon icon={'xmark'} color={'red'} size={'1x'} />
                </div>
                <div key="check" className="carouselView-check" onClick={e => this.missed(e, 'correct')} style={{ visibility: this.layoutDoc[`filterOp`] == 'practice' ? 'visible' : 'hidden' }}>
                    <FontAwesomeIcon icon={'check'} color={'green'} size={'1x'} />
                </div>
            </>
        );
    }

    render() {
        return (
            <div
                className="collectionCarouselView-outer"
                ref={this.createDashEventsTarget}
                style={{
                    background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor),
                    color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color),
                }}>
                {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.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)] && this.layoutDoc[`filterOp`] == '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.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)] ? (this.childLayoutPairs?.[NumCast(this.layoutDoc._carousel_index)].layout[`${this.fieldKey}_missed`] == 'missed' ? 'block' : 'none') : 'none',
                    }}>
                    Recently missed!
                </p>
                {this.Document._chromeHidden ? null : this.buttons}
            </div>
        );
    }
}
