import { action, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import ReactLoading from 'react-loading';
import { Doc } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { StrCast } from '../../../fields/Types';
import { Networking } from '../../Network';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { FieldView, FieldViewProps } from './FieldView';
import './LoadingBox.scss';

/**
 * LoadingBox Class represents a placeholder doc for documents that are currently
 * being uploaded to the server and being fetched by the client. The LoadingBox doc is then used to
 * generate the actual type of doc that is required once the document has been successfully uploaded.
 *
 * Design considerations:
 * We are using the docToFiles map in Documents to keep track of all files being uploaded in one session of the client.
 * If the file is not found we assume an error has occurred with the file upload, e.g. it has been interrupted by a client refresh
 * or network issues. The docToFiles essentially gets reset everytime the page is refreshed.
 *
 * TODOs:
 *  1) ability to query server to retrieve files that already exist if users upload duplicate files.
 *  2) ability to restart upload if there is an error
 *  3) detect network error and notify the user
 *  4 )if file upload gets interrupted, it still gets uploaded to the server if there are no network interruptions which leads to unused space. this could be
 *      handled with (1)
 *  5) Fixing the stacking view bug
 *  6) Fixing the CSS
 *
 * @author naafiyan
 */
@observer
export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(LoadingBox, fieldKey);
    }
    @observable public static CurrentlyLoading: Doc[] = []; // this assignment doesn't work. the actual assignment happens in DocumentManager's constructor
    // removes from currently loading display
    @action
    public static removeCurrentlyLoading(doc: Doc) {
        if (LoadingBox.CurrentlyLoading) {
            const index = LoadingBox.CurrentlyLoading.indexOf(doc);
            index !== -1 && LoadingBox.CurrentlyLoading.splice(index, 1);
        }
    }

    // adds doc to currently loading display
    @action
    public static addCurrentlyLoading(doc: Doc) {
        if (LoadingBox.CurrentlyLoading.indexOf(doc) === -1) {
            LoadingBox.CurrentlyLoading.push(doc);
        }
    }

    _timer: any;
    @observable progress = '';
    componentDidMount() {
        if (!LoadingBox.CurrentlyLoading?.includes(this.Document)) {
            this.Document.loadingError = 'Upload interrupted, please try again';
        } else {
            const updateFunc = async () => {
                const result = await Networking.QueryYoutubeProgress(StrCast(this.Document[Id])); // We use the guid of the overwriteDoc to track file uploads.
                runInAction(() => (this.progress = result.progress));
                !this.Document.loadingError && (this._timer = setTimeout(updateFunc, 1000));
            };
            this._timer = setTimeout(updateFunc, 1000);
        }
    }
    componentWillUnmount() {
        clearTimeout(this._timer);
    }

    render() {
        return (
            <div className="loadingBoxContainer" style={{ background: !this.Document.loadingError ? '' : 'red' }}>
                <div className="loadingBox-textContainer">
                    <span className="loadingBox-title">{StrCast(this.Document.title)}</span>
                    <p className="loadingBox-headerText">{StrCast(this.Document.loadingError, 'Loading ' + (this.progress.replace('[download]', '') || '(can take several minutes)'))}</p>
                    {this.Document.loadingError ? null : (
                        <div className="loadingBox-spinner">
                            <ReactLoading type="spinningBubbles" color="blue" height={100} width={100} />
                        </div>
                    )}
                </div>
            </div>
        );
    }
}
