var fs = require('fs');
var exec = require('child_process').exec;
var crypto = require('crypto');

var parseError = function(err) {
  var m = err.message.match(/tmp\.ly\:(\d+:\d+)\: error\: (.+)/);
  if (m) {
    err.location = m[1];
    err.message =  m[2];
  }
  else if (err.message == "Command failed: ") {
    err.message =  "Command failed: timeout";
  }
};

const RENDER_TIMEOUT = 60000; // 60 seconds

var watchProcess = function(p) {
  // kill processes that take too long to complete
  var timer = setTimeout(function() {
    p.kill('SIGKILL');
  }, RENDER_TIMEOUT);
  
  p.on('exit', function() {
    clearTimeout(timer);
  });
}

var renderer = function(source, callback) {
  var hash = crypto.createHash('md5').update(source).digest('hex');
  var pdfFile = hash + '.pdf';
  
  // first, check if we already rendered this source
  fs.readFile(pdfFile, function(err, data) {
    if (!err) {
      return callback(undefined, data);
    }

    // start lilypond and let it know the music is coming on stdin
    var cmd = 'lilypond -o ' + hash + ' -';
    var proc = exec(cmd , function(err, stdout, stderr) {
      if (err) {
        parseError(err);
        return callback(err);
      }
    
      console.log('Render ' + pdfFile);
    
      fs.readFile(pdfFile, function(err, data) {
        if (err) {
          return callback(err);
        }
      
        callback(undefined, data);
      })
    });
    
    // watch for timeout
    watchProcess(proc);
    
    // write source to lilypond
    proc.stdin.write(source);
    proc.stdin.end();
  });
};

exports.render = function(req, res) {
  var source = req.param('source');
  
  renderer(source || '', function(err, pdf) {
    if (err) {
      var body = JSON.stringify({
        error: {
          kind: err.constructor.name,
          message: err.message,
          location: err.location
        }
      });
      res.setHeader('Content-Type', 'text/json');
      res.setHeader('Content-Length', body.length);
      res.end(body);
    }
    else {
      res.setHeader('Content-Type', 'application/pdf');
      res.setHeader('Content-Disposition', 'inline');
      res.setHeader('Content-Length', pdf.length);
      res.end(pdf);
    }
  });
};