import React = require('react');
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { extname } from 'path';
import DatePicker from 'react-datepicker';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field } from '../../../../fields/Doc';
import { Cast, DateCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils';
import { Transform } from '../../../util/Transform';
import { EditableView } from '../../EditableView';
import { Colors } from '../../global/globalEnums';
import { FieldView, FieldViewProps } from '../../nodes/FieldView';
import { KeyValueBox } from '../../nodes/KeyValueBox';
import { DefaultStyleProvider } from '../../StyleProvider';
import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
import { FInfo } from '../../../documents/Documents';

export interface SchemaTableCellProps {
    Document: Doc;
    col: number;
    deselectCell: () => void;
    selectCell: (doc: Doc, col: number) => void;
    selectedCell: () => [Doc, number] | undefined;
    fieldKey: string;
    columnWidth: number;
    isRowActive: () => boolean | undefined;
    getFinfo: (fieldKey: string) => FInfo | undefined;
    setColumnValues: (field: string, value: string) => boolean;
}

@observer
export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
    private _editorRef: EditableView | null = null;

    @computed get readOnly() {
        return this.props.getFinfo(this.props.fieldKey)?.readOnly ?? false;
    }

    @computed get selected() {
        const selected: [Doc, number] | undefined = this.props.selectedCell();
        return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col;
    }

    componentDidUpdate() {
        if (!this.selected) {
            this._editorRef?.setIsFocused(false);
            document.removeEventListener('keydown', this.onKeyDown);
        } else if (!this.readOnly) {
            document.addEventListener('keydown', this.onKeyDown);
        }
    }

    @action
    onKeyDown = (e: KeyboardEvent) => {
        e.stopPropagation();
        if (e.key == 'Enter') {
            this._editorRef?.setIsFocused(true);
        }
        if (e.key == 'Escape') {
            this.props.deselectCell();
        }
    };
    colWidthFunc = () => this.props.columnWidth;
    colRowHeightFunc = () => CollectionSchemaView._rowHeight;
    @computed get defaultCellContent() {
        const props: FieldViewProps = {
            Document: this.props.Document,
            docFilters: returnEmptyFilter,
            docRangeFilters: returnEmptyFilter,
            searchFilterDocs: returnEmptyDoclist,
            styleProvider: DefaultStyleProvider,
            docViewPath: returnEmptyDoclist,
            fieldKey: this.props.fieldKey,
            rootSelected: returnFalse,
            isSelected: returnFalse,
            setHeight: returnFalse,
            select: emptyFunction,
            dropAction: 'alias',
            bringToFront: emptyFunction,
            renderDepth: 1,
            isContentActive: returnFalse,
            whenChildContentsActiveChanged: emptyFunction,
            ScreenToLocalTransform: Transform.Identity,
            focus: emptyFunction,
            PanelWidth: this.colWidthFunc,
            PanelHeight: this.colRowHeightFunc,
            addDocTab: returnFalse,
            pinToPres: returnZero,
        };
        let protoCount = 0;
        let doc: Doc | undefined = this.props.Document;
        while (doc) {
            if (Object.keys(doc).includes(this.props.fieldKey)) {
                break;
            }
            protoCount++;
            doc = doc.proto;
        }
        const parenCount = Math.max(0, protoCount - 1);
        const color = protoCount === 0 ? 'black' : 'blue';

        return (
            <div
                className="schemacell-edit-wrapper"
                style={{
                    color,
                    textDecoration: parenCount ? 'underline' : undefined,
                    cursor: !this.readOnly ? 'text' : 'default',
                    pointerEvents: !this.readOnly && this.props.isRowActive() ? 'all' : 'none',
                }}>
                <EditableView
                    ref={ref => (this._editorRef = ref)}
                    contents={<FieldView {...props} />}
                    GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
                    SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => {
                        if (shiftDown && enterKey) {
                            this.props.setColumnValues(this.props.fieldKey, value);
                        }
                        return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value);
                    }}
                    editing={this.selected ? undefined : false}
                />
            </div>
        );
    }

    get getCellType() {
        const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType;
        if (columnTypeStr) {
            if (columnTypeStr in FInfotoColType) {
                return FInfotoColType[columnTypeStr];
            }

            return ColumnType.Any;
        }

        const cellValue = this.props.Document[this.props.fieldKey];
        if (cellValue instanceof ImageField) return ColumnType.Image;
        if (cellValue instanceof DateField) return ColumnType.Date;

        return ColumnType.Any;
    }

    get content() {
        const cellType: ColumnType = this.getCellType;
        switch (cellType) {
            case ColumnType.Image:
                return <SchemaImageCell {...this.props} />;
            case ColumnType.Date:
            // return <SchemaDateCell {...this.props} />;
            default:
                return this.defaultCellContent;
        }
    }

    render() {
        return (
            <div
                className="schema-table-cell"
                onPointerDown={action(e => {
                    if (!this.selected) this.props.selectCell(this.props.Document, this.props.col);
                })}
                style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
                {this.content}
            </div>
        );
    }
}

// mj: most of this is adapted from old schema code so I'm not sure what it does tbh
@observer
export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
    @observable _previewRef: HTMLImageElement | undefined;

    choosePath(url: URL) {
        if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
        if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question

        const ext = extname(url.href);
        return url.href.replace(ext, '_s' + ext);
    }

    get url() {
        const field = Cast(this.props.Document[this.props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
        const alts = DocListCast(this.props.Document[this.props.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
        const altpaths = alts
            .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
            .filter(url => url)
            .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
        const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
        // If there is a path, follow it; otherwise, follow a link to a default image icon
        const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
        return url[0];
    }

    @action
    showHoverPreview = (e: React.PointerEvent) => {
        this._previewRef = document.createElement('img');
        document.body.appendChild(this._previewRef);
        const ext = extname(this.url);
        this._previewRef.src = this.url.replace('_s' + ext, '_m' + ext);
        this._previewRef.style.position = 'absolute';
        this._previewRef.style.left = e.clientX + 10 + 'px';
        this._previewRef.style.top = e.clientY + 10 + 'px';
        this._previewRef.style.zIndex = '1000';
    };

    @action
    moveHoverPreview = (e: React.PointerEvent) => {
        if (!this._previewRef) return;
        this._previewRef.style.left = e.clientX + 10 + 'px';
        this._previewRef.style.top = e.clientY + 10 + 'px';
    };

    @action
    removeHoverPreview = (e: React.PointerEvent) => {
        if (!this._previewRef) return;
        document.body.removeChild(this._previewRef);
    };

    render() {
        const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio
        // let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px
        // const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px
        const height = CollectionSchemaView._rowHeight - 10;
        const width = height * aspect; // increase the width of the image if necessary to maintain proportionality

        return <img src={this.url} width={width} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
    }
}

@observer
export class SchemaDateCell extends React.Component<SchemaTableCellProps> {
    @observable _pickingDate: boolean = false;

    @computed get date(): DateField {
        // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
        return DateCast(this.props.Document[this.props.fieldKey]);
    }

    @action
    handleChange = (date: any) => {
        // const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } });
        // if (script.compiled) {
        //     this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
        // } else {
        // ^ DateCast is always undefined for some reason, but that is what the field should be set to
        this.props.Document[this.props.fieldKey] = new DateField(date as Date);
        //}
    };

    render() {
        return <DatePicker dateFormat={'Pp'} selected={this.date.date} onChange={(date: any) => this.handleChange(date)} />;
    }
}
