<commit_before>var util = require('util'); // util.inherits
var net = require('net');

var handshake = require('./handshake');
//var xevents = require('./xevents');

var EventEmitter = require('events').EventEmitter;
var PackStream = require('./unpackstream');
var coreRequestsTemplate = require('./corereqs');
var hexy = require('./hexy').hexy;

var Buffer = require('buffer').Buffer;
// add 'unpack' method for buffer
require('./unpackbuffer').addUnpack(Buffer);

var xerrors = require('./xerrors');
var coreRequests = require('./corereqs');

function XClient(stream)
{
    EventEmitter.call(this);
    this.stream = stream;

    this.core_requests = {};
    this.ext_requests = {};

    pack_stream = new PackStream();

    // data received from stream is dispached to
    // read requests set by calls to .unpack and .unpackTo
    //stream.pipe(pack_stream);
   
     // pack_stream write requests are buffered and
    // flushed to stream as result of call to .flush
    // TODO: listen for drain event and flush automatically 
    //pack_stream.pipe(stream);
    
    pack_stream.on('data', function( data ) {
        //console.error(hexy(data, {prefix: 'from packer '}));
        stream.write(data);
    });
    stream.on('data', function( data ) {
        //console.error(hexy(data, {prefix: 'to unpacker '}));
        pack_stream.write(data);
    });

    this.pack_stream = pack_stream;

    this.rcrc_id = 0; // generated for each new resource
    this.seq_num = 0; // incremented in each request. (even if we don't expect reply)

    // in/out packets indexed by sequence ID
    this.requests = {};
    this.replies = {};
    this.events = {};

    this.importRequestsFromTemplates(this, coreRequests);
    this.startHandshake();

    this.event_consumers = {}; // maps window id to eventemitter

    // import available extentions
    // TODO: lazy import on first call?
    /*
    this.ext = {};
    this.ListExtensions( function(err, extentionsList ) {
        for (ext in extentionsList) {
            var extRequests = require('./ext/' + extentionsList[ext]);
            // TODO: need to call QueryExtention to get [major opcode, first event, first error]
            importRequestsFromTemplates(this, extRequests);
        }
    }
    */
    // init comon extentions
    
}
util.inherits(XClient, EventEmitter);

XClient.prototype.importRequestsFromTemplates = function(target, reqs)
{
    var client = this;
    for (r in reqs)
    {
        // r is request name
        target[r] = (function(reqName) {
            var reqFunc = function req_proxy() {
            client.seq_num++; // TODO: handle overflow (seq should be last 15 (?)  bits of the number
            // is it fast?
            var args = Array.prototype.slice.call(req_proxy.arguments);

            var callback = args.length > 0 ? args[args.length - 1] : null;
            if (callback && callback.constructor.name != 'Function')
                callback = null;

            // TODO: see how much we can calculate in advance (not in each request)
            var reqReplTemplate = reqs[reqName];
            var reqTemplate  = reqReplTemplate[0];
            var templateType = typeof reqTemplate;

            if (templateType == 'object')
                templateType = reqTemplate.constructor.name;

            if (templateType == 'function')
            {
                 // call template with input arguments (not including callback which is last argument TODO currently with callback. won't hurt)
                 //reqPack = reqTemplate.call(args);
                 reqPack = reqTemplate.apply(this, req_proxy.arguments); 
                 var format = reqPack[0];
                 var requestArguments = reqPack[1];

                 if (callback)
                     this.replies[this.seq_num] = [reqName, callback];
                                                                            
                 //console.error([format, requestArguments]);
                 client.pack_stream.pack(format, requestArguments);
                 client.pack_stream.flush();
            } else if (templateType == 'Array'){
                 var format = reqTemplate[0];
                 var requestArguments = reqTemplate[1];
                 for (a in args)
                     requestArguments.push(args[a]);

                 if (callback)
                     this.replies[this.seq_num] = [reqName, callback];

                 //console.error([format, requestArguments]);
                 client.pack_stream.pack(format, requestArguments);
                 client.pack_stream.flush();
            } else {
                 throw 'unknown request format - ' + templateType;
            }
        }
        return reqFunc;
        })(r);
    }
}

XClient.prototype.AllocID = function()
{
    // TODO: handle overflow (XCMiscGetXIDRange from XC_MISC ext)
    // TODO: unused id buffer
    this.display.rsrc_id++;
    return (this.display.rsrc_id << this.display.rsrc_shift) + this.display.resource_base;
}

XClient.prototype.unpackEvent = function(type, seq, extra, raw)
{
    var event = {}; // TODO: constructor & base functions
    event.type = type;
    event.seq = seq;

    if (type == 6) { // motion event
        var values = raw.unpack('LLLSSSSSC'); //TODO: should be LLLLsssssSC
        //event.raw = values;
        // TODO: use unpackTo???
        event.time = extra;
        event.root = values[0];
        event.wid = values[1];
        event.child = values[2];
        event.rootx = values[3];
        event.rooty = values[4];
        event.x = values[5];
        event.y = values[6];
        event.buttons = values[7];
        event.sameScreen = values[8];
    } else if (type == 12) { // Expose
        var values = raw.unpack('LSSSSS');
        event.wid = values[0];
        event.x = values[1];
        event.y = values[2];
        event.width = values[3];
        event.height = values[4];
        event.count = values[5]; // TODO: ???
    }
    return event;
}

XClient.prototype.expectReplyHeader = function()
{
    // TODO: BigReq!!!!

    var client = this;
    client.pack_stream.unpack(
       'CCSL', function(res) {
            var type = res[0];
            var seq_num = res[2];
            if (type == 0)
            {
                var error_code = res[1];              
                // unpack error packet (32 bytes for all error types, 8 of them in CCSL header)
                client.pack_stream.get(24, function(buf) {
                    // TODO: dispatch, use sequence number
                    console.error('error!!!!' + xerrors.errorText[error_code]);
                    client.expectReplyHeader();
                } ); 
                return;
            } else if (type > 1)
            {
                client.pack_stream.get(24, function(buf) {
                    var extra = res[3];
                    var ev = client.unpackEvent(type, seq_num, extra, buf);
                    client.emit('event', ev);
                    var ee = client.event_consumers[ev.wid];
                    if (ee) {
                       ee.emit('event', ev);
                    }
                    client.expectReplyHeader();
                } ); <commit_msg>initial error reply unpacking, client emits 'error' event<commit_after>var util = require('util'); // util.inherits
var net = require('net');

var handshake = require('./handshake');
//var xevents = require('./xevents');

var EventEmitter = require('events').EventEmitter;
var PackStream = require('./unpackstream');
var coreRequestsTemplate = require('./corereqs');
var hexy = require('./hexy').hexy;

var Buffer = require('buffer').Buffer;
// add 'unpack' method for buffer
require('./unpackbuffer').addUnpack(Buffer);

var xerrors = require('./xerrors');
var coreRequests = require('./corereqs');

function XClient(stream)
{
    EventEmitter.call(this);
    this.stream = stream;

    this.core_requests = {};
    this.ext_requests = {};

    pack_stream = new PackStream();

    // data received from stream is dispached to
    // read requests set by calls to .unpack and .unpackTo
    //stream.pipe(pack_stream);
   
     // pack_stream write requests are buffered and
    // flushed to stream as result of call to .flush
    // TODO: listen for drain event and flush automatically 
    //pack_stream.pipe(stream);
    
    pack_stream.on('data', function( data ) {
        //console.error(hexy(data, {prefix: 'from packer '}));
        stream.write(data);
    });
    stream.on('data', function( data ) {
        //console.error(hexy(data, {prefix: 'to unpacker '}));
        pack_stream.write(data);
    });

    this.pack_stream = pack_stream;

    this.rcrc_id = 0; // generated for each new resource
    this.seq_num = 0; // incremented in each request. (even if we don't expect reply)

    // in/out packets indexed by sequence ID
    this.requests = {};
    this.replies = {};
    this.events = {};

    this.importRequestsFromTemplates(this, coreRequests);
    this.startHandshake();

    this.event_consumers = {}; // maps window id to eventemitter

    // import available extentions
    // TODO: lazy import on first call?
    /*
    this.ext = {};
    this.ListExtensions( function(err, extentionsList ) {
        for (ext in extentionsList) {
            var extRequests = require('./ext/' + extentionsList[ext]);
            // TODO: need to call QueryExtention to get [major opcode, first event, first error]
            importRequestsFromTemplates(this, extRequests);
        }
    }
    */
    // init comon extentions
    
}
util.inherits(XClient, EventEmitter);

XClient.prototype.importRequestsFromTemplates = function(target, reqs)
{
    var client = this;
    for (r in reqs)
    {
        // r is request name
        target[r] = (function(reqName) {
            var reqFunc = function req_proxy() {
            client.seq_num++; // TODO: handle overflow (seq should be last 15 (?)  bits of the number
            // is it fast?
            var args = Array.prototype.slice.call(req_proxy.arguments);

            var callback = args.length > 0 ? args[args.length - 1] : null;
            if (callback && callback.constructor.name != 'Function')
                callback = null;

            // TODO: see how much we can calculate in advance (not in each request)
            var reqReplTemplate = reqs[reqName];
            var reqTemplate  = reqReplTemplate[0];
            var templateType = typeof reqTemplate;

            if (templateType == 'object')
                templateType = reqTemplate.constructor.name;

            if (templateType == 'function')
            {
                 // call template with input arguments (not including callback which is last argument TODO currently with callback. won't hurt)
                 //reqPack = reqTemplate.call(args);
                 reqPack = reqTemplate.apply(this, req_proxy.arguments); 
                 var format = reqPack[0];
                 var requestArguments = reqPack[1];

                 if (callback)
                     this.replies[this.seq_num] = [reqName, callback];
                                                                            
                 //console.error([format, requestArguments]);
                 client.pack_stream.pack(format, requestArguments);
                 client.pack_stream.flush();
            } else if (templateType == 'Array'){
                 var format = reqTemplate[0];
                 var requestArguments = reqTemplate[1];
                 for (a in args)
                     requestArguments.push(args[a]);

                 if (callback)
                     this.replies[this.seq_num] = [reqName, callback];

                 //console.error([format, requestArguments]);
                 client.pack_stream.pack(format, requestArguments);
                 client.pack_stream.flush();
            } else {
                 throw 'unknown request format - ' + templateType;
            }
        }
        return reqFunc;
        })(r);
    }
}

XClient.prototype.AllocID = function()
{
    // TODO: handle overflow (XCMiscGetXIDRange from XC_MISC ext)
    // TODO: unused id buffer
    this.display.rsrc_id++;
    return (this.display.rsrc_id << this.display.rsrc_shift) + this.display.resource_base;
}

XClient.prototype.unpackEvent = function(type, seq, extra, raw)
{
    var event = {}; // TODO: constructor & base functions
    event.type = type;
    event.seq = seq;

    if (type == 6) { // motion event
        var values = raw.unpack('LLLSSSSSC'); //TODO: should be LLLLsssssSC
        //event.raw = values;
        // TODO: use unpackTo???
        event.time = extra;
        event.root = values[0];
        event.wid = values[1];
        event.child = values[2];
        event.rootx = values[3];
        event.rooty = values[4];
        event.x = values[5];
        event.y = values[6];
        event.buttons = values[7];
        event.sameScreen = values[8];
    } else if (type == 12) { // Expose
        var values = raw.unpack('LSSSSS');
        event.wid = values[0];
        event.x = values[1];
        event.y = values[2];
        event.width = values[3];
        event.height = values[4];
        event.count = values[5]; // TODO: ???
    }
    return event;
}

XClient.prototype.expectReplyHeader = function()
{
    // TODO: BigReq!!!!

    var client = this;
    client.pack_stream.unpack(
       'CCSL', function(res) {
            var type = res[0];
            var seq_num = res[2];
            if (type == 0)
            {              
                var error_code = res[1];
                var error = {};
                error.code = error_code;
                error.seq = seq_num;
                error.message = xerrors.errorText[error_code];

                // unpack error packet (32 bytes for all error types, 8 of them in CCSL header)
                client.pack_stream.get(24, function(buf) {
                    // TODO: dispatch, use sequence number
                    //TODO: add more generic way to read common values
                    // if (error_code == 14)
                    {
                        var res = buf.unpack('LSC');
                        error.badPparam = res[0]; // id: GC, WinID, Font, Atom etc; Value
                        error.minorOpcode = res[1];
                        error.majorOpcode = res[2];                       
                    }
                    client.emit('error', error);
                    client.expectReplyHeader();
                } ); 
                return;
            } else if (type > 1)
            {
                client.pack_stream.get(24, function(buf) {
                    var extra = res[3];
                    var ev = client.unpackEvent(type, seq_num, extra, buf);
                    client.emit('event', ev);
                    var ee = client.event_consumers[ev.wid];
                    if (ee) {
                       ee.emit('event', ev);
                    }
                    client.expectReplyHeader();
                } ); 
{'fraction_non_alphanumeric': 0.11959778917227143, 'fraction_numerical': 0.005926616501298529, 'mean_word_length': 1.780081451314328, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5348158', 'n_tokens_mistral': 4162, 'n_tokens_neox': 4027, 'n_words': 1061}