/*! @license ©2013 Ruben Verborgh - Multimedia Lab / iMinds / Ghent University */

/** LinkedDataFragmentsServer is an HTTP server that provides access to Linked Data Fragments */

var path = require('path'),
    express = require('express'),
    negotiate = require('express-negotiate'),
    qejs = require('qejs'),
    TurtleSerializer = require('./TurtleSerializer'),
    N3Util = require('n3').Util;

var blankNodePattern = /^<?_:/,
    rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
    xsd = 'http://www.w3.org/2001/XMLSchema',
    voID = 'http://rdfs.org/ns/void#';

// Creates a new LinkedDataFragmentsServer for the specified data sources (key/value object)
function LinkedDataFragmentsServer(datasources, prefixes) {
  // create server
  var app = express();
  for (var member in prototype)
    app[member] = prototype[member];
  app._datasources = datasources;
  app._prefixes = prefixes || {};

  // set up QEJS templates
  app.engine('html', qejsEngine);
  app.set('view engine', 'html');
  app.set('views', path.join(__dirname, '../views'));

  // set up routes
  for (var route in routes)
    app.get(routes[route], app[route].bind(app));
  app.use(app.handleError.bind(app));

  return app;
}

// Handler functions and their corresponding URL patterns
var routes = {
  showIndex: '/',
  queryDatasource: /^\/(\w+)$/,
  sendStylesheet: '/stylesheets/main',
};

var prototype = LinkedDataFragmentsServer.prototype = {
  // Shows the index page with a list of data sources
  showIndex: function (req, res) {
    var datasources = Object.keys(this._datasources);
    req.negotiate({
      'text/html': function () {
        res.render('index', { datasources: datasources });
      },
      'text/turtle': function () {
        var serializer = new TurtleSerializer(res);
        res.writeHead(200, { 'Content-Type': 'text/turtle; charset=utf-8' });
        serializer.writeTriples(datasources.map(function (datasource) {
          return { subject: '/' + datasource, predicate: rdf + 'type', object: voID + 'Dataset' };
        }));
        serializer.end();
      },
    });
  },

  // Shows results of a data source query
  queryDatasource: function (req, res) {
    var datasourceName = req.params[0],
        datasource = this._datasources[datasourceName],
        query = req.query,
        pattern = { subject: parseEntity(query.subject, this._prefixes),
                    predicate: parseEntity(query.predicate, this._prefixes),
                    object: parseEntity(query.object, this._prefixes) },
        queryResult = datasource.query(pattern);

    req.negotiate({
      'text/html': function () {
        res.render('datasource', { N3Util: N3Util, datasourceName: datasourceName,
                                   query: query, pattern: pattern, queryResult: queryResult });
      },
      'text/turtle': function () {
        queryResult.then(function (queryResult) {
          var serializer = new TurtleSerializer(res);
          res.writeHead(200, { 'Content-Type': 'text/turtle; charset=utf-8' });
          serializer.writeTriple({ subject: req.url, predicate: voID + 'triples',
                                   object: '"' + queryResult.total + '"^^<' + xsd + 'integer>' });
          serializer.writeTriples(queryResult.triples);
          serializer.end();
        });
      },
    });
  },

  // Sends the stylesheet
  sendStylesheet: function (req, res) {
    res.sendfile('views/main.css');
  },

  // Handles an application error
  handleError: function (error, req, res, next) {
    res.status(500);
    res.render('error', { error: error });
  },
};

// Parses an entity (URI, literal, or blank) into canonical form
function parseEntity(entity, prefixes) {
  // falsy or variable indicates an unknown
  if (!entity || /^<?_:/.test(entity))
    return null;
  // angular brackets indicate a URI
  var match = /^<(.*)>$/.exec(entity);
  if (match)
    return match[1];
  // if it's a QName, expand it
  if (N3Util.isQName(entity))
    return N3Util.expandQName(entity, prefixes);
  // literals don't require extra parsing
  if (N3Util.isLiteral(entity))
    return entity;
  // assume it is a URI without angular brackets
  return entity;
}

// Renders the file with the QEJS engine
function qejsEngine(path, options, callback) {
  qejs.renderFile(path, options).then(callback.bind(null, null), callback);
}

module.exports = LinkedDataFragmentsServer;

@@ -1075,16 +1075,53 @@
  routes%0A
+  app.use(app.addHeaders.bind(app));%0A
   for (v
@@ -1140,16 +1140,16 @@
 routes)%0A
-
     app.
@@ -1468,16 +1468,168 @@
 ype = %7B%0A
+  addHeaders: function (req, res, next) %7B%0A    res.set('Vary', 'Accept');%0A    res.set('X-Powered-By', 'Linked Data Fragments Server');%0A    next();%0A  %7D,%0A%0A
   // Sho
@@ -1983,33 +1983,20 @@
     res.
-writeHead(200, %7B 
+set(
 'Content
@@ -1993,33 +1993,33 @@
 t('Content-Type'
-:
+,
  'text/turtle; c
@@ -2023,34 +2023,32 @@
 ; charset=utf-8'
- %7D
 );%0A        seria
@@ -3144,25 +3144,12 @@
 res.
-writeHead(200, %7B 
+set(
 'Con
@@ -3158,17 +3158,17 @@
 nt-Type'
-:
+,
  'text/t
@@ -3188,18 +3188,16 @@
 t=utf-8'
- %7D
 );%0A     

Add HTTP headers.