/**
 * TraCINg-Server - Gathering and visualizing cyber incidents on the world
 *
 * Copyright 2013 Matthias Gazzari, Annemarie Mattmann, André Wolski
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * This module defines a http server with a static webroot and a https server
 * cappable of receiving POST data. Additionally a socket.io listener provides
 * realtime connection between server and clients.
 */

// load node modules
var http = require("http");
var https = require("https");
var statics = require("node-static");	// requires 'npm install node-static'
var db = require("./db/database.js");
var utils = require("./utils.js");
var Router = require('router');
var url = require('url');

function handleError(code, message, response){
	response.statusCode = code;
	response.setHeader('Content-Type', 'text/plain; charset=utf-8');
	response.end(message + "\n");
}

function start(postHandle, ioListener, config, options) {
	var fileServer, postReceiver, io, router, httpsRouter;

	router = Router();
	router.get('/api/get_attacks', function(req, res){
		var filter = {};

		if(req.query.hasOwnProperty("country")){
			filter["countries"] = [req.query.country];
		}

		if(req.query.hasOwnProperty("latitude") && req.query.hasOwnProperty("longitude")){
			filter["location"] = [parseFloat(req.query.longitude), parseFloat(req.query.latitude)];

			if(req.query.hasOwnProperty("distance")){
				filter["distance"] = parseInt(req.query.distance, 10);
			}
		}

		if(req.query.hasOwnProperty("start")){
			filter["start"] = req.query.start;
		}

		if(req.query.hasOwnProperty("end")){
			filter["end"] = req.query.end;
		}

		if(req.query.hasOwnProperty("page")){
			filter["page"] = req.query.page;
		}

		res.setHeader('Content-Type', 'application/json; charset=utf-8');

		db.requestAttacks(filter, function(err, data){
			if(err){
				res.statusCode = 404;
				res.end();
			} else {
				res.end(JSON.stringify(data));
			}
		}, {type: 'response', response: res});
	});

	// http request
	function onRequestHttp(request, response) {
		console.log("http request received.");

		request.query = url.parse(request.url, true).query;

		router(request, response, function(){
			// try to serve files if nothing was matched
			fileServer.serve(request, response);
		});
	}

	// https request (POST only, no routing)
	function onRequestHttps(request, response) {
		var postData = "";
		//console.log("https request received.");

		var request_ip = request.headers['x-forwarded-for'] ||
			request.connection.remoteAddress ||
			request.socket.remoteAddress ||
			request.connection.socket.remoteAddress;

		var authorized = request.client.authorized;
		var sensor = {};

		var clientCert = request.connection.getPeerCertificate();
		if(clientCert && clientCert.subject){
			sensor["name"] = clientCert.subject.CN;
			sensor["type"] = clientCert.subject.O;
			if(clientCert.subject.OU){
				sensor["type"] += " " + clientCert.subject.OU;
			}

			console.log("Sensor information retrieved from client certificate: ", sensor);
		}

		// receive POST data
		request.on("data", function(chunk) {
			postData += chunk;
			// prohibit too large POST data
			if(postData.length > 1e6) {
				response.writeHead(413, {"Content-Type": "text/plain"});
				response.write("Request Entity Too Large");
				response.end();
				request.connection.destroy();
			}
		});

		// process received Data
		request.on("end", function() {
			if(request.url === "/sync"){
				if (postData) {
					try {
						var data = JSON.parse(postData);
						db.getEntriesFromSyncInfo(data, function(error, data){
							response.setHeader('Content-Type', 'application/json; charset=utf-8');
							response.statusCode = 200;
							response.end(JSON.stringify(data));
						});
					} catch(e) {
						handleError(400, "Invalid json-data given", response);
					}
				} else {
					handleError(400, "No content given", response);
				}

				return;
			}

			postHandle(response, postData, authorized, sensor, io, request_ip);
		});
	}

	db.connect(function() {
		// http Server
		var server = http.createServer(onRequestHttp).listen(config.httpPort);
		// file Server serving files from a specific folder
		fileServer = new (statics.Server)(config.webroot, {cache: 10, gzip: true});	// TODO remove cache time - test purpose
		// https Server
		postReceiver = https.createServer(options, onRequestHttps).listen(config.httpsPort);
		// io sockets listener (listens on same port as the http server)
		io = ioListener.listen(server);
		console.log("Server has started.");
	});
}

exports.start = start;
