import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { InkTool } from '../../fields/InkField';
import { List } from '../../fields/List';
import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils';
import { DocUtils } from '../documents/Documents';
import { DocumentManager } from '../util/DocumentManager';
import { LinkManager } from '../util/LinkManager';
import { SelectionManager } from '../util/SelectionManager';
import { Transform } from '../util/Transform';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
import { TabDocView } from './collections/TabDocView';
import { GestureOverlay } from './GestureOverlay';
import './LightboxView.scss';
import { MainView } from './MainView';
import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';

interface LightboxViewProps {
    PanelWidth: number;
    PanelHeight: number;
    maxBorder: number[];
}

type LightboxSavedState = {
    panX: Opt<number>;
    panY: Opt<number>;
    scale: Opt<number>;
    scrollTop: Opt<number>;
    layoutKey: Opt<string>;
};
@observer
export class LightboxView extends React.Component<LightboxViewProps> {
    @computed public static get LightboxDoc() {
        return this._doc;
    }
    private static LightboxDocTemplate = () => LightboxView._layoutTemplate;
    @observable private static _layoutTemplate: Opt<Doc>;
    @observable private static _layoutTemplateString: Opt<string>;
    @observable private static _doc: Opt<Doc>;
    @observable private static _docTarget: Opt<Doc>;
    @observable private static _docFilters: string[] = []; // filters
    private static _savedState: Opt<LightboxSavedState>;
    private static _history: Opt<{ doc: Doc; target?: Doc }[]> = [];
    @observable private static _future: Opt<Doc[]> = [];
    @observable private static _docView: Opt<DocumentView>;
    static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<LightboxSavedState> }[] = [];
    @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) {
        if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) {
            if (this._savedState.panX !== undefined) this.LightboxDoc._panX = this._savedState.panX;
            if (this._savedState.panY !== undefined) this.LightboxDoc._panY = this._savedState.panY;
            if (this._savedState.scrollTop !== undefined) this.LightboxDoc._scrollTop = this._savedState.scrollTop;
            if (this._savedState.scale !== undefined) this.LightboxDoc._viewScale = this._savedState.scale;
            this.LightboxDoc.layoutKey = this._savedState.layoutKey;
        }
        if (!doc) {
            this._docFilters && (this._docFilters.length = 0);
            this._future = this._history = [];
            Doc.ActiveTool = InkTool.None;
            MainView.Instance._exploreMode = false;
        } else {
            if (doc) {
                const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement();
                l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
            }
            CollectionStackedTimeline.CurrentlyPlaying?.forEach(doc => {
                DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => {
                    dv.ComponentView?.Pause?.();
                });
            });
            //TabDocView.PinDoc(doc, { hidePresBox: true });
            this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]);
            if (doc !== LightboxView.LightboxDoc) {
                this._savedState = {
                    layoutKey: StrCast(doc.layoutKey),
                    panX: Cast(doc.panX, 'number', null),
                    panY: Cast(doc.panY, 'number', null),
                    scale: Cast(doc.viewScale, 'number', null),
                    scrollTop: Cast(doc.scrollTop, 'number', null),
                };
            }
        }
        if (future) {
            this._future = [
                ...(this._future ?? []),
                ...(this.LightboxDoc ? [this.LightboxDoc] : []),
                ...future
                    .slice()
                    .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow))
                    .sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length),
            ];
        }
        this._doc = doc;
        this._layoutTemplate = layoutTemplate instanceof Doc ? layoutTemplate : undefined;
        if (doc && (typeof layoutTemplate === 'string' ? layoutTemplate : undefined)) {
            doc.layoutKey = layoutTemplate;
        }
        this._docTarget = target || doc;

        return true;
    }
    public static IsLightboxDocView(path: DocumentView[]) {
        return (path ?? []).includes(this._docView!);
    }
    @computed get leftBorder() {
        return Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]);
    }
    @computed get topBorder() {
        return Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]);
    }
    lightboxWidth = () => this.props.PanelWidth - this.leftBorder * 2;
    lightboxHeight = () => this.props.PanelHeight - this.topBorder * 2;
    lightboxScreenToLocal = () => new Transform(-this.leftBorder, -this.topBorder, 1);
    navBtn = (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => {
        return (
            <div
                className="lightboxView-navBtn-frame"
                style={{
                    display: display(),
                    left,
                    width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
                    bottom,
                }}>
                <div className="lightboxView-navBtn" title={color} style={{ top, color: color ? 'red' : 'white', background: color ? 'white' : undefined }} onClick={click}>
                    <div style={{ height: 10 }}>{color}</div>
                    <FontAwesomeIcon icon={icon as any} size="3x" />
                </div>
            </div>
        );
    };
    public static GetSavedState(doc: Doc) {
        return this.LightboxDoc === doc && this._savedState ? this._savedState : undefined;
    }

    // adds a cookie to the lightbox view - the cookie becomes part of a filter which will display any documents whose cookie metadata field matches this cookie
    @action
    public static SetCookie(cookie: string) {
        if (this.LightboxDoc && cookie) {
            this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`);
        }
    }
    public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => {
        if (location !== OpenWhere.lightbox) {
            const inPlaceView = DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
            if (inPlaceView) {
                inPlaceView.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List<Doc>([doc]);
                return true;
            }
        }
        SelectionManager.DeselectAll();
        return LightboxView.SetLightboxDoc(
            doc,
            undefined,
            [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort(
                (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)
            ),
            layoutTemplate
        );
    };
    docFilters = () => LightboxView._docFilters || [];
    addDocTab = LightboxView.AddDocTab;
    @action public static Next() {
        const doc = LightboxView._doc!;
        const target = (LightboxView._docTarget = this._future?.pop());
        const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
        if (targetDocView && target) {
            const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement();
            l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
            targetDocView.focus(target, { originalTarget: target, willPanZoom: true, zoomScale: 0.9 });
            if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target });
        } else {
            if (!target && LightboxView.path.length) {
                const saved = LightboxView._savedState;
                if (LightboxView.LightboxDoc && saved) {
                    LightboxView.LightboxDoc._panX = saved.panX;
                    LightboxView.LightboxDoc._panY = saved.panY;
                    LightboxView.LightboxDoc._viewScale = saved.scale;
                    LightboxView.LightboxDoc._scrollTop = saved.scrollTop;
                }
                const pop = LightboxView.path.pop();
                if (pop) {
                    LightboxView._doc = pop.doc;
                    LightboxView._docTarget = pop.target;
                    LightboxView._future = pop.future;
                    LightboxView._history = pop.history;
                    LightboxView._savedState = pop.saved;
                }
            } else {
                LightboxView.SetLightboxDoc(target);
            }
        }
    }

    @action public static Previous() {
        const previous = LightboxView._history?.pop();
        if (!previous || !LightboxView._history?.length) {
            LightboxView.SetLightboxDoc(undefined);
            return;
        }
        const { doc, target } = LightboxView._history?.lastElement();
        const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc);
        if (docView) {
            LightboxView._docTarget = target;
            target && docView.focus(target, { willPanZoom: true, zoomScale: 0.9 });
        } else {
            LightboxView.SetLightboxDoc(doc, target);
        }
        if (LightboxView._future?.lastElement() !== previous.target || previous.doc) LightboxView._future?.push(previous.target || previous.doc);
    }
    @action
    stepInto = () => {
        LightboxView.path.push({
            doc: LightboxView.LightboxDoc,
            target: LightboxView._docTarget,
            future: LightboxView._future,
            history: LightboxView._history,
            saved: LightboxView._savedState,
        });
        const coll = LightboxView._docTarget;
        if (coll) {
            const fieldKey = Doc.LayoutFieldKey(coll);
            const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])];
            const links = DocListCast(coll.links)
                .map(link => LinkManager.getOppositeAnchor(link, coll))
                .filter(doc => doc)
                .map(doc => doc!);
            LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links);
        }
    };

    future = () => LightboxView._future;
    render() {
        let downx = 0,
            downy = 0;
        return !LightboxView.LightboxDoc ? null : (
            <div
                className="lightboxView-frame"
                onPointerDown={e => {
                    downx = e.clientX;
                    downy = e.clientY;
                }}
                onClick={e => {
                    if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) {
                        LightboxView.SetLightboxDoc(undefined);
                    }
                }}>
                <div
                    className="lightboxView-contents"
                    style={{
                        left: this.leftBorder,
                        top: this.topBorder,
                        width: this.lightboxWidth(),
                        height: this.lightboxHeight(),
                        clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`,
                    }}>
                    {/* <CollectionMenu /> TODO:glr This is where it would go*/}

                    <GestureOverlay isActive={true}>
                        <DocumentView
                            ref={action((r: DocumentView | null) => (LightboxView._docView = r !== null ? r : undefined))}
                            Document={LightboxView.LightboxDoc}
                            DataDoc={undefined}
                            PanelWidth={this.lightboxWidth}
                            PanelHeight={this.lightboxHeight}
                            LayoutTemplate={LightboxView.LightboxDocTemplate}
                            isDocumentActive={returnTrue} // without this being true, sidebar annotations need to be activated before text can be selected.
                            isContentActive={returnTrue}
                            styleProvider={DefaultStyleProvider}
                            ScreenToLocalTransform={this.lightboxScreenToLocal}
                            renderDepth={0}
                            rootSelected={returnTrue}
                            docViewPath={returnEmptyDoclist}
                            docFilters={this.docFilters}
                            docRangeFilters={returnEmptyFilter}
                            searchFilterDocs={returnEmptyDoclist}
                            ContainingCollectionView={undefined}
                            ContainingCollectionDoc={undefined}
                            addDocument={undefined}
                            removeDocument={undefined}
                            whenChildContentsActiveChanged={emptyFunction}
                            addDocTab={this.addDocTab}
                            pinToPres={TabDocView.PinDoc}
                            bringToFront={emptyFunction}
                            onBrowseClick={MainView.Instance.exploreMode}
                            focus={DocUtils.DefaultFocus}
                        />
                    </GestureOverlay>
                </div>

                {this.navBtn(
                    0,
                    undefined,
                    this.props.PanelHeight / 2 - 12.5,
                    'chevron-left',
                    () => (LightboxView.LightboxDoc && LightboxView._history?.length ? '' : 'none'),
                    e => {
                        e.stopPropagation();
                        LightboxView.Previous();
                    }
                )}
                {this.navBtn(
                    this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
                    undefined,
                    this.props.PanelHeight / 2 - 12.5,
                    'chevron-right',
                    () => (LightboxView.LightboxDoc && LightboxView._future?.length ? '' : 'none'),
                    e => {
                        e.stopPropagation();
                        LightboxView.Next();
                    },
                    this.future()?.length.toString()
                )}
                <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} />
                <div
                    className="lightboxView-navBtn"
                    title={'toggle fit width'}
                    onClick={e => {
                        e.stopPropagation();
                        LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth;
                    }}>
                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
                </div>
                <div
                    className="lightboxView-tabBtn"
                    title={'open in tab'}
                    onClick={e => {
                        e.stopPropagation();
                        CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, OpenWhereMod.none);
                        SelectionManager.DeselectAll();
                        LightboxView.SetLightboxDoc(undefined);
                    }}>
                    <FontAwesomeIcon icon={'file-download'} size="2x" />
                </div>
                <div
                    className="lightboxView-penBtn"
                    title="toggle pen annotation"
                    style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }}
                    onClick={e => {
                        e.stopPropagation();
                        Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
                    }}>
                    <FontAwesomeIcon color={Doc.ActiveTool === InkTool.Pen ? 'black' : 'white'} icon={'pen'} size="2x" />
                </div>
                <div
                    className="lightboxView-exploreBtn"
                    title="toggle explore mode to navigate among documents only"
                    style={{ background: MainView.Instance._exploreMode ? 'white' : undefined }}
                    onClick={action(e => {
                        e.stopPropagation();
                        MainView.Instance._exploreMode = !MainView.Instance._exploreMode;
                    })}>
                    <FontAwesomeIcon color={MainView.Instance._exploreMode ? 'black' : 'white'} icon={'globe-americas'} size="2x" />
                </div>
            </div>
        );
    }
}
interface LightboxTourBtnProps {
    navBtn: (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => JSX.Element;
    future: () => Opt<Doc[]>;
    stepInto: () => void;
}
@observer
export class LightboxTourBtn extends React.Component<LightboxTourBtnProps> {
    render() {
        return this.props.navBtn(
            '50%',
            0,
            0,
            'chevron-down',
            () => (LightboxView.LightboxDoc /*&& this.props.future()?.length*/ ? '' : 'none'),
            e => {
                e.stopPropagation();
                this.props.stepInto();
            },
            ''
        );
    }
}
