"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const buffer_writer_1 = require("./buffer-writer");
const writer = new buffer_writer_1.Writer();
const startup = (opts) => {
    // protocol version
    writer.addInt16(3).addInt16(0);
    for (const key of Object.keys(opts)) {
        writer.addCString(key).addCString(opts[key]);
    }
    writer.addCString('client_encoding').addCString('UTF8');
    var bodyBuffer = writer.addCString('').flush();
    // this message is sent without a code
    var length = bodyBuffer.length + 4;
    return new buffer_writer_1.Writer().addInt32(length).add(bodyBuffer).flush();
};
const requestSsl = () => {
    const response = Buffer.allocUnsafe(8);
    response.writeInt32BE(8, 0);
    response.writeInt32BE(80877103, 4);
    return response;
};
const password = (password) => {
    return writer.addCString(password).flush(112 /* startup */);
};
const sendSASLInitialResponseMessage = function (mechanism, initialResponse) {
    // 0x70 = 'p'
    writer.addCString(mechanism).addInt32(Buffer.byteLength(initialResponse)).addString(initialResponse);
    return writer.flush(112 /* startup */);
};
const sendSCRAMClientFinalMessage = function (additionalData) {
    return writer.addString(additionalData).flush(112 /* startup */);
};
const query = (text) => {
    return writer.addCString(text).flush(81 /* query */);
};
const emptyArray = [];
const parse = (query) => {
    // expect something like this:
    // { name: 'queryName',
    //   text: 'select * from blah',
    //   types: ['int8', 'bool'] }
    // normalize missing query names to allow for null
    const name = query.name || '';
    if (name.length > 63) {
        /* eslint-disable no-console */
        console.error('Warning! Postgres only supports 63 characters for query names.');
        console.error('You supplied %s (%s)', name, name.length);
        console.error('This can cause conflicts and silent errors executing queries');
        /* eslint-enable no-console */
    }
    const types = query.types || emptyArray;
    var len = types.length;
    var buffer = writer
        .addCString(name) // name of query
        .addCString(query.text) // actual query text
        .addInt16(len);
    for (var i = 0; i < len; i++) {
        buffer.addInt32(types[i]);
    }
    return writer.flush(80 /* parse */);
};
const bind = (config = {}) => {
    // normalize config
    const portal = config.portal || '';
    const statement = config.statement || '';
    const binary = config.binary || false;
    var values = config.values || emptyArray;
    var len = values.length;
    var useBinary = false;
    // TODO(bmc): all the loops in here aren't nice, we can do better
    for (var j = 0; j < len; j++) {
        useBinary = useBinary || values[j] instanceof Buffer;
    }
    var buffer = writer.addCString(portal).addCString(statement);
    if (!useBinary) {
        buffer.addInt16(0);
    }
    else {
        buffer.addInt16(len);
        for (j = 0; j < len; j++) {
            buffer.addInt16(values[j] instanceof Buffer ? 1 : 0);
        }
    }
    buffer.addInt16(len);
    for (var i = 0; i < len; i++) {
        var val = values[i];
        if (val === null || typeof val === 'undefined') {
            buffer.addInt32(-1);
        }
        else if (val instanceof Buffer) {
            buffer.addInt32(val.length);
            buffer.add(val);
        }
        else {
            buffer.addInt32(Buffer.byteLength(val));
            buffer.addString(val);
        }
    }
    if (binary) {
        buffer.addInt16(1); // format codes to use binary
        buffer.addInt16(1);
    }
    else {
        buffer.addInt16(0); // format codes to use text
    }
    return writer.flush(66 /* bind */);
};
const emptyExecute = Buffer.from([69 /* execute */, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00]);
const execute = (config) => {
    // this is the happy path for most queries
    if (!config || (!config.portal && !config.rows)) {
        return emptyExecute;
    }
    const portal = config.portal || '';
    const rows = config.rows || 0;
    const portalLength = Buffer.byteLength(portal);
    const len = 4 + portalLength + 1 + 4;
    // one extra bit for code
    const buff = Buffer.allocUnsafe(1 + len);
    buff[0] = 69 /* execute */;
    buff.writeInt32BE(len, 1);
    buff.write(portal, 5, 'utf-8');
    buff[portalLength + 5] = 0; // null terminate portal cString
    buff.writeUInt32BE(rows, buff.length - 4);
    return buff;
};
const cancel = (processID, secretKey) => {
    const buffer = Buffer.allocUnsafe(16);
    buffer.writeInt32BE(16, 0);
    buffer.writeInt16BE(1234, 4);
    buffer.writeInt16BE(5678, 6);
    buffer.writeInt32BE(processID, 8);
    buffer.writeInt32BE(secretKey, 12);
    return buffer;
};
const cstringMessage = (code, string) => {
    const stringLen = Buffer.byteLength(string);
    const len = 4 + stringLen + 1;
    // one extra bit for code
    const buffer = Buffer.allocUnsafe(1 + len);
    buffer[0] = code;
    buffer.writeInt32BE(len, 1);
    buffer.write(string, 5, 'utf-8');
    buffer[len] = 0; // null terminate cString
    return buffer;
};
const emptyDescribePortal = writer.addCString('P').flush(68 /* describe */);
const emptyDescribeStatement = writer.addCString('S').flush(68 /* describe */);
const describe = (msg) => {
    return msg.name
        ? cstringMessage(68 /* describe */, `${msg.type}${msg.name || ''}`)
        : msg.type === 'P'
            ? emptyDescribePortal
            : emptyDescribeStatement;
};
const close = (msg) => {
    const text = `${msg.type}${msg.name || ''}`;
    return cstringMessage(67 /* close */, text);
};
const copyData = (chunk) => {
    return writer.add(chunk).flush(100 /* copyFromChunk */);
};
const copyFail = (message) => {
    return cstringMessage(102 /* copyFail */, message);
};
const codeOnlyBuffer = (code) => Buffer.from([code, 0x00, 0x00, 0x00, 0x04]);
const flushBuffer = codeOnlyBuffer(72 /* flush */);
const syncBuffer = codeOnlyBuffer(83 /* sync */);
const endBuffer = codeOnlyBuffer(88 /* end */);
const copyDoneBuffer = codeOnlyBuffer(99 /* copyDone */);
const serialize = {
    startup,
    password,
    requestSsl,
    sendSASLInitialResponseMessage,
    sendSCRAMClientFinalMessage,
    query,
    parse,
    bind,
    execute,
    describe,
    close,
    flush: () => flushBuffer,
    sync: () => syncBuffer,
    end: () => endBuffer,
    copyData,
    copyDone: () => copyDoneBuffer,
    copyFail,
    cancel,
};
exports.serialize = serialize;
//# sourceMappingURL=serializer.js.map