'use strict'


//create multiple types of server in one go.
/*
  net-shs:
    ip, port, keypair

  [['net', ip, port], ['shs', keypair]]

  [host, port, 'net', keypair, 'shs']

  [host, port, 'ws', keypair, 'shs']

  [shs:key,net:port:host/url]

*/

var isArray = Array.isArray
function head (opts) {
  return isArray(opts) ? opts[0] : opts
}
function tail (opts) {
  return isArray(opts) ? opts.slice(1) : []
}

function compose (stream, transforms, cb) {
  ;(function next (err, stream, i) {
    if(err) return cb(err)
    else if(i >= transforms.length) return cb(null, stream)
    else
      transforms[i](stream, function (err, stream) {
        next(err, stream, i+1)
      })
  })(null, stream, 0)
}

function parse (u) {
  return u && 'object' == typeof u ? u : require('url').parse(u)
}

module.exports = function (handlers) {
  function findProto (opts) {
    if(!opts) throw new Error('expected protocol description')
    return handlers.find(function (e) {
      var protocol = opts.protocol.replace(/:$/,'')
      return e.protocol == protocol || e.name == protocol
    })
  }

  function transforms (opts, isServer) {
    return opts.map(function (opt) {
      var create = findProto(opt)(opt) // create server???
      return function (stream, cb) {
        create(stream, cb)
      }
    })
  }

  return {
    createServer: function (opts, onConnection) {
      var closes = []
      opts.forEach(function (proto) {
        var p = findProto(head(proto))
        if(p) closes.push(p.createServer(head(proto), function (stream) {
          compose(stream, transforms(tail(proto), true), function (err, stream) {
            if(err) console.error(err)
            else onConnection(stream)
          })
        }))
      })
      return function () {
        closes.forEach(function (close) { close () })
      }
    },
    connect: function (address, cb) {
      var opts = parse(address)
      var p = findProto(head(opts))
      if(p) p.connect(head(opts), function (err, stream) {
        if(err) cb(err)
        else compose(stream, transforms(tail(opts), false), cb)
      })
      else cb(new Error('could not connect to:'+JSON.stringify(address)))
    }
  }
}


@@ -6,16 +6,1014 @@
 strict'%0A
+/*%0AAPI%0A%0Acurrently:%0A%0Ainstall plugins%0A%0Athen describe server.%0A%0Athen describe connection.%0A%0Aconsidered create plugins + describe server in one go,%0Abut some plugins (i.e. security) might be installed in more than one%0Aplace... we don't want to have to describe each possible client%0Acombination.%0A%0Aalso, some plugins have state (like access to keys)%0Athat should be set one time.%0A%0Aor maybe we do want to preconfigure the clients and servers%0Athat are acceptable? we shouldn't we shouldn't just connect via%0Aany string description we can parse.%0A%0Ams = MultiServer(%5B%0A  server(opts),%0A  %5Bserver(opts), security(opts), compression(opts)%5D//...%0A%5D)%0A%0AMultiServer()%0A  .useServers(%5B...%5D) //net~shs; ws~shs; utp~shs; webrtc~shs; tor~shs;%0A  .useClients(%5B...%5D)%0A  .createServer(function (stream) %7B...%7D)%0A  .connect(string, function (err, stream) %7B...%7D)%0A%0A%0Ams.combine(server, transform, transform) //etc%0A%0Ahmm, okay so one thing we def need is to take a server chain%0Aand produce a address string. maybe I should do that next?%0A%0A*/%0A
 %0A%0A//crea

check in notes, for prosperity