text: 'use strict';
  
  /**
   * Module dependencies.
   */
  const express = require('express'),
  	morgan = require('morgan'),
  	logger = require('./logger'),
  	bodyParser = require('body-parser'),
  	session = require('express-session'),
  	compression = require('compression'),
  	methodOverride = require('method-override'),
  	cookieParser = require('cookie-parser'),
  	helmet = require('helmet'),
  	passport = require('passport'),
  	raven = require('raven'),
  	MongoStore = require('connect-mongo')(session),
  	config = require('./config'),
  	consolidate = require('consolidate'),
  	path = require('path'),
  	client = new raven.Client(config.DSN),
  	i18n = require('i18n');
  
  var mongoose = require('mongoose');
  
  var supportedLanguages = ['en', 'de', 'fr', 'it', 'es'];
  
  function containsAnySupportedLanguages(preferredLanguages){
  	var i, currIndex;
  	for (i = 0; i < preferredLanguages.length; i++) {
  		currIndex = supportedLanguages.indexOf(preferredLanguages[i]);
  	    if (currIndex > -1) {
  	        return supportedLanguages[currIndex];
  	    }
  	}
  	return null;
  }
  
  module.exports = function(db) {
  	// Initialize express app
  	var app = express();
  	var url = require('url');
  
  	// Globbing model files
  	config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
  		require(path.resolve(modelPath));
  	});
  
  	// Setting application local variables
  	app.locals.google_analytics_id = config.app.google_analytics_id;
  	app.locals.title = config.app.title;
  	app.locals.signupDisabled = config.signupDisabled;
  	app.locals.description = config.app.description;
  	app.locals.keywords = config.app.keywords;
  
  	app.locals.subdomainsDisabled = config.subdomainsDisabled;
  
  	if(config.socketPortExternallyVisible){
  		app.locals.socketPort = config.socketPort;
  	} else {
  		app.locals.socketPort = '';
  	}
  
  	if(config.socketUrl){
  		app.locals.socketUrl = config.socketUrl;
  	}
  
  	app.locals.bowerFormJSFiles = config.getBowerFormJSAssets();
  	app.locals.bowerJSFiles = config.getBowerJSAssets();
  	app.locals.bowerCssFiles = config.getBowerCSSAssets();
  	app.locals.bowerOtherFiles = config.getBowerOtherAssets();
  
  	app.locals.jsFiles = config.getJavaScriptAssets();
  	app.locals.formJSFiles = config.getFormJavaScriptAssets();
  	app.locals.cssFiles = config.getCSSAssets();
  
    //Setup i18n
    i18n.configure({
      locales: supportedLanguages,
      directory: __dirname + '/locales',
      defaultLocale: 'en',
      cookie: 'userLang'
    });
  
    app.use(i18n.init);
  
    app.use(function(req, res, next) {
      // express helper for natively supported engines
      res.locals.__ = res.__ = function() {
        return i18n.__.apply(req, arguments);
      };
  
      res.locals.copyright = () => {
        return '©MySurvey '+(new Date().getFullYear());
      };
  
      next();
    });
  
  	app.use(function (req, res, next) {
  		var urlPath;
  		if(!config.subdomainsDisabled) {
  			var User = mongoose.model('User');
  			var subdomainPath = '/subdomain/';
  			var subdomains = req.subdomains;
  
  			if (subdomains.slice(0, 4).join('.') + '' === '1.0.0.127') {
  				subdomains = subdomains.slice(4);
  			}
  
  			// continue if no subdomains
  			if (!subdomains.length) {
  				return next();
  			}
  
  			urlPath = url.parse(req.url).path.split('/');
  			if (urlPath.indexOf('static') > -1) {
  				urlPath.splice(1, 1);
  				req.root = req.protocol + '://' + config.baseUrl + urlPath.join('/');
  				return next();
  			}
  
  			if (urlPath.indexOf('users') > -1 && urlPath.indexOf('me') > -1) {
  				return next();
  			}
  
  			if (subdomains.indexOf('stage') > -1 || subdomains.indexOf('admin') > -1) {
  				return next();
  			}
  
  			if (subdomains.indexOf('api') > -1) {
  				// rebuild url
  				subdomainPath += 'api' + req.url;
  				// TODO: check path and query strings are preserved
  				// reassign url
  				req.url = subdomainPath;
  				return next();
  			}
  
  			User.findOne({username: req.subdomains.reverse()[0]}).exec(function (err, user) {
  
  				if (err) {
  					req.subdomains = null;
  					// Error page
  					return res.status(404).render('404', {
              custom: 'INVALID_SUB_DOMAIN'
  					});
  				}
  				if (user === null) {
  					// Error page
  					return res.status(404).render('404', {
              custom: 'INVALID_SUB_DOMAIN'
  					});
  				}
  
  				// rebuild url
  				subdomainPath += subdomains.join('/') + req.url;
  
  				// TODO: check path and query strings are preserved
  				// reassign url
  				req.url = subdomainPath;
  
  				// Q.E.D.
  				return next();
  			});
  		} else {
  
  			urlPath = url.parse(req.url).path.split('/');
  			if (urlPath.indexOf('static') > -1 && urlPath.indexOf('view') === urlPath.indexOf('static')-1) {
  				urlPath.splice(1, 1);
  				req.url = urlPath.join('/');
  			}
  
  			return next();
  		}
  	});
  
    //Setup Prerender.io
    app.use(require('prerender-node').set('prerenderToken', process.env.PRERENDER_TOKEN));
  
  
  	// Passing the request url to environment locals
  	app.use(function(req, res, next) {
  		if(config.baseUrl === ''){
  			config.baseUrl = req.protocol + '://' + req.headers.host;
  		}
  	    res.locals.url = req.protocol + '://' + req.headers.host + req.url;
  		next();
  	});
  
  	// Should be placed before express.static
  	app.use(compression({
  		// only compress files for the following content types
  		filter: function(req, res) {
  			return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
  		},
  		// zlib option for compression level
  		level: 9
  	}));
  
  	// Set template engine as defined in the config files
  	app.engine('server.view.pug', consolidate.pug);
  
  	// Set views path and view engine
  	app.set('view engine', 'server.view.pug');
  	app.set('views', './app/views');
  
  	// Enable logger (morgan)
  	app.use(morgan(logger.getLogFormat(), logger.getMorganOptions()));
  
  	// Environment dependent middleware
  	if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
  		// Disable views cache
  		app.set('view cache', false);
  	} else if (process.env.NODE_ENV === 'production') {
  		app.locals.cache = 'memory';
  		app.set('view cache', true);
  	}
  
  	// Request body parsing middleware should be above methodOverride
  	app.use(bodyParser.urlencoded({
  		extended: true,
  		limit: '100mb'
  	}));
  
  	app.use(bodyParser.json({ limit: '100mb' }));
  	app.use(methodOverride());
  
  	// Use helmet to secure Express headers
  	app.use(helmet.frameguard());
  	app.use(helmet.xssFilter());
  	app.use(helmet.noSniff());
  	app.use(helmet.ieNoOpen());
  	app.use(helmet.dnsPrefetchControl());
  	app.use(helmet.hidePoweredBy());
  
  
  	// Setting the app router and static folder
  	app.use('/static', express.static(path.resolve('./public')));
  
  	// CookieParser should be above session
  	app.use(cookieParser());
  
  	// Express MongoDB session storage
  	app.use(session({
  		saveUninitialized: true,
  		resave: true,
  		secret: config.sessionSecret,
  		store: new MongoStore({
  	      mongooseConnection: mongoose.connection,
  	      collection: config.sessionCollection
  	    }),
  		cookie: config.sessionCookie,
  		name: config.sessionName
  	}));
  
  	// use passport session
  	app.use(passport.initialize());
  	app.use(passport.session());
  
  
  	//Visitor Language Detection
  	app.use(function(req, res, next) {
  		var acceptLanguage = req.headers['accept-language'];
  
  		var languages, supportedLanguage;
  
  		if(acceptLanguage){
  			languages = acceptLanguage.match(/[a-z]{2}(?!-)/g) || [];
  			supportedLanguage = containsAnySupportedLanguages(languages);
  		}
  
  		if(!req.user && supportedLanguage !== null){
  			var currLanguage = res.cookie('userLang');
  			if(currLanguage && currLanguage !== supportedLanguage || !currLanguage){
  				res.clearCookie('userLang');
  				res.cookie('userLang', supportedLanguage, { maxAge: 90000, httpOnly: true });
  			} else if(req.user && (!req.cookies.hasOwnProperty('userLang') || req.cookies.userLang !== req.user.language) ){
  				res.cookie('userLang', req.user.language, { maxAge: 90000, httpOnly: true });
  			}
  		}
  		next();
  	});
  
  	// Globbing routing files
  	config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
  		require(path.resolve(routePath))(app);
  	});
  
  	// Add headers for Sentry
  	app.use(function (req, res, next) {
  
  	    // Website you wish to allow to connect
  	    res.setHeader('Access-Control-Allow-Origin', 'https://sentry.io');
  
  	    // Request methods you wish to allow
  	    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
  
  	    // Request headers you wish to allow
  	    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
  
  	    // Set to true if you need the website to include cookies in the requests sent
  	    // to the API (e.g. in case you use sessions)
  	    res.setHeader('Access-Control-Allow-Credentials', true);
  
  	    // Pass to next layer of middleware
  	    next();
  	});
  
  	// Sentry (Raven) middleware
  	app.use(raven.middleware.express.requestHandler(config.DSN));
  
  	// Should come before any other error middleware
  	app.use(raven.middleware.express.errorHandler(config.DSN));
  
  	// Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
  	app.use(function(err, req, res, next) {
  		// If the error object doesn't exists
  		if (!err) {
  			return next();
  		}
  
  		// Log it
  		client.captureError(err);
  
  		// Error page
  		res.status(500).render('500', {
  		    __: i18n.__,
              error: err.stack
  		});
  	});
  
  	// Assume 404 since no middleware responded
  	app.use(function(req, res) {
  		client.captureError(new Error('Page Not Found'));
  		res.status(404).render('404', {
  			url: req.originalUrl,
  			error: 'Not Found',
              __: i18n.__
  		});
  	});
  
  	app = require('./socket.io')(app, db);
  
  	// Return Express server instance
  	return app;
  };
  