"use strict";
/**
 * Copyright (c) 2020 The xterm.js authors. All rights reserved.
 * @license MIT
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ImageAddon = void 0;
const IIPHandler_1 = require("./IIPHandler");
const ImageRenderer_1 = require("./ImageRenderer");
const ImageStorage_1 = require("./ImageStorage");
const SixelHandler_1 = require("./SixelHandler");
// default values of addon ctor options
const DEFAULT_OPTIONS = {
    enableSizeReports: true,
    pixelLimit: 16777216, // limit to 4096 * 4096 pixels
    sixelSupport: true,
    sixelScrolling: true,
    sixelPaletteLimit: 256,
    sixelSizeLimit: 25000000,
    storageLimit: 128,
    showPlaceholder: true,
    iipSupport: true,
    iipSizeLimit: 20000000
};
// max palette size supported by the sixel lib (compile time setting)
const MAX_SIXEL_PALETTE_SIZE = 4096;
class ImageAddon {
    constructor(opts) {
        this._disposables = [];
        this._handlers = new Map();
        this._opts = Object.assign({}, DEFAULT_OPTIONS, opts);
        this._defaultOpts = Object.assign({}, DEFAULT_OPTIONS, opts);
    }
    dispose() {
        for (const obj of this._disposables) {
            obj.dispose();
        }
        this._disposables.length = 0;
        this._handlers.clear();
    }
    _disposeLater(...args) {
        for (const obj of args) {
            this._disposables.push(obj);
        }
    }
    activate(terminal) {
        this._terminal = terminal;
        // internal data structures
        this._renderer = new ImageRenderer_1.ImageRenderer(terminal);
        this._storage = new ImageStorage_1.ImageStorage(terminal, this._renderer, this._opts);
        // enable size reports
        if (this._opts.enableSizeReports) {
            // const windowOptions = terminal.getOption('windowOptions');
            // windowOptions.getWinSizePixels = true;
            // windowOptions.getCellSizePixels = true;
            // windowOptions.getWinSizeChars = true;
            // terminal.setOption('windowOptions', windowOptions);
            const windowOps = terminal.options.windowOptions || {};
            windowOps.getWinSizePixels = true;
            windowOps.getCellSizePixels = true;
            windowOps.getWinSizeChars = true;
            terminal.options.windowOptions = windowOps;
        }
        this._disposeLater(this._renderer, this._storage, 
        // DECSET/DECRST/DA1/XTSMGRAPHICS handlers
        terminal.parser.registerCsiHandler({ prefix: '?', final: 'h' }, params => this._decset(params)), terminal.parser.registerCsiHandler({ prefix: '?', final: 'l' }, params => this._decrst(params)), terminal.parser.registerCsiHandler({ final: 'c' }, params => this._da1(params)), terminal.parser.registerCsiHandler({ prefix: '?', final: 'S' }, params => this._xtermGraphicsAttributes(params)), 
        // render hook
        terminal.onRender(range => { var _a; return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.render(range); }), 
        /**
         * reset handlers covered:
         * - DECSTR
         * - RIS
         * - Terminal.reset()
         */
        terminal.parser.registerCsiHandler({ intermediates: '!', final: 'p' }, () => this.reset()), terminal.parser.registerEscHandler({ final: 'c' }, () => this.reset()), terminal._core._inputHandler.onRequestReset(() => this.reset()), 
        // wipe canvas and delete alternate images on buffer switch
        terminal.buffer.onBufferChange(() => { var _a; return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.wipeAlternate(); }), 
        // extend images to the right on resize
        terminal.onResize(metrics => { var _a; return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.viewportResize(metrics); }));
        // SIXEL handler
        if (this._opts.sixelSupport) {
            const sixelHandler = new SixelHandler_1.SixelHandler(this._opts, this._storage, terminal);
            this._handlers.set('sixel', sixelHandler);
            this._disposeLater(terminal._core._inputHandler._parser.registerDcsHandler({ final: 'q' }, sixelHandler));
        }
        // iTerm IIP handler
        if (this._opts.iipSupport) {
            const iipHandler = new IIPHandler_1.IIPHandler(this._opts, this._renderer, this._storage, terminal);
            this._handlers.set('iip', iipHandler);
            this._disposeLater(terminal._core._inputHandler._parser.registerOscHandler(1337, iipHandler));
        }
    }
    // Note: storageLimit is skipped here to not intoduce a surprising side effect.
    reset() {
        var _a;
        // reset options customizable by sequences to defaults
        this._opts.sixelScrolling = this._defaultOpts.sixelScrolling;
        this._opts.sixelPaletteLimit = this._defaultOpts.sixelPaletteLimit;
        // also clear image storage
        (_a = this._storage) === null || _a === void 0 ? void 0 : _a.reset();
        // reset protocol handlers
        for (const handler of this._handlers.values()) {
            handler.reset();
        }
        return false;
    }
    get storageLimit() {
        var _a;
        return ((_a = this._storage) === null || _a === void 0 ? void 0 : _a.getLimit()) || -1;
    }
    set storageLimit(limit) {
        var _a;
        (_a = this._storage) === null || _a === void 0 ? void 0 : _a.setLimit(limit);
        this._opts.storageLimit = limit;
    }
    get storageUsage() {
        if (this._storage) {
            return this._storage.getUsage();
        }
        return -1;
    }
    get showPlaceholder() {
        return this._opts.showPlaceholder;
    }
    set showPlaceholder(value) {
        var _a;
        this._opts.showPlaceholder = value;
        (_a = this._renderer) === null || _a === void 0 ? void 0 : _a.showPlaceholder(value);
    }
    getImageAtBufferCell(x, y) {
        var _a;
        return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.getImageAtBufferCell(x, y);
    }
    extractTileAtBufferCell(x, y) {
        var _a;
        return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.extractTileAtBufferCell(x, y);
    }
    _report(s) {
        var _a;
        (_a = this._terminal) === null || _a === void 0 ? void 0 : _a._core.coreService.triggerDataEvent(s);
    }
    _decset(params) {
        for (let i = 0; i < params.length; ++i) {
            switch (params[i]) {
                case 80:
                    this._opts.sixelScrolling = false;
                    break;
            }
        }
        return false;
    }
    _decrst(params) {
        for (let i = 0; i < params.length; ++i) {
            switch (params[i]) {
                case 80:
                    this._opts.sixelScrolling = true;
                    break;
            }
        }
        return false;
    }
    // overload DA to return something more appropriate
    _da1(params) {
        if (params[0]) {
            return true;
        }
        // reported features:
        // 62 - VT220
        // 4 - SIXEL support
        // 9 - charsets
        // 22 - ANSI colors
        if (this._opts.sixelSupport) {
            this._report(`\x1b[?62;4;9;22c`);
            return true;
        }
        return false;
    }
    /**
     * Implementation of xterm's graphics attribute sequence.
     *
     * Supported features:
     * - read/change palette limits (max 4096 by sixel lib)
     * - read SIXEL canvas geometry (reports current window canvas or
     *   squared pixelLimit if canvas > pixel limit)
     *
     * Everything else is deactivated.
     */
    _xtermGraphicsAttributes(params) {
        var _a, _b, _c, _d, _e, _f;
        if (params.length < 2) {
            return true;
        }
        if (params[0] === 1 /* GaItem.COLORS */) {
            switch (params[1]) {
                case 1 /* GaAction.READ */:
                    this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${this._opts.sixelPaletteLimit}S`);
                    return true;
                case 2 /* GaAction.SET_DEFAULT */:
                    this._opts.sixelPaletteLimit = this._defaultOpts.sixelPaletteLimit;
                    this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${this._opts.sixelPaletteLimit}S`);
                    // also reset protocol handlers for now
                    for (const handler of this._handlers.values()) {
                        handler.reset();
                    }
                    return true;
                case 3 /* GaAction.SET */:
                    if (params.length > 2 && !(params[2] instanceof Array) && params[2] <= MAX_SIXEL_PALETTE_SIZE) {
                        this._opts.sixelPaletteLimit = params[2];
                        this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${this._opts.sixelPaletteLimit}S`);
                    }
                    else {
                        this._report(`\x1b[?${params[0]};${2 /* GaStatus.ACTION_ERROR */}S`);
                    }
                    return true;
                case 4 /* GaAction.READ_MAX */:
                    this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${MAX_SIXEL_PALETTE_SIZE}S`);
                    return true;
                default:
                    this._report(`\x1b[?${params[0]};${2 /* GaStatus.ACTION_ERROR */}S`);
                    return true;
            }
        }
        if (params[0] === 2 /* GaItem.SIXEL_GEO */) {
            switch (params[1]) {
                // we only implement read and read_max here
                case 1 /* GaAction.READ */:
                    let width = (_b = (_a = this._renderer) === null || _a === void 0 ? void 0 : _a.dimensions) === null || _b === void 0 ? void 0 : _b.css.canvas.width;
                    let height = (_d = (_c = this._renderer) === null || _c === void 0 ? void 0 : _c.dimensions) === null || _d === void 0 ? void 0 : _d.css.canvas.height;
                    if (!width || !height) {
                        // for some reason we have no working image renderer
                        // --> fallback to default cell size
                        const cellSize = ImageStorage_1.CELL_SIZE_DEFAULT;
                        width = (((_e = this._terminal) === null || _e === void 0 ? void 0 : _e.cols) || 80) * cellSize.width;
                        height = (((_f = this._terminal) === null || _f === void 0 ? void 0 : _f.rows) || 24) * cellSize.height;
                    }
                    if (width * height < this._opts.pixelLimit) {
                        this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${width.toFixed(0)};${height.toFixed(0)}S`);
                    }
                    else {
                        // if we overflow pixelLimit report that squared instead
                        const x = Math.floor(Math.sqrt(this._opts.pixelLimit));
                        this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${x};${x}S`);
                    }
                    return true;
                case 4 /* GaAction.READ_MAX */:
                    // read_max returns pixelLimit as square area
                    const x = Math.floor(Math.sqrt(this._opts.pixelLimit));
                    this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${x};${x}S`);
                    return true;
                default:
                    this._report(`\x1b[?${params[0]};${2 /* GaStatus.ACTION_ERROR */}S`);
                    return true;
            }
        }
        // exit with error on ReGIS or any other requests
        this._report(`\x1b[?${params[0]};${1 /* GaStatus.ITEM_ERROR */}S`);
        return true;
    }
}
exports.ImageAddon = ImageAddon;