var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { Part } from '../../part.js';
import { part, property, component } from '../../decorators.js';
import { subscribeDOM, EventEmitter } from '@kano/common/index.js';
import { PartComponent } from '../../component.js';
import { Sticker } from '../sticker/types.js';
import { transformLegacyMouse } from './legacy.js';
class MouseComponent extends PartComponent {
    constructor() {
        super(...arguments);
        this.down = new EventEmitter();
        this.up = new EventEmitter();
        this.move = new EventEmitter();
        this.cursor = new Sticker(null);
    }
}
__decorate([
    property({ type: EventEmitter, value: new EventEmitter(), noReset: true })
], MouseComponent.prototype, "down", void 0);
__decorate([
    property({ type: EventEmitter, value: new EventEmitter(), noReset: true })
], MouseComponent.prototype, "up", void 0);
__decorate([
    property({ type: EventEmitter, value: new EventEmitter(), noReset: true })
], MouseComponent.prototype, "move", void 0);
__decorate([
    property({ type: Sticker, value: () => new Sticker(null) })
], MouseComponent.prototype, "cursor", void 0);
let MousePart = class MousePart extends Part {
    constructor() {
        super();
        this._scale = 1;
        this._rect = null;
        this._x = 0;
        this._y = 0;
        this._dx = 0;
        this._dy = 0;
        this._lastMoveEvent = null;
        this._root = null;
        this._imageCache = new Map();
        this.core = this._components.get('core');
        this._stickers = {
            categorisedResource: [],
            categoryEnum: [],
            resourceSet: [],
            getUrl: () => { return ''; },
            getRandom: () => { return ''; },
            getRandomFrom: () => { return ''; },
            cacheValue: () => { return new HTMLCanvasElement(); },
            load: () => { return Promise.resolve(); },
        };
    }
    static transformLegacy(app) {
        transformLegacyMouse(app);
    }
    onInstall(context) {
        // Listen to the resize event ot update the rect and scale
        context.dom.onDidResize(() => {
            this.resize(context);
        });
        subscribeDOM(context.dom.root, 'mousemove', (e) => {
            let { pageX, pageY } = e;
            // In case no resize event is triggered before the mouse part is added
            if (!this._rect) {
                return;
            }
            // Adjust the cursor position by making the coordinates relative to the top left corner of the output
            // Also applies the scale
            const x = Math.max(0, Math.min(context.visuals.width, (pageX - this._rect.left) * this._scale));
            const y = Math.max(0, Math.min(context.visuals.height, (pageY - this._rect.top) * this._scale));
            // Record the current timestamp
            const now = Date.now();
            // When no event is received after 100ms, reset the mouse speed
            if (this._lastMoveEvent === null || now - this._lastMoveEvent > 100) {
                this._dx = 0;
                this._dy = 0;
            }
            else {
                this._dx = x - this._x;
                this._dy = y - this._y;
            }
            this._x = x;
            this._y = y;
            // Update last event time
            this._lastMoveEvent = now;
            this.core.move.fire();
        }, this, this.subscriptions);
        // On mouse down fire the down event. Also starts listening to the up event.
        // If a up event happens anywhere, fire the up event. This makes sure if the user clicks
        // in the canvas, then move the mouse ou and release it still triggers the up event
        subscribeDOM(context.dom.root, 'mousedown', () => {
            const upSub = subscribeDOM(window, 'mouseup', () => {
                upSub.dispose();
                this.core.up.fire();
            });
            this.core.down.fire();
        }, this, this.subscriptions);
        // Trigger an initial resize to populate the scale and rect
        this.resize(context);
        this.core.onDidInvalidate(() => this.render(), this, this.subscriptions);
        this._root = context.dom.root;
        if (context.resources.get('stickers')) {
            this._stickers = context.resources.get('stickers');
        }
    }
    resize(context) {
        this._rect = context.dom.root.getBoundingClientRect();
        this._scale = context.visuals.width / this._rect.width;
    }
    render() {
        if (!this.core.invalidated) {
            return;
        }
        if (!this._root) {
            return;
        }
        const sticker = this.core.cursor.get();
        if (sticker && this._stickers && this._stickers.getUrl(sticker)) {
            this.loadImage(this._stickers.getUrl(sticker))
                .then((url) => {
                this._root.style.cursor = `url('${url}'), auto`;
            });
        }
        else {
            this._root.style.cursor = '';
        }
        this.core.apply();
    }
    onDown(callback) {
        return this.core.down.event(callback, null, this.userSubscriptions);
    }
    onUp(callback) {
        return this.core.up.event(callback, null, this.userSubscriptions);
    }
    onMove(callback) {
        return this.core.move.event(callback, null, this.userSubscriptions);
    }
    get x() {
        return this._x;
    }
    get y() {
        return this._y;
    }
    get dx() {
        return this._dx;
    }
    get dy() {
        return this._dy;
    }
    set cursor(c) {
        this.core.cursor.set(c);
        this.core.invalidate();
    }
    random(id) {
        if (!this._stickers) {
            return '';
        }
        if (id === 'all') {
            return this._stickers.getRandom();
        }
        return this._stickers.getRandomFrom(id);
    }
    loadImage(url) {
        if (this._imageCache.has(url)) {
            return Promise.resolve(this._imageCache.get(url));
        }
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.crossOrigin = 'Anonymous';
            img.onload = () => {
                let canvas = document.createElement('canvas'), ctx;
                canvas.width = 30;
                canvas.height = 30;
                ctx = canvas.getContext('2d');
                ctx.drawImage(img, 0, 0, 30, 30);
                this._imageCache.set(url, canvas.toDataURL());
                resolve(this._imageCache.get(url));
            };
            img.onerror = reject;
            img.src = url;
        });
    }
};
__decorate([
    component(MouseComponent)
], MousePart.prototype, "core", void 0);
MousePart = __decorate([
    part('mouse')
], MousePart);
export { MousePart };
//# sourceMappingURL=mouse.js.map