'use strict'

var AbstractTransport = require('./abstract')
var net = require('net')
var util = require('util')
var winston = require('winston-debug')
var winstonWrapper = require('winston-meta-wrapper')

function TcpWrapper () {
  AbstractTransport.call(this)
  // logging
  this._log = winstonWrapper(winston)
  this._log.addMeta({
    module: 'stun:transports:tcp'
  })
}

// Inherit from abstract transport
util.inherits(TcpWrapper, AbstractTransport)

TcpWrapper.prototype.init = function (host, port, onReady, onData, onError) {
  // init
  var self = this
  this._host = host
  this._port = port
  this._client = net.createConnection(this._port, this._host)
  this._log.debug('created TCP connection with ' + this._host + ':' + this._port)
  this._client.on('error', onError)
  this._client.on('data', function (bytes) {
    self._log.debug('incoming data: ' + bytes.length + ' bytes')
    var rinfo = {}
    rinfo.address = self._host
    rinfo.port = parseInt(self._port, 10)
    rinfo.family = net.isIPv4(self._host) ? 'IPv4' : 'IPv6'
    rinfo.size = bytes.length
    onData(bytes, rinfo, false)
  })
  this._client.on('connect', function () {
    onReady()
    // localport represents connection
    self._log.addMeta({
      conn: self._client.localPort
    })
  })
}

TcpWrapper.prototype.isReliable = function () {
  return true
}

TcpWrapper.prototype.getTimeoutDelay = function () {
  return 0
}

TcpWrapper.prototype.send = function (bytes, onSuccess, onFailure) {
  this._log.debug('outgoing data: ' + bytes.length + ' bytes')
  if (onSuccess === undefined || onFailure === undefined) {
    var errorMsg = 'tcp send bytes callback handlers are undefined'
    this._log.error(errorMsg)
    throw new Error(errorMsg)
  }
  var self = this
  var flushed = this._client.write(bytes, 'binary', function () {
    self._log.debug('message sent')
  })
  if (!flushed) {
    this._log.debug('high water -- buffer size = ' + this._client.bufferSize)
    this._client.once('drain', function () {
      self._log.debug('drained -- buffer size = ' + self._client.bufferSize)
      onSuccess()
    })
  } else {
    onSuccess()
  }
}

TcpWrapper.prototype.close = function (done) {
  this._client.once('close', function () {
    if (done) {
      done()
    }
  })
  this._client.destroy()
}

module.exports = TcpWrapper

@@ -49,24 +49,53 @@
 /abstract')%0A
+var merge = require('merge')%0A
 var net = re
@@ -252,40 +252,192 @@
 er (
+args
 ) %7B%0A  
-AbstractTransport.call(thi
+if (!(this instanceof TcpWrapper)) %7B%0A    return new TcpWrapper(args)%0A  %7D%0A  AbstractTransport.call(this)%0A  // init%0A  this._args = merge(Object.create(TcpWrapper.DEFAULTS), arg
 s)%0A 
@@ -631,16 +631,65 @@
 sport)%0A%0A
+TcpWrapper.DEFAULTS = %7B%0A  connectTimeout: 100%0A%7D%0A%0A
 TcpWrapp
@@ -828,22 +828,53 @@
  port%0A  
-this._
+// create and store tcp socket%0A  var 
 client =
@@ -919,16 +919,40 @@
 ._host)%0A
+  this._client = client%0A
   this._
@@ -967,18 +967,19 @@
 g('creat
-ed
+ing
  TCP con
@@ -1024,30 +1024,55 @@
 is._port)%0A  
-this._
+// add socket event handlers%0A  
 client.on('e
@@ -1085,30 +1085,24 @@
  onError)%0A  
-this._
 client.on('d
@@ -1408,30 +1408,24 @@
 lse)%0A  %7D)%0A  
-this._
 client.on('c
@@ -1455,126 +1455,544 @@
     
-onReady()%0A    // localport represents connection%0A    self._log.addMeta(%7B%0A      conn: self._client.localPort%0A    %7D)%0A  %7D
+// stop and remove timer%0A    clearTimeout(client._connectionTimeout)%0A    delete client._connectionTimeout%0A    // fire ready callback%0A    onReady()%0A    // localport represents connection%0A    self._log.addMeta(%7B%0A      conn: self._client.localPort%0A    %7D)%0A  %7D)%0A  client.on('close', function () %7B%0A    self._log.debug('TCP connection closed')%0A  %7D)%0A  // init connection timeout%0A  client._connectionTimeout = setTimeout(function () %7B%0A    self._log.error('connection timeout')%0A    onError('TCP connection timeout')%0A  %7D, this._args.connectTimeout
 )%0A%7D%0A

raise error when TCP connection timeouts