var cradle  = require('cradle');
var express = require('express');
var fs      = require('fs');
var spawner = require('spawner').create();
var util    = require('util');
var uuid    = require('node-uuid');

var app = express.createServer(
  express.logger(),
  express.cookieParser(),
  express.session({ secret: process.env.SECRET }),
  require('connect-form')({ keepExtensions: true })
);

function log_action(uuid, message) {
  console.log(uuid + ': ' + message);
}

function log_error(uuid, message) {
  log_action(uuid, 'ERROR: ' + message);
}

// connect to couchdb
var couchdb_url = require('url').parse(process.env.CLOUDANT_URL);
var couchdb_options = couchdb_url.auth ?
  { auth: { username: couchdb_url.auth.split(':')[0], password: couchdb_url.auth.split(':')[1] }  } :
  { }
var db = new(cradle.Connection)(couchdb_url.hostname, couchdb_url.port || 5984, couchdb_options).database('make');
db.create(function(){});

// POST /make starts a build
app.post('/make', function(request, response, next) {

  // require a form
  if (! request.form) {
    console.log('invalid form');
    response.write('invalid form');
    response.send(500);
  } else {

    // form handler
    request.form.complete(function(err, fields, files) {

      // if there's an error, pass through to the next handler
      if (err) { return next(err); }

      // match on the shared secret
      if (fields.secret != process.env.SECRET) {
        response.write('invalid secret');
        response.send(500);
      } else {

        var id      = uuid();
        var command = fields.command;
        var prefix  = fields.prefix;
        var deps    = JSON.parse(fields.deps);

        // create a couchdb documents for this build
        log_action(id, 'saving to couchdb');
        db.save(id, { command:command, prefix:prefix, deps:deps }, function(err, doc) {
          if (err) { log_error(id, util.inspect(err)); return next(err); }

          // save the input tarball as an attachment
          log_action(id, 'saving attachment - [id:' + doc.id + ', rev:' + doc.rev + ']')
          fs.createReadStream(files.code.path).pipe(db.saveAttachment(
            {id: doc.id,
             rev: doc.rev},
            {name:'input',
            'Content-Type': 'application/octet-stream'},
            function(err, data) {
              if (err) {
                // work around temporary problem with cloudant and document
                // conflicts
                if (err.error != 'conflict') {
                  log_error(id, err.reason);
                  return next(err.reason);
                }
              }

              // spawn bin/make with this build id
              log_action(id, 'spawning build');
              var ls = spawner.spawn('bin/make ' + id, function(err) {
                log_error(id, 'could not spawn: ' + err);
                response.write('could not spawn: ' + err);
                response.send(500);
              });

              ls.on('error', function(error) {
                log_error(id, 'spawn error: ' + util.inspect(error));
                response.write('error: ' + util.inspect(error));
                response.send(500);
              });

              ls.on('data', function(data) {
                response.write(data);
              });

              ls.on('exit', function(code) {
                response.end();
              });
            }
          ));

          // return the build id as a header
          response.header('X-Make-Id', id);
        });
      }

    });
  }
});

// download build output
app.get('/output/:id', function(request, response, next) {

  // from couchdb
  var stream = db.getAttachment(request.params.id, 'output', function(){});

  stream.on('error', function(err) {
    console.log('download error: ' + err);
  });

  stream.on('data', function(chunk) {
    response.write(chunk, 'binary');
  });

  stream.on('end', function(chunk) {
    response.end();
  });

});

// start up the webserver
var port = process.env.PORT || 3000;
console.log('listening on port ' + port);
app.listen(port);

@@ -1630,16 +1630,64 @@
 eps    =
+ %5B%5D;%0A        if (fields.deps) %7B%0A          deps =
  JSON.pa
@@ -1703,16 +1703,26 @@
 s.deps);
+%0A        %7D
 %0A%0A      

Deal with empty dependencies, fixes #18