import './core.css';
import utils from 'somes';
import { List } from 'somes/event';
import { React } from '../lib';
import { getDefaultId } from '../lib/dialog';
import { Type, CoverType, Window, ActivityNavStatus } from './ctr';
import Gesture from '../lib/gesture';
import * as ReactDom from 'react-dom';
import { DelayCall } from 'somes/delay_call';
import * as fastClick from 'fastclick';
fastClick.attach(document.body);
var _launcher = null;
var ACTIVITY_ANIMATE_TIME = 400;
var CoverName;
(function (CoverName) {
    CoverName["TOP"] = "top";
    CoverName["BOTTOM"] = "bottom";
})(CoverName || (CoverName = {}));
function getCoverName(type) {
    return type == CoverType.TOP ? CoverName.TOP : CoverName.BOTTOM;
}
export const MAX_ACTIVITY_RETAIN_COUNT = 1;
var Target;
(function (Target) {
    Target[Target["ACTIVITY"] = 0] = "ACTIVITY";
    Target[Target["WIDGET"] = 1] = "WIDGET";
    Target[Target["TOP"] = 2] = "TOP";
    Target[Target["BOTTOM"] = 3] = "BOTTOM";
})(Target || (Target = {}));
function _get_full_id(app, id) {
    return app.name + '_' + id;
}
function _get_full_id_by_info(info) {
    return info.appName + '_' + info.id;
}
export default class ApplicationLauncher extends Gesture {
    constructor(props) {
        super(props);
        this._installed = new Map();
        this._apps = new Map();
        this._sys = null;
        this._cur = null;
        this._InstanceIDs = new Map();
        this._activityHistory = new List();
        this._widgetHistory = new List();
        this._topHistory = new List();
        this._bottomHistory = new List();
        this._disableCover = false;
        this._handleContextMenu = (event) => {
        };
        this._beginMove = false;
        this._top_full_open = false;
        this._bottom_full_open = false;
        this._disable_top_gesture = Number(this.props.disableTopGesture) ? true : false;
        this._disable_bottom_gesture = Number(this.props.disableBottomGesture) ? true : false;
        this._top_unload = new DelayCall(() => {
            var item = this._topHistory.first;
            if (item)
                this._unload(item);
        }, 1e3);
        this._bottom_unload = new DelayCall(() => {
            var item = this._bottomHistory.first;
            if (item)
                this._unload(item);
        }, 1e3);
        utils.assert(!_launcher);
        _launcher = this;
    }
    triggerRemove() {
        _launcher = null;
    }
    _genPanel(target) {
        if (target == Target.ACTIVITY) {
            var div = document.createElement('div');
            this.refs.__activity.appendChild(div);
            return [div, this._activityHistory];
        }
        else if (target == Target.WIDGET) {
            var div = document.createElement('div');
            this.refs.__widget.appendChild(div);
            return [div, this._widgetHistory];
        }
        else if (target == Target.TOP) {
            var div = document.createElement('div');
            this.refs.__top.appendChild(div);
            return [div, this._topHistory];
        }
        else if (target == Target.BOTTOM) {
            var div = document.createElement('div');
            this.refs.__bottom.appendChild(div);
            return [div, this._bottomHistory];
        }
        else {
            throw new Error('Err');
        }
    }
    get currentApplication() {
        return this._cur ? this._cur.value.app.name : '';
    }
    install(appName, app) {
        utils.assert(!this._installed.has(appName));
        this._installed.set(appName, app);
    }
    uninstall(appName) {
        utils.assert(this._installed.has(appName));
        this._installed.delete(appName);
    }
    async launch(appName, args) {
        var app = this._apps.get(appName);
        var isLoad = false;
        if (!app) {
            var ff = this._installed.get(appName);
            utils.assert(ff, `Application does not exist, ${appName}`);
            if (!ff)
                return;
            var App = await ff();
            app = new App.default(this);
            if (!this._sys) {
                this._sys = app;
            }
            isLoad = true;
            this._apps.set(appName, app);
        }
        if (isLoad) {
            await app.triggerLoad();
        }
        app.triggerLaunch({ ...args });
    }
    _exitApp(app) {
        if (app === this._sys) {
            this.closeCover(CoverType.TOP, false);
            this.closeCover(CoverType.BOTTOM, false);
            this._sys = null;
        }
        app.triggerUnload();
        this._apps.delete(app.name);
    }
    async _autoClear() {
        var retainActivityCount = 0;
        var retainApps = new Set();
        var item = this._activityHistory.first;
        while (item) {
            var next = item.next;
            var info = item.value;
            if (info.window) {
                retainActivityCount++;
                if (!info.permanent && this._cur !== item) {
                    if (retainActivityCount >= MAX_ACTIVITY_RETAIN_COUNT) {
                        this._unloadInstance(item);
                    }
                }
            }
            item = next;
        }
        if (this._sys) {
            retainApps.add(this._sys.name);
        }
        for (item = this._activityHistory.first; item; item = item.next) {
            var info = item.value;
            retainApps.add(info.appName);
        }
        for (var [, app] of this._apps) {
            if (!retainApps.has(app.name)) {
                let item = this._widgetHistory.first;
                while (item) {
                    let next = item.next;
                    let info = item.value;
                    if (info.window.app === app) {
                        await this._unmakeWidget(item, false);
                        this._unload(item);
                    }
                    item = next;
                }
                for (let item of [this._topHistory.first, this._bottomHistory.first]) {
                    if (item) {
                        let info = item.value;
                        if (info.window.app === app) {
                            if (info.target == Target.TOP)
                                await this._unmakeTop(item, false);
                            else
                                await this._unmakeBottom(item, false);
                        }
                    }
                }
                this._exitApp(app);
            }
        }
    }
    async show(app, window, args, animate = true) {
        if (window.type == Type.ACTIVITY) {
            this.closeCoverAll();
            return await this._makeActivity(await this._load(app, window, Target.ACTIVITY, args), animate);
        }
        else if (window.type == Type.WIDGET) {
            return await this._makeWidget(await this._load(app, window, Target.WIDGET, args), animate);
        }
        else {
            throw new Error('Err');
        }
    }
    async close(app, id, animate = true) {
        var item = this._getInfo(app, id);
        if (item) {
            var info = item.value;
            if (info.target == Target.ACTIVITY) {
                this.closeCoverAll();
                await this._unmakeActivity(item, animate);
                this._unload(item);
            }
            else if (info.target == Target.WIDGET) {
                await this._unmakeWidget(item, animate);
                this._unload(item);
            }
        }
    }
    _getCoverConstructor(type) {
        if (this._disableCover)
            return { app: this._sys, win: null };
        var win = null;
        var app = this._sys;
        if (this._cur) {
            var act = this._cur.value.window;
            if (act) {
                win = type == CoverType.TOP ? act.app.top() : act.app.bottom();
            }
        }
        if (!win) {
            app = this._sys;
            win = type == CoverType.TOP ? app.top() : app.bottom();
        }
        return { app, win };
    }
    async showCover(type = CoverType.TOP, animate = true) {
        var { app, win } = this._getCoverConstructor(type);
        if (!win)
            return;
        if (type == CoverType.TOP) {
            this.closeCover(CoverType.BOTTOM);
            await this._makeTop(await this._load(app, win, Target.TOP), animate);
        }
        else if (type == CoverType.BOTTOM) {
            this.closeCover(CoverType.TOP);
            await this._makeBottom(await this._load(app, win, Target.BOTTOM), animate);
        }
    }
    async closeCover(type = CoverType.TOP, animate = true) {
        if (type == CoverType.TOP) {
            var item = this._topHistory.first;
            if (item) {
                await this._unmakeTop(item, animate);
            }
        }
        else {
            var item = this._bottomHistory.first;
            if (item) {
                await this._unmakeBottom(item, animate);
            }
        }
    }
    closeCoverAll() {
        this.closeCover(CoverType.TOP);
        this.closeCover(CoverType.BOTTOM);
    }
    async _makeActivity({ item }, animate) {
        var currInfo = item.value;
        var prev = this._cur;
        this._cur = item;
        if (prev) {
            if (prev !== this._cur) {
                var prevInfo = prev.value;
                var prevAct = prevInfo.window;
                var currAct = currInfo.window;
                var time = animate ? ACTIVITY_ANIMATE_TIME : 0;
                await Promise.all([
                    currAct.navStatus == ActivityNavStatus.BACKGROUND ?
                        prevAct.intoLeave(time) : prevAct.intoBackground(time),
                    currAct.intoForeground(time),
                ]);
            }
        }
        else {
            await currInfo.window.intoForeground(0);
        }
        var act = currInfo.window;
        if (act.permanent >= 1) {
            currInfo.permanent = true;
        }
        utils.nextTick(() => this._autoClear());
        return currInfo.window;
    }
    async _unmakeActivity(item, animate) {
        if (item === this._cur) {
            var prev = item.prev;
            if (prev) {
                var cur = prev;
                var nextInfo = item.value;
                var currInfo = cur.value;
                if (!currInfo.window) {
                    prev = cur.prev;
                    this._unload(cur);
                    var args = { ...currInfo.args, __navStatus: ActivityNavStatus.BACKGROUND };
                    cur = (await this._load(currInfo.app, currInfo.New, Target.ACTIVITY, args, prev || item)).item;
                    currInfo = cur.value;
                }
                this._cur = cur;
                var currAct = currInfo.window;
                var nextAct = nextInfo.window;
                var time = animate ? ACTIVITY_ANIMATE_TIME : 0;
                await Promise.all([
                    currAct.intoForeground(time),
                    nextAct.intoLeave(time),
                ]);
            }
            else {
                this._cur = null;
                await item.value.window.intoLeave(0);
            }
        }
        utils.nextTick(() => this._autoClear());
    }
    async _makeTop({ item }, animate) {
        var info = item.value;
        if (animate) {
            this._setCoverPosition(CoverType.TOP, 100, '%', 350);
            await utils.sleep(350);
        }
        else {
            this._setCoverPosition(CoverType.TOP, 100, '%', 0);
        }
        return info.window;
    }
    async _makeBottom({ item }, animate) {
        var info = item.value;
        if (animate) {
            this._setCoverPosition(CoverType.BOTTOM, 100, '%', 350);
            await utils.sleep(350);
        }
        else {
            this._setCoverPosition(CoverType.BOTTOM, 100, '%', 0);
        }
        return info.window;
    }
    async _unmakeTop(item, animate) {
        if (animate) {
            this._setCoverPosition(CoverType.TOP, 0, '%', 350);
            await utils.sleep(350);
        }
        else {
            this._setCoverPosition(CoverType.TOP, 0, '%', 0);
        }
    }
    async _unmakeBottom(item, animate) {
        if (animate) {
            this._setCoverPosition(CoverType.BOTTOM, 0, '%', 350);
            await utils.sleep(350);
        }
        else {
            this._setCoverPosition(CoverType.BOTTOM, 0, '%', 0);
        }
    }
    async _makeWidget({ item, load }, animate) {
        var info = item.value;
        if (!load)
            return info.window;
        info.window.triggerResume();
        if (animate) {
            info.panel.style.transitionDuration = '0';
            info.panel.style.opacity = '0';
            await utils.sleep(50);
            info.panel.style.transitionDuration = `200ms`;
            info.panel.style.opacity = '1';
        }
        else {
            info.panel.style.transitionDuration = '0';
            info.panel.style.opacity = '1';
        }
        return info.window;
    }
    async _unmakeWidget(item, animate) {
        var info = item.value;
        info.window.triggerPause();
        if (animate) {
            info.panel.style.transitionDuration = '0';
            info.panel.style.opacity = '1';
            await utils.sleep(50);
            info.panel.style.transitionDuration = `200ms`;
            info.panel.style.opacity = '0';
            await utils.sleep(200);
        }
        else {
            info.panel.style.transitionDuration = '0';
            info.panel.style.opacity = '0';
        }
    }
    async _load(app, window, target, args, prev) {
        utils.equalsClass(Window, window);
        var id = (args === null || args === void 0 ? void 0 : args.id) ? String(args.id) : getDefaultId(window);
        var fid = _get_full_id(app, id);
        var item = this._InstanceIDs.get(fid), load = false;
        if (!item) {
            var [panel, stack] = this._genPanel(target);
            var New = window;
            var win = await new Promise(r => ReactDom.render(React.createElement(New, Object.assign({}, args, { __app__: app, id: id })), panel, function () { r(this); }));
            var info = {
                window: win, panel, id, target, time: Date.now(),
                permanent: false, args, appName: app.name, New, app,
            };
            if (prev) {
                item = stack.insert(prev, info);
            }
            else {
                item = stack.push(info);
            }
            this._InstanceIDs.set(fid, item);
            load = true;
        }
        else {
        }
        return { item, load };
    }
    _unloadInstance(item) {
        var info = item.value;
        if (info.window) {
            var panel = info.panel;
            ReactDom.unmountComponentAtNode(panel);
            panel.parentElement.removeChild(panel);
            info.window = undefined;
            info.panel = undefined;
            this._InstanceIDs.delete(_get_full_id_by_info(info));
        }
    }
    _unload(item) {
        if (!item.host)
            return;
        this._unloadInstance(item);
        item.host.del(item);
    }
    _getInfo(app, id) {
        utils.assert(app);
        var _id = typeof id == 'string' ? String(id) : getDefaultId(id);
        var fid = _get_full_id(app, _id);
        var item = this._InstanceIDs.get(fid);
        return item || null;
    }
    getWindow(app, id) {
        var _a, _b;
        return ((_b = (_a = this._getInfo(app, id)) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.window) || null;
    }
    render() {
        return (React.createElement("div", { className: "iso_sys", ref: "__root", onContextMenu: this._handleContextMenu },
            React.createElement("div", { className: "iso_activitys", ref: "__activity" }),
            React.createElement("div", { className: "iso_widgets", ref: "__widget" }),
            React.createElement("div", { className: "iso_covers", style: { top: '-100%' }, ref: "__top" }),
            React.createElement("div", { className: "iso_covers", style: { top: '100%' }, ref: "__bottom" })));
    }
    get $el() {
        return this.refs.__root;
    }
    _setCoverFullOpen(name, value) {
        var self = this;
        if (self[`_${name}_full_open`] != value) {
            self[`_${name}_full_open`] = value;
            var item = self[`_${name}History`].first;
            if (item) {
                if (value) {
                    item.value.window.triggerResume();
                }
                else {
                    item.value.window.triggerPause();
                }
            }
        }
    }
    _setCoverPosition(type, value, unit, duration) {
        var self = this;
        var __top = self.refs.__top;
        var __bottom = self.refs.__bottom;
        var name = getCoverName(type);
        if (type == CoverType.TOP) {
            __top.style.transform = `translateY(${value}${unit}) translateZ(1px)`;
            __top.style.transitionDuration = `${duration}ms`;
        }
        else {
            __bottom.style.transform = `translateY(${-value}${unit}) translateZ(1px)`;
            __bottom.style.transitionDuration = `${duration}ms`;
        }
        if (value > 0) {
            if (value == 100 && unit == '%')
                this._setCoverFullOpen(name, true);
            if (type == CoverType.TOP) {
                self[`_top_unload`].clear();
            }
            else {
                self[`_bottom_unload`].clear();
            }
        }
        else {
            this._setCoverFullOpen(name, false);
            if (type == CoverType.TOP) {
                self[`_top_unload`].call();
            }
            else {
                self[`_bottom_unload`].call();
            }
        }
    }
    _setCoverPositionNoAnimate(type, value) {
        if (type == CoverType.TOP) {
            var { app, win } = this._getCoverConstructor(type);
            if (!win)
                return;
            if (this._topHistory.length === 0) {
                this._load(app, win, Target.TOP);
            }
        }
        else {
            var { app, win } = this._getCoverConstructor(type);
            if (!win)
                return;
            if (this._bottomHistory.length === 0) {
                this._load(app, win, Target.BOTTOM);
            }
        }
        this._setCoverPosition(type, value, 'px', 0);
    }
    triggerBeginMove(e) {
        if (this._cur) {
            var info = this._cur.value;
            var act = info.window;
            if (act.preventCover) {
                return;
            }
        }
        this._beginMove = true;
    }
    triggerMove(e) {
        if (!this._beginMove) {
            return;
        }
        if (e.begin_direction == 2) {
            if (this._top_full_open) {
                if (!this._disable_top_gesture)
                    this._setCoverPositionNoAnimate(CoverType.TOP, this.clientHeight - Math.max(e.begin_y - e.y, 0));
            }
            else if (!this._bottom_full_open) {
                if (!this._disable_bottom_gesture)
                    this._setCoverPositionNoAnimate(CoverType.BOTTOM, e.begin_y - e.y);
            }
        }
        else if (e.begin_direction == 4) {
            if (this._bottom_full_open) {
                if (!this._disable_bottom_gesture)
                    this._setCoverPositionNoAnimate(CoverType.BOTTOM, this.clientHeight - Math.max(e.y - e.begin_y, 0));
            }
            else if (!this._top_full_open) {
                if (!this._disable_top_gesture) {
                    this._setCoverPositionNoAnimate(CoverType.TOP, e.y - e.begin_y);
                }
            }
        }
    }
    triggerEndMove(e) {
        if (!this._beginMove) {
            return;
        }
        this._beginMove = false;
        if (e.begin_direction == 2 || e.begin_direction == 4) {
            var ok = e.speed > 200 && e.begin_direction == e.instant_direction;
            if (e.begin_direction == 2) {
                if (this._top_full_open) {
                    if (!this._disable_top_gesture) {
                        if (ok) {
                            this.closeCover(CoverType.TOP);
                        }
                        else {
                            this.showCover(CoverType.TOP);
                        }
                    }
                }
                else if (!this._bottom_full_open) {
                    if (!this._disable_bottom_gesture) {
                        if (ok) {
                            this.showCover(CoverType.BOTTOM);
                        }
                        else {
                            this.closeCover(CoverType.BOTTOM);
                        }
                    }
                }
            }
            else {
                if (this._bottom_full_open) {
                    if (!this._disable_bottom_gesture) {
                        if (ok) {
                            this.closeCover(CoverType.BOTTOM);
                        }
                        else {
                            this.showCover(CoverType.BOTTOM);
                        }
                    }
                }
                else if (!this._top_full_open) {
                    if (!this._disable_top_gesture) {
                        if (ok) {
                            this.showCover(CoverType.TOP);
                        }
                        else {
                            this.closeCover(CoverType.TOP);
                        }
                    }
                }
            }
        }
    }
    static get current() {
        utils.assert(_launcher, 'No the ApplicationLauncher instance');
        return _launcher;
    }
    static launch(appName, args) {
        return ApplicationLauncher.current.launch(appName, args);
    }
}
