var stream = require('stream')
var util = require('util')
var gen = require('generate-object-property')

var CsvWriteStream = function(opts) {
  if (!opts) opts = {}
  stream.Transform.call(this, {objectMode:true, highWaterMark:16})

  this.sendHeaders = opts.sendHeaders !== false
  this.headers = opts.headers || null
  this.separator = opts.separator || opts.seperator || ','
  this.newline = opts.newline || '\n'

  this._objRow = null
  this._arrRow = null
  this._first = true
  this._destroyed = false
}

util.inherits(CsvWriteStream, stream.Transform)

CsvWriteStream.prototype._compile = function(headers) {
  var sep = this.separator
  var str = 'function toRow(obj) {\n'

  if (!headers.length) str += '""'

  headers = headers.map(function(prop, i) {
    str += 'var a'+i+' = '+prop+' == null ? "" : '+prop+'\n'
    return 'a'+i
  })

  str += 'return '

  headers.forEach(function(prop, i) {
    str += (i ? '+"'+sep+'"+' : '') + '(/['+sep+'\\r\\n"]/.test('+prop+') ? esc('+prop+'+"") : '+prop+')'
  })

  str += '+'+JSON.stringify(this.newline)+'\n}'

  return new Function('esc', 'return '+str)(esc)
}

CsvWriteStream.prototype._transform = function(row, enc, cb) {
  var isArray = Array.isArray(row)

  if (!isArray && !this.headers) this.headers = Object.keys(row)

  if (this._first && this.headers) {
    this._first = false

    var objProps = []
    var arrProps = []
    var heads = []

    for (var i = 0; i < this.headers.length; i++) {
      arrProps.push('obj['+i+']')
      objProps.push(gen('obj', this.headers[i]))
    }

    this._objRow = this._compile(objProps)
    this._arrRow = this._compile(arrProps)

    if (this.sendHeaders) this.push(this._arrRow(this.headers))
  }

  if (isArray) {
    if (!this.headers) return cb(new Error('no headers specified'))
    this.push(this._arrRow(row))
  } else {
    this.push(this._objRow(row))
  }

  cb()
}

CsvWriteStream.prototype.destroy = function (err) {
  if(this._destroyed) return
  this._destroyed = true
  
  process.nextTick(function () {
    if(err) this.emit('error', err)
    this.emit('close')
  }.bind(this))
}

module.exports = function(opts) {
  return new CsvWriteStream(opts)
}

function esc(cell) {
  return '"'+cell.replace(/"/g, '""')+'"'
}
@@ -606,24 +606,53 @@
 (headers) %7B%0A
+  var newline = this.newline%0A
   var sep = 
@@ -875,35 +875,246 @@
 %0A%0A  
-str += 'return '%0A%0A  headers
+for (var i = 0; i %3C headers.length; i += 500) %7B // do not overflowi the callstack on lots of cols%0A    var part = headers.length %3C 500 ? headers : headers.slice(i, i + 500)%0A    str += i ? 'result += %22'+sep+'%22 + ' : 'var result = '%0A    part
 .for
@@ -1125,37 +1125,39 @@
 (function(prop, 
-i
+j
 ) %7B%0A
+  
     str += (i ? 
@@ -1152,17 +1152,17 @@
 str += (
-i
+j
  ? '+%22'+
@@ -1250,24 +1250,60 @@
 ')'%0A
+  
   %7D)%0A
-%0A  str += '
+    str += '%5Cn'%0A  %7D%0A%0A  str += 'return result 
 +'+J
@@ -1316,21 +1316,16 @@
 ringify(
-this.
 newline)

fix maxogden/dat#287 by not exceeding callstack on big objects