var util = require('util') var Stream = require('stream') var StringDecoder = require('string_decoder').StringDecoder

module.exports = StringStream module.exports.AlignedStringDecoder = AlignedStringDecoder

function StringStream(from, to) {

if (!(this instanceof StringStream)) return new StringStream(from, to)

Stream.call(this)

if (from == null) from = 'utf8'

this.readable = this.writable = true
this.paused = false
this.toEncoding = (to == null ? from : to)
this.fromEncoding = (to == null ? '' : from)
this.decoder = new AlignedStringDecoder(this.toEncoding)

} util.inherits(StringStream, Stream)

StringStream.prototype.write = function(data) {

if (!this.writable) {
  var err = new Error('stream not writable')
  err.code = 'EPIPE'
  this.emit('error', err)
  return false
}
if (this.fromEncoding) {
  if (Buffer.isBuffer(data)) data = data.toString()
  data = new Buffer(data, this.fromEncoding)
}
var string = this.decoder.write(data)
if (string.length) this.emit('data', string)
return !this.paused

}

StringStream.prototype.flush = function() {

if (this.decoder.flush) {
  var string = this.decoder.flush()
  if (string.length) this.emit('data', string)
}

}

StringStream.prototype.end = function() {

if (!this.writable && !this.readable) return
this.flush()
this.emit('end')
this.writable = this.readable = false
this.destroy()

}

StringStream.prototype.destroy = function() {

this.decoder = null
this.writable = this.readable = false
this.emit('close')

}

StringStream.prototype.pause = function() {

this.paused = true

}

StringStream.prototype.resume = function () {

if (this.paused) this.emit('drain')
this.paused = false

}

function AlignedStringDecoder(encoding) {

StringDecoder.call(this, encoding)

switch (this.encoding) {
  case 'base64':
    this.write = alignedWrite
    this.alignedBuffer = new Buffer(3)
    this.alignedBytes = 0
    break
}

} util.inherits(AlignedStringDecoder, StringDecoder)

AlignedStringDecoder.prototype.flush = function() {

if (!this.alignedBuffer || !this.alignedBytes) return ''
var leftover = this.alignedBuffer.toString(this.encoding, 0, this.alignedBytes)
this.alignedBytes = 0
return leftover

}

function alignedWrite(buffer) {

var rem = (this.alignedBytes + buffer.length) % this.alignedBuffer.length
if (!rem && !this.alignedBytes) return buffer.toString(this.encoding)

var returnBuffer = new Buffer(this.alignedBytes + buffer.length - rem)

this.alignedBuffer.copy(returnBuffer, 0, 0, this.alignedBytes)
buffer.copy(returnBuffer, this.alignedBytes, 0, buffer.length - rem)

buffer.copy(this.alignedBuffer, 0, buffer.length - rem, buffer.length)
this.alignedBytes = rem

return returnBuffer.toString(this.encoding)

}