"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DictEncoder = void 0;
const tslib_1 = require("tslib");
const bin_util_1 = require("@ot-builder/bin-util");
const CffInterp = tslib_1.__importStar(require("../interp/ir"));
const EPSILON = 1e-8;
const ZERO = "0".charCodeAt(0);
const DOT = ".".charCodeAt(0);
const Em = "e".charCodeAt(0);
const E = "E".charCodeAt(0);
const MINUS = "-".charCodeAt(0);
const PLUS = "+".charCodeAt(0);
class DictEncoder extends CffInterp.Encoder {
    constructor(frag) {
        super();
        this.frag = frag;
    }
    operand(val) {
        if (Math.abs(val - Math.round(val)) < EPSILON) {
            // Value is integer -- encode as int
            return this.encodeInt(val);
        }
        else {
            return this.encodeReal(val);
        }
    }
    encodeInt(val) {
        if (val >= -107 && val <= 107) {
            return this.frag.uint8(val + 139);
        }
        else if (val >= 108 && val <= 1131) {
            val -= 108;
            return this.frag.uint8((val >> 8) + 247).uint8(val & 0xff);
        }
        else if (val >= -1131 && val <= -108) {
            val = -108 - val;
            return this.frag.uint8((val >> 8) + 251).uint8(val & 0xff);
        }
        else if (val >= -32768 && val < 32768) {
            return this.frag.uint8(28).int16(val);
        }
        else {
            return this.frag.uint8(29).int32(val);
        }
    }
    // Generate a "short" string representation of a CFF real number
    // TODO: use Dragon or Ryu?
    shortToString(val) {
        const direct = val.toString(10);
        const exp = val.toExponential(8);
        if (exp.length < direct.length)
            return exp;
        else
            return direct;
    }
    encodeReal(val) {
        // Value is float
        const nibbles = [];
        const valStr = this.shortToString(val);
        for (let digit = 0; digit < valStr.length;) {
            const cur = valStr.charCodeAt(digit);
            const next = digit < valStr.length - 1 ? valStr.charCodeAt(digit + 1) : 0;
            digit += this.encodeRealDigit(cur, next, nibbles);
        }
        nibbles.push(0x0f);
        if (nibbles.length % 2)
            nibbles.push(0x0f);
        this.frag.uint8(30);
        for (let digit = 0; digit < nibbles.length; digit += 2) {
            this.frag.uint8((nibbles[digit] << 4) | nibbles[digit + 1]);
        }
    }
    encodeRealDigit(cur, next, nibbles) {
        if (cur === DOT) {
            nibbles.push(0x0a);
            return 1;
        }
        else if (cur >= ZERO && cur <= ZERO + 9) {
            nibbles.push(cur - ZERO);
            return 1;
        }
        else if ((cur === Em || cur === E) && next === MINUS) {
            nibbles.push(0x0c);
            return 2;
        }
        else if ((cur === Em || cur === E) && next === PLUS) {
            nibbles.push(0x0b);
            return 2;
        }
        else if (cur === Em || cur === E) {
            nibbles.push(0x0b);
            return 1;
        }
        else if (cur === MINUS) {
            nibbles.push(0x0e);
            return 1;
        }
        return 0;
    }
    operator(opCode, flags) {
        if ((opCode & 0xff00) === 0x0c00) {
            this.frag.uint8(0x0c).uint8(opCode & 0xff);
        }
        else {
            this.frag.uint8(opCode);
        }
        // Mask bits
        if (flags) {
            let maskByte = 0;
            let bits = 0;
            for (const bit of flags) {
                maskByte = (maskByte << 1) | (bit ? 1 : 0);
                bits++;
                if (bits === 8) {
                    this.frag.uint8(maskByte);
                    bits = 0;
                }
            }
            if (bits) {
                maskByte = maskByte << (8 - bits);
                this.frag.uint8(maskByte);
            }
        }
    }
    absPointer(p, op) {
        this.frag.uint8(29);
        this.frag.ptr32(p, bin_util_1.FragPointerEmbedding.Absolute);
        this.operator(op);
    }
    embRelPointer(p, op) {
        this.frag.uint8(29);
        this.frag.ptr32(p, bin_util_1.FragPointerEmbedding.EmbedRelative);
        this.operator(op);
    }
}
exports.DictEncoder = DictEncoder;
//# sourceMappingURL=encoder.js.map