text: var getAuthString = require('./auth');
  var xutil = require('./xutil');
  
  function readVisuals(bl, visuals, n_visuals, cb)
  {
      if (n_visuals == 0)
      {
          cb();
          return;
      }
  
      var visual = {};
      bl.unpackTo( visual,
      [
          'L vid', 
          'C class', 
          'C bits_per_rgb', 
          'S map_ent',
          'L red_mask',
          'L green_mask',
          'L blue_mask',
          'xxxx'
      ], 
      function() {
           var vid = visual.vid;
           // delete visual.vid;
           visuals[visual.vid] = visual;
           if (Object.keys(visuals).length == n_visuals)
               cb()
           else
               readVisuals(bl, visuals, n_visuals, cb);      
      });
  }
  
  function readDepths(bl, display, depths, n_depths, cb)
  {
      if (n_depths == 0)
      {
          cb();
          return;
      }
  
      bl.unpack( 'CxSxxxx', function(res) {
          var dep = res[0];
          var n_visuals = res[1];
          var visuals = {};
          readVisuals(bl, visuals, n_visuals, function()
          {
              depths[dep] = visuals;
              if (Object.keys(depths).length == n_depths)
                  cb();
              else
                  readDepths(bl, display, depths, n_depths, cb);
          });  
      });
  }
  
  function readScreens(bl, display, cbDisplayReady)
  {
       // for (i=0; i < display.screen_num; ++i)
       {
           var scr = {};
           bl.unpackTo( scr,
           [
               'L root', 
               'L default_colormap',
               'L white_pixel', 
               'L black_pixel',
               'L input_masks', 
               'S pixel_width', 
               'S pixel_height', 
               'S mm_width', 
               'S mm_height', 
               'S min_installed_maps', 
               'S max_installed_maps',
               'L root_visual', 
               'C root_depth',
               'C backing_stores',
               'C root_depth',
               'C num_depths'
           ], 
           function () {
               var depths = {};
               readDepths(bl, display, depths, scr.num_depths, function() {
  
                   scr.depths = depths;
                   delete scr.num_depths;
                   display.screen.push(scr);
                
                   if (display.screen.length == display.screen_num)
                   {
                       delete display.screen_num;
                       cbDisplayReady(display);
                       return;
                   } else {
                       readScreens(bl, display, cbDisplayReady);
                   }
               });
           });         
       }
  }
  
  function readServerHello(bl, cb)
  {
  
  bl.unpack('C', function(res) {
       
       if (res[0] == 0)
       {
           // conection time error
           // unpack error (? TODO)
           var err = new Error;
           cb(err); // TODO: detect that this is error on xcore side
           // TODO: do we need to close stream from our side?
           // TODO: api to close source stream via attached unpackstream
           return;
       }
  
       var display = {};
       bl.unpackTo(
           display,
           [
            'x',
            'S major',
            'S minor',
            'S xlen',
            'L release', 
            'L resource_base', 
            'L resource_mask', 
            'L motion_buffer_size', 
            'S vlen',
            'S max_request_length',
            'C screen_num',
            'C format_num',
            'C image_byte_order',
            'C bitmap_bit_order',
            'C bitmap_scanline_unit',
            'C bitmap_scanline_pad',
            'C min_keycode',
            'C max_keycode',
            'xxxx'
           ],       
             
           function()
           {
               var pvlen = xutil.padded_length(display.vlen);
  
               // setup data to generate resource id
               // TODO: cleaunup code here
               var mask = display.resource_mask;
               display.rsrc_shift = 0; 
               while (!( (mask >> display.rsrc_shift) & 1) )
                   display.rsrc_shift++;
               display.rsrc_id = 0;
  
               bl.get(pvlen, function(vendor) 
               {
                   display.vendor = vendor.toString().substr(0, display.vlen); // utf8 by default?
  
                   display.format = {};
                   for (var i=0; i < display.format_num; ++i)
                   {
                       bl.unpack('CCCxxxxx', function(fmt) {
                           var depth = fmt[0];
                           display.format[depth] = {};
                           display.format[depth].bits_per_pixel = fmt[1];
                           display.format[depth].scanline_pad = fmt[2];
                           if (Object.keys(display.format).length == display.format_num)
                           {
                               delete display.format_num;
                               display.screen = [];
                               readScreens(bl, display, cb);
                           }
                       });
                   }            
               });
           }
       );
  });
  
  }
  
  function getByteOrder() {
      var isLittleEndian = ((new Uint32Array((new Uint8Array([1,2,3,4])).buffer))[0] === 0x04030201);
      if (isLittleEndian) {
          return 'l'.charCodeAt(0);
      } else {
          return 'B'.charCodeAt(0);
      }
  }
  
  function writeClientHello(stream, displayNum, authHost)
  {
      getAuthString( displayNum, authHost, function( authType, authData ) {
          var byte_order = getByteOrder();
          var protocol_major = 11; // TODO: config? env?
          var protocol_minor = 0;
          stream.pack(
              'CxSSSSxxpp', 
              [ 
              byte_order,
              protocol_major,
              protocol_minor,
              authType.length,
              authData.length,
              authType,
              authData
              ]
          );
          stream.flush(); 
      });
  }
  
  module.exports.readServerHello = readServerHello;
  module.exports.writeClientHello = writeClientHello;
  