"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.C40Encoder = void 0;
var StringBuilder_1 = __importDefault(require("../../util/StringBuilder"));
var HighLevelEncoder_1 = __importDefault(require("./HighLevelEncoder"));
var constants_1 = require("./constants");
var C40Encoder = /** @class */ (function () {
    function C40Encoder() {
    }
    C40Encoder.prototype.getEncodingMode = function () {
        return constants_1.C40_ENCODATION;
    };
    C40Encoder.prototype.encodeMaximal = function (context) {
        var buffer = new StringBuilder_1.default();
        var lastCharSize = 0;
        var backtrackStartPosition = context.pos;
        var backtrackBufferLength = 0;
        while (context.hasMoreCharacters()) {
            var c = context.getCurrentChar();
            context.pos++;
            lastCharSize = this.encodeChar(c, buffer);
            if (buffer.length() % 3 === 0) {
                backtrackStartPosition = context.pos;
                backtrackBufferLength = buffer.length();
            }
        }
        if (backtrackBufferLength !== buffer.length()) {
            var unwritten = Math.floor((buffer.length() / 3) * 2);
            var curCodewordCount = Math.floor(context.getCodewordCount() + unwritten + 1); // +1 for the latch to C40
            context.updateSymbolInfo(curCodewordCount);
            var available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
            var rest = Math.floor(buffer.length() % 3);
            if ((rest === 2 && available !== 2) ||
                (rest === 1 && (lastCharSize > 3 || available !== 1))) {
                // buffer.setLength(backtrackBufferLength);
                context.pos = backtrackStartPosition;
            }
        }
        if (buffer.length() > 0) {
            context.writeCodeword(constants_1.LATCH_TO_C40);
        }
        this.handleEOD(context, buffer);
    };
    C40Encoder.prototype.encode = function (context) {
        // step C
        var buffer = new StringBuilder_1.default();
        while (context.hasMoreCharacters()) {
            var c = context.getCurrentChar();
            context.pos++;
            var lastCharSize = this.encodeChar(c, buffer);
            var unwritten = Math.floor(buffer.length() / 3) * 2;
            var curCodewordCount = context.getCodewordCount() + unwritten;
            context.updateSymbolInfo(curCodewordCount);
            var available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
            if (!context.hasMoreCharacters()) {
                // Avoid having a single C40 value in the last triplet
                var removed = new StringBuilder_1.default();
                if (buffer.length() % 3 === 2 && available !== 2) {
                    lastCharSize = this.backtrackOneCharacter(context, buffer, removed, lastCharSize);
                }
                while (buffer.length() % 3 === 1 &&
                    (lastCharSize > 3 || available !== 1)) {
                    lastCharSize = this.backtrackOneCharacter(context, buffer, removed, lastCharSize);
                }
                break;
            }
            var count = buffer.length();
            if (count % 3 === 0) {
                var newMode = HighLevelEncoder_1.default.lookAheadTest(context.getMessage(), context.pos, this.getEncodingMode());
                if (newMode !== this.getEncodingMode()) {
                    // Return to ASCII encodation, which will actually handle latch to new mode
                    context.signalEncoderChange(constants_1.ASCII_ENCODATION);
                    break;
                }
            }
        }
        this.handleEOD(context, buffer);
    };
    C40Encoder.prototype.backtrackOneCharacter = function (context, buffer, removed, lastCharSize) {
        var count = buffer.length();
        var test = buffer.toString().substring(0, count - lastCharSize);
        buffer.setLengthToZero();
        buffer.append(test);
        // buffer.delete(count - lastCharSize, count);
        /*for (let i = count - lastCharSize; i < count; i++) {
          buffer.deleteCharAt(i);
        }*/
        context.pos--;
        var c = context.getCurrentChar();
        lastCharSize = this.encodeChar(c, removed);
        context.resetSymbolInfo(); // Deal with possible reduction in symbol size
        return lastCharSize;
    };
    C40Encoder.prototype.writeNextTriplet = function (context, buffer) {
        context.writeCodewords(this.encodeToCodewords(buffer.toString()));
        var test = buffer.toString().substring(3);
        buffer.setLengthToZero();
        buffer.append(test);
        // buffer.delete(0, 3);
        /*for (let i = 0; i < 3; i++) {
          buffer.deleteCharAt(i);
        }*/
    };
    /**
     * Handle "end of data" situations
     *
     * @param context the encoder context
     * @param buffer  the buffer with the remaining encoded characters
     */
    C40Encoder.prototype.handleEOD = function (context, buffer) {
        var unwritten = Math.floor((buffer.length() / 3) * 2);
        var rest = buffer.length() % 3;
        var curCodewordCount = context.getCodewordCount() + unwritten;
        context.updateSymbolInfo(curCodewordCount);
        var available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
        if (rest === 2) {
            buffer.append('\0'); // Shift 1
            while (buffer.length() >= 3) {
                this.writeNextTriplet(context, buffer);
            }
            if (context.hasMoreCharacters()) {
                context.writeCodeword(constants_1.C40_UNLATCH);
            }
        }
        else if (available === 1 && rest === 1) {
            while (buffer.length() >= 3) {
                this.writeNextTriplet(context, buffer);
            }
            if (context.hasMoreCharacters()) {
                context.writeCodeword(constants_1.C40_UNLATCH);
            }
            // else no unlatch
            context.pos--;
        }
        else if (rest === 0) {
            while (buffer.length() >= 3) {
                this.writeNextTriplet(context, buffer);
            }
            if (available > 0 || context.hasMoreCharacters()) {
                context.writeCodeword(constants_1.C40_UNLATCH);
            }
        }
        else {
            throw new Error('Unexpected case. Please report!');
        }
        context.signalEncoderChange(constants_1.ASCII_ENCODATION);
    };
    C40Encoder.prototype.encodeChar = function (c, sb) {
        if (c === ' '.charCodeAt(0)) {
            sb.append(3);
            return 1;
        }
        if (c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0)) {
            sb.append(c - 48 + 4);
            return 1;
        }
        if (c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) {
            sb.append(c - 65 + 14);
            return 1;
        }
        if (c < ' '.charCodeAt(0)) {
            sb.append(0); // Shift 1 Set
            sb.append(c);
            return 2;
        }
        if (c <= '/'.charCodeAt(0)) {
            sb.append(1); // Shift 2 Set
            sb.append(c - 33);
            return 2;
        }
        if (c <= '@'.charCodeAt(0)) {
            sb.append(1); // Shift 2 Set
            sb.append(c - 58 + 15);
            return 2;
        }
        if (c <= '_'.charCodeAt(0)) {
            sb.append(1); // Shift 2 Set
            sb.append(c - 91 + 22);
            return 2;
        }
        if (c <= 127) {
            sb.append(2); // Shift 3 Set
            sb.append(c - 96);
            return 2;
        }
        sb.append("".concat(1, "\u001E")); // Shift 2, Upper Shift
        var len = 2;
        len += this.encodeChar(c - 128, sb);
        return len;
    };
    C40Encoder.prototype.encodeToCodewords = function (sb) {
        var v = 1600 * sb.charCodeAt(0) + 40 * sb.charCodeAt(1) + sb.charCodeAt(2) + 1;
        var cw1 = v / 256;
        var cw2 = v % 256;
        var result = new StringBuilder_1.default();
        result.append(cw1);
        result.append(cw2);
        return result.toString();
    };
    return C40Encoder;
}());
exports.C40Encoder = C40Encoder;
