var Stream=require("stream").Stream,util=require("util"),driver=require("websocket-driver"),EventTarget=require("./api/event_target"),Event=require("./api/event"),API=function(e){e=e||{},driver.validateOptions(e,["headers","extensions","maxLength","ping","proxy","tls","ca"]),this.readable=this.writable=!0;var t=e.headers;if(t)for(var i in t)this._driver.setHeader(i,t[i]);var r=e.extensions;r&&[].concat(r).forEach(this._driver.addExtension,this._driver),this._ping=e.ping,this._pingId=0,this.readyState=API.CONNECTING,this.bufferedAmount=0,this.protocol="",this.url=this._driver.url,this.version=this._driver.version;var s=this;this._driver.on("open",function(e){s._open()}),this._driver.on("message",function(e){s._receiveMessage(e.data)}),this._driver.on("close",function(e){s._beginClose(e.reason,e.code)}),this._driver.on("error",function(e){s._emitError(e.message)}),this.on("error",function(){}),this._driver.messages.on("drain",function(){s.emit("drain")}),this._ping&&(this._pingTimer=setInterval(function(){s._pingId+=1,s.ping(s._pingId.toString())},1e3*this._ping)),this._configureStream(),this._proxy||(this._stream.pipe(this._driver.io),this._driver.io.pipe(this._stream))};util.inherits(API,Stream),API.CONNECTING=0,API.OPEN=1,API.CLOSING=2,API.CLOSED=3,API.CLOSE_TIMEOUT=3e4;var instance={write:function(e){return this.send(e)},end:function(e){void 0!==e&&this.send(e),this.close()},pause:function(){return this._driver.messages.pause()},resume:function(){return this._driver.messages.resume()},send:function(e){return!(this.readyState>API.OPEN)&&(e instanceof Buffer||(e=String(e)),this._driver.messages.write(e))},ping:function(e,t){return!(this.readyState>API.OPEN)&&this._driver.ping(e,t)},close:function(e,t){if(void 0===e&&(e=1e3),void 0===t&&(t=""),1e3!==e&&(e<3e3||e>4999))throw new Error("Failed to execute 'close' on WebSocket: The code must be either 1000, or between 3000 and 4999. "+e+" is neither.");this.readyState!==API.CLOSED&&(this.readyState=API.CLOSING),this._driver.close(t,e);var i=this;this._closeTimer=setTimeout(function(){i._beginClose("",1006)},API.CLOSE_TIMEOUT)},_configureStream:function(){var e=this;this._stream.setTimeout(0),this._stream.setNoDelay(!0),["close","end"].forEach(function(t){this._stream.on(t,function(){e._finalizeClose()})},this),this._stream.on("error",function(t){e._emitError("Network error: "+e.url+": "+t.message),e._finalizeClose()})},_open:function(){if(this.readyState===API.CONNECTING){this.readyState=API.OPEN,this.protocol=this._driver.protocol||"";var e=new Event("open");e.initEvent("open",!1,!1),this.dispatchEvent(e)}},_receiveMessage:function(e){if(this.readyState>API.OPEN)return!1;this.readable&&this.emit("data",e);var t=new Event("message",{data:e});t.initEvent("message",!1,!1),this.dispatchEvent(t)},_emitError:function(e){if(!(this.readyState>=API.CLOSING)){var t=new Event("error",{message:e});t.initEvent("error",!1,!1),this.dispatchEvent(t)}},_beginClose:function(e,t){this.readyState!==API.CLOSED&&(this.readyState=API.CLOSING,this._closeParams=[e,t],this._stream&&(this._stream.destroy(),this._stream.readable||this._finalizeClose()))},_finalizeClose:function(){if(this.readyState!==API.CLOSED){this.readyState=API.CLOSED,this._closeTimer&&clearTimeout(this._closeTimer),this._pingTimer&&clearInterval(this._pingTimer),this._stream&&this._stream.end(),this.readable&&this.emit("end"),this.readable=this.writable=!1;var e=this._closeParams?this._closeParams[0]:"",t=this._closeParams?this._closeParams[1]:1006,i=new Event("close",{code:t,reason:e});i.initEvent("close",!1,!1),this.dispatchEvent(i)}}};for(var method in instance)API.prototype[method]=instance[method];for(var key in EventTarget)API.prototype[key]=EventTarget[key];module.exports=API;