"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.clearCanvas = clearCanvas;
const draw_1 = require("../../../../../client/canvas/draw");
const getSize_1 = require("../../../../../client/getSize");
const html_1 = require("../../../../../client/html");
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 APINotImplementedError_1 = require("../../../../../exception/APINotImplementedError");
const isRelative_1 = require("../../../../../isRelative");
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();
}
class CanvasComp extends html_1.default {
    constructor($props, $system) {
        super($props, $system, $system.api.document.createElement('canvas'), $system.style['canvas']);
        this._d = [];
        this._setup = () => {
            const { className, style } = this.$props;
            const old_canvas_el = this.$element;
            const canvas_el = this._create_canvas();
            if (className !== undefined) {
                canvas_el.className = className;
            }
            (0, style_1.applyStyle)(this.$element, { ...this.$defaultStyle, ...style });
            canvas_el.draggable = false;
            this.$element = canvas_el;
            const context = canvas_el.getContext('2d', { willReadFrequently: true });
            this._context = context;
            if (old_canvas_el) {
                (0, replaceChild_1.replaceChild)(old_canvas_el, canvas_el);
            }
        };
        this._get_parent_width = () => {
            if (this.$slotParent) {
                return (0, getSize_1.getSize)(this.$slotParent.$element).width;
            }
            return 0;
        };
        this._get_parent_height = () => {
            if (this.$slotParent) {
                return (0, getSize_1.getSize)(this.$slotParent.$element).width;
            }
            return 0;
        };
        this._get_frame_width = () => {
            const { $width } = this.$context;
            return $width;
        };
        this._get_frame_height = () => {
            const { $height } = this.$context;
            return $height;
        };
        this._create_canvas = () => {
            // console.log('CanvasComp', '_create_canvas')
            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_width);
            canvas_el.height = (0, parseRelativeUnit_1.parseRelativeUnit)(height, this._get_parent_height, this._get_frame_height);
            (0, style_1.applyStyle)(canvas_el, { ...this.$defaultStyle, ...style });
            this.$element = canvas_el;
            return canvas_el;
        };
        this._reset = () => {
            this._reset_context();
        };
        this._reset_redraw = () => {
            this._reset();
            this._redraw();
        };
        this._set_width = (width) => {
            this.$element.setAttribute('width', `${width - 1}`);
            this._reset_redraw();
        };
        this._set_height = (height) => {
            this.$element.setAttribute('height', `${height - 1}`);
            this._reset_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 = { ...this.$defaultStyle, ...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._reset_context = () => {
            // console.log('Canvas', '_setup_context')
            const { sx = 1, sy = 1 } = this.$props;
            const color = this._get_fill_style();
            const context = this.$element.getContext('2d');
            this._context = context;
            this._context.strokeStyle = color;
            this._context.fillStyle = color;
            this._context.lineJoin = 'round';
            this._context.lineCap = 'round';
            this._context.lineWidth = 3;
            this._context.scale(sx, sy);
        };
        this._setup();
        this._reset();
        this.$propHandler = {
            ...this.$propHandler,
            style: (current) => {
                const final_style = { ...this.$defaultStyle, ...current };
                const color = this._get_fill_style().toLowerCase();
                (0, style_1.applyStyle)(this.$element, final_style);
                if (this._context.fillStyle !== color ||
                    this._context.strokeStyle !== color) {
                    this._context.fillStyle = color;
                    this._context.strokeStyle = color;
                    clearCanvas(this._context);
                    this._redraw();
                }
            },
            width: (width) => {
                this._unlisten_frame_width();
                if (typeof width === 'string') {
                    if ((0, isRelative_1.isRelativeValue)(width)) {
                        this._width_frame_unlisten = (0, style_1.reactToFrameSize)(width, 
                        // @ts-ignore
                        this, this._set_width);
                    }
                    else {
                        width = Number.parseInt(width);
                        if (Number.isNaN(width)) {
                            //
                        }
                        else {
                            this._set_width(width);
                        }
                    }
                }
                else {
                    this._set_width(width);
                }
            },
            height: (height) => {
                this._unlisten_frame_height();
                if (typeof height === 'string') {
                    if ((0, isRelative_1.isRelativeValue)(height)) {
                        this._height_frame_unlisten = (0, style_1.reactToFrameSize)(height, 
                        // @ts-ignore
                        this, this._set_height);
                    }
                    else {
                        height = Number.parseInt(height);
                        if (Number.isNaN(height)) {
                            //
                        }
                        else {
                            this._set_height(height);
                        }
                    }
                }
                else {
                    this._set_height(height);
                }
            },
            d: (d) => {
                this.clear();
                this._draw_steps(d || []);
            },
            lineWidth: (lineWidth = 3) => {
                this._context.lineWidth = lineWidth;
            },
            sx: (sx) => {
                const { a, b, c, d, e, f } = this._context.getTransform();
                this._context.setTransform(sx !== null && sx !== void 0 ? sx : 1, b, c, d, e, f);
            },
            sy: (xy) => {
                const { a, b, c, d, e, f } = this._context.getTransform();
                this._context.setTransform(a, b, c, xy !== null && xy !== void 0 ? xy : 1, e, f);
            },
        };
    }
    reset() {
        super.reset();
        this._reset();
        this.clear();
    }
    _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._reset();
    }
    async draw(step) {
        // console.log('draw', step)
        this._d.push(step);
        this._draw(step);
    }
    async clear(_) {
        clearCanvas(this._context);
        this._d = [];
    }
    async 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 {
            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);
    }
    async toBlob(type, quality) {
        return new Promise((resolve, reject) => {
            this.$element.toBlob((blob) => {
                resolve(blob);
            }, type, quality);
        });
    }
    async toDataUrl(type, quality) {
        return this.$element.toDataURL();
    }
    async captureStream({ frameRate, }) {
        if (this.$element.captureStream) {
            return this.$element.captureStream(frameRate);
        }
        else {
            throw new APINotImplementedError_1.APINotSupportedError('Capture Stream');
        }
    }
    async getImageData(x, y, width, height, opt) {
        return this._context.getImageData(x, y, width, height, opt);
    }
    async 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