"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_STYLE = exports.clearCanvas = void 0;
const element_1 = require("../../../../../client/element");
const parseRelativeUnit_1 = require("../../../../../client/parseRelativeUnit");
const style_1 = require("../../../../../client/style");
const theme_1 = require("../../../../../client/theme");
const replaceChild_1 = require("../../../../../client/util/replaceChild");
const userSelect_1 = require("../../../../../client/util/style/userSelect");
const APINotImplementedError_1 = require("../../../../../exception/APINotImplementedError");
const isRelative_1 = require("../../../../../isRelative");
const NOOP_1 = require("../../../../../NOOP");
const draw_1 = require("./draw");
function clearCanvas(context) {
    const transform = context.getTransform();
    context.setTransform(1, 0, 0, 1, 0, 0);
    const width = context.canvas.width;
    const height = context.canvas.height;
    context.clearRect(0, 0, width, height);
    context.setTransform(transform);
    context.beginPath();
}
exports.clearCanvas = clearCanvas;
exports.DEFAULT_STYLE = Object.assign({ background: 'none', touchAction: 'none', display: 'block', imageResizing: 'pixelated', '-webkit-touch-callout': 'none' }, (0, userSelect_1.userSelect)('none'));
class CanvasComp extends element_1.Element {
    constructor($props, $system) {
        super($props, $system);
        this._d = [];
        this._reset = () => {
            const { className, style } = this.$props;
            const old_canvas_el = this._canvas_el;
            const canvas_el = this._create_canvas();
            if (className !== undefined) {
                canvas_el.className = className;
            }
            (0, style_1.applyStyle)(this._canvas_el, Object.assign(Object.assign({}, exports.DEFAULT_STYLE), style));
            canvas_el.draggable = false;
            this._canvas_el = canvas_el;
            const context = canvas_el.getContext('2d', { willReadFrequently: true });
            this._context = context;
            this._context;
            if (old_canvas_el) {
                (0, replaceChild_1.replaceChild)(old_canvas_el, canvas_el);
            }
        };
        this._get_parent_width = () => {
            // TODO
            return 0;
        };
        this._get_parent_height = () => {
            // TODO
            return 0;
        };
        this._get_frame_width = () => {
            // TODO
            return 0;
        };
        this._get_frame_height = () => {
            // TODO
            return 0;
        };
        this._create_canvas = () => {
            // console.log('CanvasComp', '_create_canvas', style)
            const { style, width = 200, height = 200 } = this.$props;
            const canvas_el = this.$system.api.document.createElement('canvas');
            canvas_el.classList.add('canvas');
            canvas_el.width = (0, parseRelativeUnit_1.parseRelativeUnit)(width, this._get_parent_width, this._get_frame_height);
            canvas_el.height = (0, parseRelativeUnit_1.parseRelativeUnit)(height, this._get_parent_height, this._get_frame_height);
            (0, style_1.applyStyle)(canvas_el, Object.assign(Object.assign({}, exports.DEFAULT_STYLE), style));
            this._canvas_el = canvas_el;
            return canvas_el;
        };
        this._setup = () => {
            // this._setup_canvas()
            this._setup_context();
        };
        this._setup_redraw = () => {
            this._setup_context();
            this._redraw();
        };
        this._set_width = (width) => {
            this._canvas_el.setAttribute('width', `${width - 1}`);
            this._setup_redraw();
        };
        this._set_height = (height) => {
            this._canvas_el.setAttribute('height', `${height - 1}`);
            this._setup_redraw();
        };
        this._get_fill_style = () => {
            var _a, _b, _c;
            const { $theme } = (_a = this.$context) !== null && _a !== void 0 ? _a : { $color: theme_1.COLOR_WHITE, $theme: 'dark' };
            const final_style = Object.assign(Object.assign({}, exports.DEFAULT_STYLE), this.$props.style);
            const fallbackColor = (_c = (_b = final_style.strokeStyle) !== null && _b !== void 0 ? _b : final_style.color) !== null && _c !== void 0 ? _c : (0, theme_1.defaultThemeColor)($theme);
            return fallbackColor;
        };
        this._redraw = () => {
            // console.log('Graph', '_redraw', this._d)
            this._draw_steps(this._d);
        };
        this._setup_canvas = () => {
            const { api: { screen: { devicePixelRatio }, }, } = this.$system;
            const { width = 200, height = 200 } = this.$props;
            this._context.setTransform(1, 0, 0, 1, 0, 0);
            const dpr = devicePixelRatio || 1;
            const _width = (0, parseRelativeUnit_1.parseRelativeUnit)(width, this._get_parent_width, this._get_frame_height);
            const _height = (0, parseRelativeUnit_1.parseRelativeUnit)(height, this._get_parent_height, this._get_frame_height);
            this._canvas_el.width = _width * dpr;
            this._canvas_el.height = _height * dpr;
            this._context.scale(1 / dpr, 1 / dpr);
        };
        this._setup_context = () => {
            // console.log('Canvas', '_setup_context')
            const { sx = 1, sy = 1 } = this.$props;
            const color = this._get_fill_style();
            const context = this._canvas_el.getContext('2d');
            this._context = context;
            this._context.strokeStyle = color;
            this._context.fillStyle = color;
            this._context.lineJoin = 'round';
            this._context.lineWidth = 3;
            this._context.scale(sx, sy);
        };
        const {} = $props;
        this._reset();
        this._setup();
        this.$element = this._canvas_el;
    }
    onPropChanged(prop, current) {
        // console.log('Canvas', 'onPropChanged', prop, current)
        if (prop === 'style') {
            const final_style = Object.assign(Object.assign({}, exports.DEFAULT_STYLE), current);
            const color = this._get_fill_style().toLowerCase();
            (0, style_1.applyStyle)(this._canvas_el, final_style);
            if (this._context.fillStyle !== color ||
                this._context.strokeStyle !== color) {
                this._context.fillStyle = color;
                this._context.strokeStyle = color;
                clearCanvas(this._context);
                this._redraw();
            }
        }
        else if (prop === 'width') {
            this._unlisten_frame_width();
            if (typeof current === 'string') {
                if ((0, isRelative_1.isRelativeValue)(current)) {
                    this._width_frame_unlisten = (0, style_1.reactToFrameSize)(current, this, this._set_width);
                }
                else {
                    const width = Number.parseInt(current);
                    if (Number.isNaN(width)) {
                        //
                    }
                    else {
                        this._set_width(width);
                    }
                }
            }
            else {
                this._set_width(current);
            }
        }
        else if (prop === 'height') {
            this._unlisten_frame_height();
            if (typeof current === 'string') {
                if ((0, isRelative_1.isRelativeValue)(current)) {
                    this._height_frame_unlisten = (0, style_1.reactToFrameSize)(current, this, this._set_height);
                }
                else {
                    const height = Number.parseInt(current);
                    if (Number.isNaN(height)) {
                        //
                    }
                    else {
                        this._set_height(height);
                    }
                }
            }
            else {
                this._set_height(current);
            }
        }
        else if (prop === 'd') {
            this.clear();
            this._d = current;
            this._draw_steps(current || []);
        }
        else if (prop === 'lineWidth') {
            this._context.lineWidth = current;
        }
        else if (prop === 'sx') {
            const { a, b, c, d, e, f } = this._context.getTransform();
            this._context.setTransform(current !== null && current !== void 0 ? current : 1, b, c, d, e, f);
        }
        else if (prop === 'sy') {
            const { a, b, c, d, e, f } = this._context.getTransform();
            this._context.setTransform(a, b, c, current !== null && current !== void 0 ? current : 1, e, f);
        }
    }
    _unlisten_frame_width() {
        if (this._width_frame_unlisten) {
            this._width_frame_unlisten();
            this._width_frame_unlisten = undefined;
        }
    }
    _unlisten_frame_height() {
        if (this._height_frame_unlisten) {
            this._height_frame_unlisten();
            this._height_frame_unlisten = undefined;
        }
    }
    _draw(step) {
        // console.log('Canvas', '_draw')
        (0, draw_1.draw)(this._context, step);
    }
    _draw_steps(steps) {
        for (const step of steps) {
            this._draw(step);
        }
    }
    onMount() {
        this._setup();
    }
    draw(step) {
        // console.log('draw', step)
        this._d.push(step);
        this._draw(step);
    }
    clear(_) {
        clearCanvas(this._context);
        this._d = [];
    }
    drawImage(image, x, y, width, height) {
        if (image instanceof ImageBitmap) {
            this._context.drawImage(image, 0, 0, image.width, image.height, x, y, width, height);
        }
        else if (image instanceof HTMLVideoElement) {
            this._context.drawImage(image, x, y, width, height);
        }
    }
    strokePath(d) {
        // console.log('Canvas', 'strokePath', d)
        this._d.push(['strokePath', d]);
        this._strokePath(d);
    }
    _strokePath(d) {
        // console.log('Canvas', '_strokePath', d)
        this._context.stroke(new Path2D(d));
    }
    fillPath(d, fillRule) {
        // console.log('Canvas', 'fillPath', d)
        this._d.push(['fillPath', d, fillRule]);
        this._fillPath(d, fillRule);
    }
    _fillPath(d, fillRule) {
        // console.log('Canvas', '_fillPath', d)
        this._context.fill(new Path2D(d), fillRule);
    }
    translate(x, y) {
        // console.log('Canvas', 'translate', x, y)
        this._context.translate(x, y);
    }
    scale(sx, sy) {
        // console.log('Canvas', 'scale', sx, sy)
        this._context.scale(sx, sy);
    }
    toBlob({ type, quality }, callback = NOOP_1.NOOP) {
        this._canvas_el.toBlob(callback, type, quality);
    }
    captureStream({ frameRate, }) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this._canvas_el.captureStream) {
                return this._canvas_el.captureStream(frameRate);
            }
            else {
                throw new APINotImplementedError_1.APINotSupportedError('Capture Stream');
            }
        });
    }
    putImageData(image, dx, dy, x, y, width, height) {
        this._context.putImageData(image, dx, dy, x, y, width, height);
    }
}
exports.default = CanvasComp;
//# sourceMappingURL=Component.js.map