var assert = require('assert'),
    spdy = require('../../'),
    keys = require('../fixtures/keys'),
    https = require('https'),
    tls = require('tls'),request
    Buffer = require('buffer').Buffer;

suite('A SPDY Server', function() {
  var server;
  setup(function(done) {
    server = spdy.createServer(keys, function(req, res) {
      res.end('ok');
    });

    server.listen(8081, done);
  });

  teardown(function(done) {
    server.once('close', done);
    server.close();
  });

  test('should respond on regular https requests', function(done) {
    https.request({
      host: 'localhost',
      port: 8081,
      path: '/',
      method: 'GET',
      agent: false,
      rejectUnauthorized: false
    }, function(res) {
      assert.equal(res.statusCode, 200);
      done();
    }).end();
  });

  test('should respond on spdy requests', function(done) {
    var socket = tls.connect(
      8081,
      'localhost',
      { NPNProtocols: ['spdy/2'], rejectUnauthorized: false },
      function() {
        var deflate = spdy.utils.createDeflate(2),
            chunks = [],
            length = 0;

        deflate.on('data', function(chunk) {
          chunks.push(chunk);
          length += chunk.length;
        });

        // Deflate headers
        deflate.write(new Buffer([
          0x00, 0x04, // method, url, version = 3 fields
          0x00, 0x06,
          0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // method
          0x00, 0x03,
          0x47, 0x45, 0x54, // get
          0x00, 0x03,
          0x75, 0x72, 0x6c, // url
          0x00, 0x01,
          0x2f, // '/'
          0x00, 0x07,
          0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // version
          0x00, 0x08,
          0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, // HTTP/1.1
          0x00, 0x04,
          0x68, 0x6f, 0x73, 0x74, // host
          0x00, 0x09,
          0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74 //localhost
        ]));

        deflate.flush(function() {
          // StreamID + Associated StreamID
          length += 10;

          // Headers
          socket.write(new Buffer([
            0x80, 0x02, 0x00, 0x01, // Control, Version, SYN_STREAM
            0x00, 0x00, 0x00, length, // Flags, length (1 byte in this case)
            0x00, 0x00, 0x00, 0x01, // StreamID
            0x00, 0x00, 0x00, 0x00, // Associated StreamID
            0x00, 0x00 // Priority + Unused
          ]));

          // Write compressed headers
          chunks.forEach(function(chunk) {
            socket.write(chunk);
          });
        });

        var response = new Buffer(85),
            offset = 0;

        socket.on('data', function(chunk) {
          assert.ok(offset + chunk.length <= 85);

          chunk.copy(response, offset);
          offset += chunk.length;

          if (offset === 85) {
            var frames = [];

            offset = 0;
            while (offset < response.length) {
              var len = (response.readUInt32BE(offset + 4) & 0x00ffffff) + 8;
              frames.push(response.slice(offset, offset + len));

              offset += len;
            }

            // SYN_STREAM frame
            assert.ok(frames.some(function(frame) {
              return frame[0] === 0x80 && // Control frame
                     frame[1] === 0x02 && // Version
                     frame.readUInt16BE(2) === 0x0002 && // SYN_STREAM
                     frame.readUInt32BE(8) === 0x0001; // StreamID
            }));

            // Data frames
            assert.ok(frames.some(function(frame) {
              return frame[0] === 0x00 && // Data frame
                     frame.readUInt32BE(0) === 0x0001 && // StreamID
                     frame.slice(8).toString() === 'ok';
            }));

            socket.destroy();
          }
        });

        socket.on('close', function() {
          done();
        });
      }
    );

    socket.on('error', function(err) {
      console.error('Socket error: ' + err);
    });

    server.on('request', function(req, res) {
      assert.equal(req.url, '/');
      assert.equal(req.method, 'GET');
      res.end('ok');
    });
  });
});
