'use strict';

const zlib = require('zlib');

// tag COMPRESS DEFLATE
module.exports = {
    state: ['Authenticated', 'Selected'],

    schema: [
        {
            name: 'mechanism',
            type: 'string'
        }
    ],

    handler(command, callback) {
        let mechanism = ((command.attributes[0] && command.attributes[0].value) || '')
            .toString()
            .toUpperCase()
            .trim();

        if (!mechanism) {
            return callback(null, {
                response: 'BAD'
            });
        }

        if (mechanism !== 'DEFLATE') {
            return callback(null, {
                response: 'BAD',
                code: 'CANNOT',
                message: 'Unsupported compression mechanism'
            });
        }

        setImmediate(() => {
            this.compression = true;

            this._deflate = zlib.createDeflateRaw({
                windowBits: 15
            });
            this._inflate = zlib.createInflateRaw();

            this._deflate.once('error', err => {
                this._server.logger.debug(
                    {
                        err,
                        tnx: 'deflate',
                        cid: this.id
                    },
                    '[%s] Deflate error %s',
                    this.id,
                    err.message
                );
                this.close();
            });

            this._inflate.once('error', err => {
                this._server.logger.debug(
                    {
                        err,
                        tnx: 'inflate',
                        cid: this.id
                    },
                    '[%s] Inflate error %s',
                    this.id,
                    err.message
                );
                this.close();
            });

            this.writeStream.unpipe(this._socket);
            this._deflate.pipe(this._socket);
            let reading = false;
            let readNext = () => {
                reading = true;

                let chunk;
                while ((chunk = this.writeStream.read()) !== null) {
                    if (this._deflate && this._deflate.write(chunk) === false) {
                        return this._deflate.once('drain', readNext);
                    }
                }

                // flush data to socket
                if (this._deflate) {
                    this._deflate.flush();
                }

                reading = false;
            };
            this.writeStream.on('readable', () => {
                if (!reading) {
                    readNext();
                }
            });

            this._socket.unpipe(this._parser);
            this._socket.pipe(this._inflate).pipe(this._parser);
        });

        callback(null, {
            response: 'OK',
            message: 'DEFLATE active'
        });
    }
};
