"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
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 = __importDefault(require("../../../../../client/html"));
const parseRelativeUnit_1 = require("../../../../../client/parseRelativeUnit");
const style_1 = require("../../../../../client/style");
const theme_1 = require("../../../../../client/theme");
const APINotImplementedError_1 = require("../../../../../exception/APINotImplementedError");
const isRelative_1 = require("../../../../../isRelative");
function clearCanvas(context) {
    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.beginPath();
}
class Canvas_ extends html_1.default {
    constructor($props, $system) {
        super($props, $system, $system.api.document.createElement('canvas'), $system.style['canvas'], {
            draggable: false,
        }, {
            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._ctx.fillStyle !== color ||
                    this._ctx.strokeStyle !== color) {
                    this._ctx.fillStyle = color;
                    this._ctx.strokeStyle = color;
                    clearCanvas(this._ctx);
                    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) => {
                void this.clear();
                this._d = d || [];
                this._draw_steps(this._d);
            },
            lineWidth: (lineWidth = 3) => {
                this._ctx.lineWidth = lineWidth;
            },
            x: (x) => {
                const { a, b, c, d, e, f } = this._ctx.getTransform();
                this._ctx.setTransform(a, b, c, d, x ?? 0, f);
            },
            y: (y) => {
                const { a, b, c, d, e, f } = this._ctx.getTransform();
                this._ctx.setTransform(a, b, c, d, e, y ?? 0);
            },
            sx: (sx) => {
                const { a, b, c, d, e, f } = this._ctx.getTransform();
                this._ctx.setTransform(sx ?? 1, b, c, d, e, f);
            },
            sy: (sy) => {
                const { a, b, c, d, e, f } = this._ctx.getTransform();
                this._ctx.setTransform(a, b, c, sy ?? 1, e, f);
            },
        });
        this._d = [];
        this._setup = () => { };
        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._reset = () => {
            this._reset_context();
        };
        this._reset_redraw = () => {
            this._reset();
            this._redraw();
        };
        this._set_width = (width) => {
            this.$element.setAttribute('width', `${width}`);
            this._reset_redraw();
        };
        this._set_height = (height) => {
            this.$element.setAttribute('height', `${height}`);
            this._reset_redraw();
        };
        this._get_fill_style = () => {
            const { $theme } = this.$context ?? { $color: theme_1.COLOR_WHITE, $theme: 'dark' };
            const final_style = { ...this.$defaultStyle, ...this.$props.style };
            const fallbackColor = final_style.strokeStyle ?? final_style.color ?? (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', '_reset_context')
            const { sx = 1, sy = 1 } = this.$props;
            const color = this._get_fill_style();
            const context = this.$element.getContext('2d');
            this._ctx = context;
            this._ctx.setTransform(1, 0, 0, 1, 0, 0);
            this._ctx.strokeStyle = color;
            this._ctx.fillStyle = color;
            this._ctx.lineJoin = 'round';
            this._ctx.lineCap = 'round';
            this._ctx.lineWidth = 3;
            this._ctx.scale(sx, sy);
        };
        const { width = 200, height = 200, d } = this.$props;
        this.$element.width = (0, parseRelativeUnit_1.parseRelativeUnit)(width, this._get_parent_width(), this._get_frame_width());
        this.$element.height = (0, parseRelativeUnit_1.parseRelativeUnit)(height, this._get_parent_height(), this._get_frame_height());
        const ctx = this.$element.getContext('2d', { willReadFrequently: true });
        this._ctx = ctx;
        this._reset();
        if (d) {
            this._draw_steps(d);
        }
    }
    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._ctx, 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() {
        this._clear();
    }
    _clear() {
        clearCanvas(this._ctx);
        this._d = [];
    }
    async drawImage(image, x, y, width, height) {
        if (image instanceof ImageBitmap) {
            this._ctx.drawImage(image, 0, 0, image.width, image.height, x, y, width, height);
        }
        else {
            this._ctx.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._ctx.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._ctx.fill(new Path2D(d), fillRule);
    }
    translate(x, y) {
        // console.log('Canvas', 'translate', x, y)
        this._ctx.translate(x, y);
    }
    scale(sx, sy) {
        // console.log('Canvas', 'scale', sx, sy)
        this._ctx.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._ctx.getImageData(x, y, width, height, opt);
    }
    async putImageData(image, dx, dy, x, y, width, height) {
        this._ctx.putImageData(image, dx, dy, x, y, width, height);
    }
}
exports.default = Canvas_;
//# sourceMappingURL=Component.js.map