#!/usr/bin/env node
// create a local http server on port 8766 that visualizes the path of a Tesla Model S
// the data is taken from a MongoDB database
// the server can visualize / fast forward through past data in that database
// the client (as viewed in a browser) keeps updating real time until stopped
//
// You need a valid Google Maps v3 API key to use this script
//	https://developers.google.com/maps/documentation/javascript/tutorial#api_key
//
var apiKey = 'AIzaSyAtYQ9xjedv3B6_2HwsDVMY7oHlbNs-cvk';


function argchecker( argv ) {
	if (argv.db === true) throw 'MongoDB database name is unspecified. Use -d dbname or --db dbname';
}

var argv = require('optimist')
	.usage('Usage: $0 --db <MongoDB database> [--port <http listen port>] [--silent] [--verbose]')
	.check( argchecker )
	.alias('p', 'port')
	.describe('p', 'Listen port for the local http server')
	.default('p', 8766)
	.alias('d', 'db')
	.describe('d', 'MongoDB database name')
	.demand('d')
	.alias('s', 'silent')
	.describe('s', 'Silent mode: no output to console')
	.boolean(['s'])
	.alias('v', 'verbose')
	.describe('v', 'Verbose mode: more output to console')
	.boolean(['v'])
	.alias('?', 'help')
	.describe('?', 'Print usage information');



var creds = require('../config.js').config(argv);
argv = argv.argv;
if ( argv.help === true ) {
	console.log( 'Usage: visualize.js --db <MongoDB database> [--silent] [--verbose]');
	process.exit(1);
}
// set and check the validity of the HTTP listen port 
// the environment variable $PORT is read for deployment on heroku
var httpport = process.env.PORT;
if ( !isNaN(httpport) && httpport >= 1) {
	console.log('Using listen port (' + httpport + ') set by $PORT environment variable');
	argv.port = httpport;
}
var MongoClient = require('mongodb').MongoClient;
var mongoUri = process.env.MONGOLAB_URI|| process.env.MONGOHQ_URI || 'mongodb://127.0.0.1:27017/' + argv.db;
console.log('Using MongoDB URI: ' + mongoUri);
var date = new Date();
var http = require('http');
var fs = require('fs');
var lastTime = 0;
var started = false;
var to;
var capacity;
var express = require('express');
var app = express();
var passport = require('passport'), LocalStrategy = require('passport-local').Strategy;
var speedup = 60000;
var nav = "";
var system = "";
var fwVersion = "";
var vin = "";
var optionString = "";

var optionText = {
	RENA: "North American",
	REEU: "European",
	TM02: " Signature",
	PF01: " P",
	PF00: " S",
	BT85: "85PLUS",
	BT60: "60",
	BT40: "40",
	PBSB: "<li> black</li>",
	PBCW: "<li> solid white</li>",
	PMSS: "<li> silver</li>",
	PMTG: "<li> dolphin gray metallic</li>",
	PMAB: "<li> metallic brown</li>",
	PMMB: "<li> metallic blue</li>",
	PMSG: "<li> metallic green</li>",
	PPSW: "<li> pearl white</li>",
	PPMR: "<li> multi-coat red</li>",
	PPSR: "<li> signature red</li>",
	RFPO: "<li> panorama roof</li>",
	WT19: "<li> silver 19\" wheels</li>",
	WT21: "<li> silver 21\" wheels</li>",
	WTSP: "<li> gray 21\" wheels</li>",
	WTSG: "<li> gray performance 21\" wheels</li>",
	TR01: "<li> third row seats</li>",
	SU01: "<li> air suspension</li>",
	SC01: "<li> super charger enabled</li>",
	TP01: "<li> tech package</li>",
	AU01: "<li> audio upgrade</li>",
	CH01: "<li> dual charger</li>",
	PK01: "<li> parking sensors</li>",
	CW01: "<li> cold weather package</li>",
	LP01: "<li> premium lighting package</li>",
	SP01: "<li> security package</li>"
};
	

// this is super simplistic for now. Clear text passwords, nothing fancy, trivial to
// intercept - but it's a start
var users;
if (creds !== undefined && creds.hasOwnProperty('visualize')) {
	users = creds.visualize;
}
// please change this to have at least some trivial session hijacking protection
var localSecret = "please change this secret string";




// these two functions implement our simplistic user lookup
function findById(id, fn) {
	var idx = id - 1;
	if (users[idx]) {
		fn(null, users[idx]);
	} else {
		fn(new Error('User ' + id + ' does not exist'));
	}
}
function findByUsername(username, fn) {
	console.log("find user", username);
	for (var i = 0, len = users.length; i < len; i++) {
		var user = users[i];
		if (user.username === username) {
			return fn(null, user);
		}
	}
	return fn(null, null);
}



function secondsToHms(d) {
d = Number(d);
var h = Math.floor(d / 3600);
var m = Math.floor(d % 3600 / 60);
var s = Math.floor(d % 3600 % 60);
return ((h > 0 ? h + ":" : "") + (m > 0 ? (h > 0 && m < 10 ? "0" : "") + m + ":" : "0:") + (s < 10 ? "0" : "") + s); }


// Passport session setup.
//   To support persistent login sessions, Passport needs to be able to
//   serialize users into and deserialize users out of the session.  Typically,
//   this will be as simple as storing the user ID when serializing, and finding
//   the user by ID when deserializing.
passport.serializeUser(function(user, store) {
	store(null, user.id);
});
passport.deserializeUser(findById);
//
// now setup passport and route it into our express setup
//
passport.use(new LocalStrategy(
	function(username, password, done) {
		findByUsername(username, function(err, user) {
			if (err) {
				return done(err);
			}
			if (!user || user.password != password) {
				return done(null, false, { message: 'Invalid user or password' });
			}
			return done(null, user);
		});
	}
));
app.configure(function() {
	app.use(express.cookieParser());
	app.use(express.bodyParser());
	app.use(express.session({ secret: localSecret }));
	app.use(passport.initialize());
	app.use(passport.session());
	app.use(app.router);
});
// simple login screen with correspoding POST setup
app.get('/login', function(req,res) {
	fs.readFile(__dirname + "/login.html", "utf-8", function(err, data) {
		if (err) throw err;
		res.send(data);
	});
});
app.post('/login', passport.authenticate('local', { successRedirect: '/',
                                                    failureRedirect: '/login' }));

// call this in every app.get() that should only be accessible when logged in
function ensureAuthenticated(req, res, next) {
	if (users === undefined || req.isAuthenticated()) {
		return next();
	}
	res.redirect('/login')
}

// read in the shared navigation bar so we can insert this into every page
fs.readFile(__dirname + "/otherfiles/nav.html", "utf-8", function(err, data) {
	if (err) throw err;
	nav = data;
});

// shorthand to get leading zero when showing minutes
function lZ(mins) {
	var zmins = '0' + mins;
	return zmins.substr(zmins.length - 2);
}

function makeDate(string, offset) {
	var args = string.split('-');
	var date = new Date(args[0], args[1]-1, args[2], args[3], args[4], args[5]);
	if (offset !== undefined )
		date = new Date(date.getTime() + offset);
	return date;
}
function dateString(time) {
	return time.getFullYear() + '-' + (time.getMonth()+1) + '-' + time.getDate() + '-' +
		time.getHours() + '-' + time.getMinutes() + '-' + time.getSeconds();
}
function dashDate(date, filler) { // date-time with all '-'
	var c = date.replace('%20','-').replace(' ','-').split('-');
	for (var i = c.length; i < 6; i++)
		c.push(filler[i-3]);
	return c[0] + '-' + c[1] + '-' + c[2] + '-' + c[3] + '-' + c[4] + '-' + c[5];
}
function parseDates(fromQ, toQ) {
	if (toQ == undefined || toQ === null || toQ === "" || toQ.split('-').count < 2) // no valid to argument -> to = now
		this.toQ = dateString(new Date());
	else
		this.toQ = dashDate(toQ,['00','00','00']);
	if (fromQ == undefined || fromQ === null || fromQ === "" || fromQ.split('-').count < 2) // no valid from argument -> 12h before to
		this.fromQ = dashDate(dateString(makeDate(this.toQ, -12 * 3600 * 1000)));
	else
		this.fromQ = dashDate(fromQ,['23','59','59']);
}
function weekNr(d) {
	d = new Date(d);
	d.setHours(0,0,0);
	d.setDate(d.getDate() + 4 - (d.getDay()));
	var yearStart = new Date(d.getFullYear(),0,1);
	return Math.ceil(( ( (d - yearStart) / 86400000) + 1)/7);
}

MongoClient.connect(mongoUri, function(err, db) {
	// this is the first time we connect - if we get an error, just throw it
	if(err) throw(err);
	var collectionA = db.collection("tesla_aux");
	// get the last stored entry that describes the vehicles
	var query = {'vehicles': { '$exists': true } };
	var options = { 'sort': [['ts', 'desc']], 'limit': 1};
	collectionA.find(query, options).toArray(function(err, docs) {
		if (argv.verbose) console.dir(docs);
		if (docs.length === 0) {
			console.log("missing vehicles data in db, assuming Model S 60");
			capacity = 60;
		} else {
			if (docs.length > 1)
				console.log("congratulations, you have more than one Tesla Model S - this only supports your first car");
			vin = docs[0].vehicles.vin;
			var options = docs[0].vehicles.option_codes.split(',');
			for (var i = 0; i < options.length; i++) {
				if (optionText[options[i]] !== undefined)
					optionString += optionText[options[i]];
				if (options[i] == "PX01") {
					optionsString = optionsString.replace("PLUS", "+");
				}
				if (options[i].substring(0,2) == "BT") {
					if (options[i] == "BT85") {
						capacity = 85;
					} else if (options[i] == "BT60") {
						capacity = 60;
					} else if (options[i] == "BT40") {
						capacity = 60;
					}
					optionString += "<ul>";
				}
			}
			optionString = optionString.replace("PLUS", "");
			optionString += "</ul>";
			console.log(optionString);
		}
		if (argv.verbose) console.log("battery capacity", capacity);
	});
	query = {'guiSettings': { '$exists': true } };
	options = { 'sort': [['ts', 'desc']], 'limit': 1};
	collectionA.find(query,options).toArray(function(err, docs) {
		if (docs.length == 0) {
			console.log("missing GUI settings in db, assuming imperial");
			system = "imperial";
		} else {
			if (docs[0].guiSettings.gui_distance_units == "mi/hr") { // hey Tesla - that's a speed, not a distance
				system = "imperial";
			} else {
				system = "metric";
			}
		}
	});
	query = {'vehicleState': { '$exists': true } };
	options = { 'sort': [['ts', 'desc']], 'limit': 1};
	collectionA.find(query, options).toArray(function(err, docs) {
		if (docs.length == 0) {
			console.log("missing vehicleState settings in db");
		} else {
			var fwBuild = docs[0].vehicleState.car_version;
			if (fwBuild.substr(0,4) == "1.25")
				fwVersion = "4.3 ";
			else if (fwBuild.substr(0,4) == "1.31")
				fwVersion = "4.4 ";
			else if (fwBuild.substr(0,4) == "1.33")
				fwVersion = "4.5 ";
			else if (fwBuild.substr(0,4) == "1.35")
				fwVersion = "5.0 ";
			else if (fwBuild.substr(0,4) == "1.45")
				fwVersion = "5.6 ";
			fwVersion += "(" + fwBuild + ")";
		}
	});

});

if (argv.verbose) app.use(express.logger('dev'));

function check_chargestate( something, cb) {
    
        MongoClient.connect(mongoUri, function(err, db) {
		if(err) {
			console.log('error connecting to database:', err);
			return;
		}        
            var collection = db.collection('tesla_aux');
 

              // Locate all the entries using find
              collection.find({"chargeState": {$exists:true}, "chargeState.charging_state": {$exists:true} }).limit(2).sort({ts:-1}).toArray(function(err, results) {
               if(err) {throw err};
                 if (results.length == 2) {  
                    //console.dir(results);
                    var soc = results[0].chargeState.battery_level;
                    var current_chargestate = results[0].chargeState.charging_state;
                    var previous_chargestate = results[1].chargeState.charging_state;
                    var time_to_full_charge = results[0].chargeState.time_to_full_charge;
                    var time_to_full_charge = results[0].chargeState.time_to_full_charge;
                    var charger_voltage = parseInt(results[0].chargeState.charger_voltage);
                    var charger_phases = parseInt(results[0].chargeState.charger_phases);
                    var charger_actual_current = parseInt(results[0].chargeState.charger_actual_current);
                    var chargeKwh = Math.round((charger_voltage * charger_phases * charger_actual_current)/1000);

                    var multiplier = 1;

                    if (system == "metric") {multiplier = 1.609}

                    //db.close();

                    var returndata = [];
                    returndata[0] = soc;
                    returndata[1] = current_chargestate;
                    returndata[2] = previous_chargestate;
                    returndata[3] = secondsToHms(parseFloat(time_to_full_charge)*60*60);
                    returndata[4] = chargeKwh;
                    returndata[9] = Math.round(parseFloat(results[0].chargeState.battery_range)*multiplier);
                    returndata[10] = Math.round(parseFloat(results[0].chargeState.est_battery_range)*multiplier);
                    returndata[11] = Math.round(parseFloat(results[0].chargeState.ideal_battery_range)*multiplier);
                    returndata[12] = parseInt(results[0].chargeState.charger_voltage);
                    returndata[13] = parseInt(results[0].chargeState.charger_phases);
                    returndata[14] = parseInt(results[0].chargeState.charge_limit_soc);
                    returndata[15] = parseInt(results[0].chargeState.charger_pilot_current);
                    returndata[16] = results[0].chargeState.charge_energy_added;
                    returndata[17] = parseInt(results[0].chargeState.max_range_charge_counter);
                    returndata[18] = new Date(results[0].ts); //String(Math.round(results[0].ts / 1000) + date.getTimezoneOffset() * 60);


                    

                    collection.find({"climateState": {$exists:true}, "climateState.outside_temp": {$exists:true} }).limit(1).sort({ts:-1}).toArray(function(err, results) {
                         if(err) {throw err};
                         var outside_temp = results[0].climateState.outside_temp;
                         var inside_temp = results[0].climateState.inside_temp;
                         var is_auto_conditioning_on = results[0].climateState.is_auto_conditioning_on;
                         
                         returndata[5] = outside_temp;
                         returndata[6] = inside_temp;
                         returndata[7] = is_auto_conditioning_on;
                         returndata[8] = results[0].climateState.driver_temp_setting + " - " + results[0].climateState.passenger_temp_setting;
                         var hvactime = new Date(results[0].ts);
                         var ts2 = "";

                         ts2 = "" // + ":" + hvactime.getMinutes + ";" + hvactime.getSeconds;

                         returndata[19]  = hvactime;
                         returndata[20] = results[0].climateState.fan_status;

                         cb(returndata); //"SOC: " + soc + " CS: " + current_chargestate + " PCS " + previous_chargestate);
                         db.close();
                    });


                    
                    //Returning array (0 = SOC, 1 = Current ChargeState, 2 = previous chargestate)
                    //cb(returndata); //"SOC: " + soc + " CS: " + current_chargestate + " PCS " + previous_chargestate);
                 }
                // Let's close the db
                
              });
       
      });
            
            // 
}

app.get('/', ensureAuthenticated, function(req, res) {
	// friendly welcome screen
	fs.readFile(__dirname + "/welcome.html", "utf-8", function(err, data) {
		if (err) throw err;

        var SOC = 0;

        check_chargestate('someone', function (result) {
            var SOC = result[0] + '';
            var CHARGE_STATE = result[1];
            var CHARGE_TTE = result[3];
            var CHARGE_KWH = result[4];
            var INSIDE_TEMP = result[5];
            var OUTSIDE_TEMP = result[6];
            var is_auto_conditioning_on = result[7];
            var fan_status = result[20];

            var TEMP_SETTINGS = result[8];
            var RANGE =  result[9];
            var EST_RANGE =  result[10];
            var IDEAL_RANGE =  result[11];
            var VOLTAGE =  result[12];
            var PHASES =  result[13];
            var CHARGELIMIT =  result[14];
            var PILOTCURRENT =  result[15];
            var ENERGYADDED =  result[16];
            var RANGECHARGES =  result[17];
            var BATT_UPDATE =  result[18];
            var HVAC_UPDATE =  result[19];

            if (fan_status == 0) {  is_auto_conditioning_on = "OFF" }
            else { is_auto_conditioning_on = "ON" }

            //console.dir(SOC);
            res.send(data.replace("MAGIC_NAV",nav)
			 .replace("MAGIC_OPTIONS", optionString)
			 .replace("MAGIC_VIN", vin)
			 .replace("MAGIC_FIRMWARE_VERSION", fwVersion)
			 .replace("MAGIC_DISPLAY_SYSTEM", system)
             .replace("MAGIC_DISPLAY_SOC", SOC)
             .replace("MAGIC_HVAC_STATUS", is_auto_conditioning_on)
             .replace("MAGIC_HVAC_TEMP_SET", TEMP_SETTINGS)
             .replace("MAGIC_HVAC_TEMP_OUTSIDE", OUTSIDE_TEMP)
             .replace("MAGIC_HVAC_TEMP_INSIDE", INSIDE_TEMP)
             .replace("MAGIC_BATT_STATUS", CHARGE_STATE)
             .replace("MAGIC_BATT_CHARGE_KWH", CHARGE_KWH)
             .replace("MAGIC_BATT_CHARGE_LEFT", CHARGE_TTE)
             .replace("MAGIC_BATT_RATED", RANGE)
             .replace("MAGIC_BATT_ESTIMATED", EST_RANGE)
             .replace("MAGIC_BATT_IDEAL", IDEAL_RANGE)
             .replace("MAGIC_BATT_VOLTAGE", VOLTAGE)
             .replace("MAGIC_BATT_PHASES", PHASES)
             .replace("MAGIC_BATT_CHARGE_LIMIT", CHARGELIMIT)
             .replace("MAGIC_BATT_PILOT_CURRENT", PILOTCURRENT)
             .replace("MAGIC_BATT_ENERGY_ADDED", ENERGYADDED)
             .replace("MAGIC_BATT_RANGE_CHARGES", RANGECHARGES)
             .replace("MAGIC_BATT_UPDATED", BATT_UPDATE)
             .replace("MAGIC_HVAC_TIME", HVAC_UPDATE)
             .replace("MAGIC_TEMP", 'Inside temp: ' + INSIDE_TEMP + ', Outside temp: ' + OUTSIDE_TEMP + ', Aircon on: ' + is_auto_conditioning_on)
             .replace("MAGIC_CHARGE_STATE", CHARGE_STATE + ' @ ' + CHARGE_KWH + ' kWh - Time Remaining: ' + CHARGE_TTE  )
             .replace("MAGIC_S20", SOC));
        });

		
	});
});

app.get('/stopdata', function(req, res) {
	// friendly welcome screen
	fs.readFile(__dirname + "/stopdata.html", "utf-8", function(err, data) {
		if (err) throw err;
		res.send(data.replace("MAGIC_NAV",nav));

var exec = require('child_process').exec,
    child;

child = exec('net stop NodeStreaming',
    function (error, stdout, stderr) {
        if(stdout!==''){
            console.log('---------stdout: ---------\n' + stdout);
        }
        if(stderr!==''){
            console.log('---------stderr: ---------\n' + stderr);
        }
        if (error !== null) {
            console.log('---------exec error: ---------\n[' + error+']');
        }
    });



	});
});

app.get('/startdata', function(req, res) {
	// friendly welcome screen
	fs.readFile(__dirname + "/startdata.html", "utf-8", function(err, data) {
		if (err) throw err;
		res.send(data.replace("MAGIC_NAV",nav));

var exec = require('child_process').exec,
    child;

child = exec('net start NodeStreaming',
    function (error, stdout, stderr) {
        if(stdout!==''){
            console.log('---------stdout: ---------\n' + stdout);
        }
        if(stderr!==''){
            console.log('---------stderr: ---------\n' + stderr);
        }
        if (error !== null) {
            console.log('---------exec error: ---------\n[' + error+']');
        }
    });



	});
});




app.get('/getdata', ensureAuthenticated, function (req, res) {
	var ts, options, vals;
	if (argv.verbose) console.log('/getdata with',req.query.at);
	MongoClient.connect(mongoUri, function(err, db) {
		if(err) {
			console.log('error connecting to database:', err);
			return;
		}
		collection = db.collection("tesla_stream");
		if (req.query.at === null) {
			if (argv.verbose) console.log("why is there no 'at' parameter???");
			return;
		}
		// get the data at time 'at'
		ts = +req.query.at;
		options = { 'sort': [['ts', 'desc']], 'limit': 1};
		collection.find({"ts": {"$lte": +ts}}, options).toArray(function(err,docs) {
			if (argv.verbose) console.log("got datasets:", docs.length);
			if (docs.length === 0) {
				// that shouldn't happen unless the database is empty...
				console.log("no data found for /getdata request at time", console.log(new Date(+ts).toString));
				return;
			}
			res.setHeader("Content-Type", "application/json");
			vals = docs[0].record.toString().replace(",,",",0,").split(",");
			res.write("[" + JSON.stringify(vals) + "]", "utf-8");
			res.end();
			db.close();
		});
	});
});

app.get('/storetrip', ensureAuthenticated, function(req, res) {
	MongoClient.connect("mongodb://127.0.0.01:27017/" + argv.db, function(err, db) {
		if (err) {
			console.log('error connecting to database:', err);
			return;
		}
		collection = db.collection("trip_data");
		collection.remove({ 'dist': '-1'}, function(err,docs) {
			collection.insert(req.query, { 'safe': true }, function(err,docs) {
				if (err) {
					res.send(err);
				} else {
					res.send("OK");
				}
			});
		});
	});
});

app.get('/getlasttrip', ensureAuthenticated, function(req, res) {
	MongoClient.connect("mongodb://127.0.0.01:27017/" + argv.db, function(err, db) {
		if (err) {
			console.log('error connecting to database:', err);
			return;
		}
		collection = db.collection("trip_data");
		var options = { 'sort': [['chargeState.battery_range', 'desc']] };
		collection.find({},{ 'sort': [['from', 'desc']], 'limit': 1 }).toArray(function(err,docs) {
			console.log(docs);
			res.setHeader("Content-Type", "application/json");
			res.send(docs);
		});
	});
});

app.get('/update', ensureAuthenticated, function (req, res) {
	// we don't keep the database connection as that has caused occasional random issues while testing
	if (!started)
		return;
	MongoClient.connect(mongoUri, function(err, db) {
		if(err) {
			console.log('error connecting to database:', err);
			return;
		}
		collection = db.collection("tesla_stream");
		if (req.query.until === null) {
			console.log("why is there no 'until' parameter???");
			return;
		}
		// get the data until 'until'
		// but not past the end of the requested segment and not past the current time
		var endTime = +req.query.until;
		if (to && +endTime > +to)
			endTime = +to;
		var currentTime = new Date().getTime();
		if (+endTime > +currentTime)
			endTime = +currentTime;
		collection.find({"ts": {"$gt": +lastTime, "$lte": +endTime}}).toArray(function(err,docs) {
			if (argv.verbose) console.log("got datasets:", docs.length);
			if (docs.length === 0) {
				// create one dummy entry so the map app knows the last time we looked at
				docs = [ { "ts": +endTime, "record": [ +lastTime+"" ,"0","0","0","0","0","0","0","0","0","0","0"]} ];
			}
			res.setHeader("Content-Type", "application/json");
			res.write("[", "utf-8");
			var comma = "";
			docs.forEach(function(doc) {
				// the tesla streaming service replaces a few items with "" when the car is off
				// the reg exp below replaces the two that are numerical with 0 (the shift_state stays unchanged)
				var vals = doc.record.toString().replace(",,",",0,").split(",");
				res.write(comma + JSON.stringify(vals), "utf-8");
				lastTime = +doc.ts;
				comma = ",";
			});
			res.end("]", "utf-8");
			if (!argv.silent) {
				var showTime = new Date(lastTime);
				console.log("last timestamp:", lastTime, showTime.toString());
			}
			db.close();
		});
	});
});

app.get('/map', ensureAuthenticated, function(req, res) {
	var params = "";
	if (req.query.lang !== undefined)
		params = "&lang=" + req.query.lang;
	if (req.query.metric === "true")
		params += "&metric=true";
	var dates = new parseDates(req.query.from, req.query.to);
	from = makeDate(dates.fromQ);
	to = makeDate(dates.toQ);
	if (req.query.speed !== null && req.query.speed !== "" && req.query.speed <= 120 && req.query.speed >= 1)
		speedup = req.query.speed * 2000;
	if (req.query.to === undefined || req.query.to.split('-').length < 6 ||
	    req.query.from === undefined || req.query.from.split('-').length < 6) {
		var speedQ = speedup / 2000;
		res.redirect('/map?from=' + dates.fromQ + '&to=' + dates.toQ + '&speed=' + speedQ.toFixed(0) + params);
		return;
	}
	MongoClient.connect(mongoUri, function(err, db) {
		if(err) {
			console.log('error connecting to database:', err);
			return;
		}
		collection = db.collection("tesla_stream");
		var searchString = {$gte: +from, $lte: +to};
		collection.find({"ts": searchString}).limit(1).toArray(function(err,docs) {
			if (argv.verbose) console.log("got datasets:", docs.length);
			docs.forEach(function(doc) {
				var record = doc.record;
				var vals = record.toString().replace(",,",",0,").split(/[,\n\r]/);
				lastTime = +vals[0];
				res.setHeader("Content-Type", "text/html");
				fs.readFile(__dirname + "/map.html", "utf-8", function(err, data) {
					if (err) throw err;
					var response = data.replace("MAGIC_APIKEY", apiKey)
						.replace("MAGIC_FIRST_LOC", vals[6] + "," + vals[7])
						.replace("MAGIC_NAV", nav)
						.replace("MAGIC_DISPLAY_SYSTEM", '"' + system + '"');
					res.end(response, "utf-8");
				});
			});
			db.close();
			started = true;
		});
	});
	if (!argv.silent) console.log('done sending the initial page');
});

app.get('/energy', ensureAuthenticated, function(req, res) {
	var path = req.path;
	var params = "";
	if (req.query.lang !== undefined)
		params = "&lang=" + req.query.lang;
	if (req.query.metric === "true")
		params += "&metric=true";
	var dates = new parseDates(req.query.from, req.query.to);
	from = makeDate(dates.fromQ);
	to = makeDate(dates.toQ);
	if (req.query.to === undefined || req.query.to.split('-').length < 6 ||
	    req.query.from === undefined || req.query.from.split('-').length < 6) {
		res.redirect('/energy?from=' + dates.fromQ + '&to=' + dates.toQ + params);
		return;
	}
	// don't deliver more than 10000 data points (that's one BIG screen)
	var halfIncrement =  Math.round((+to - +from) / 20000);
	var increment = 2 + halfIncrement;
	var outputE = "", outputS = "", outputSOC = "", outputRange = "", firstDate = 0, lastDate = 0;
	var minE = 1000, minS = 1000, minSOC = 1000;
	var maxE = -1000, maxS = -1000, maxSOC = -1000;
	var gMaxE = -1000, gMaxS = -1000;
	var gMinE = 1000, gMinS = 1000;
	var cumulE = 0, cumulR = 0, cumulES, cumulRS, prevTS;
	MongoClient.connect(mongoUri, function(err, db) {
		var speed, energy, soc, vals;
		if(err) {
			console.log('error connecting to database:', err);
			return;
		}
		res.setHeader("Content-Type", "text/html");
		collection = db.collection("tesla_stream");
		collection.find({"ts": {$gte: +from, $lte: +to}}).toArray(function(err,docs) {
			docs.forEach(function(doc) {
				vals = doc.record.toString().replace(",,",",0,").split(",");
				speed = parseInt(vals[1]);
				energy = parseInt(vals[8]);
				soc = parseInt(vals[3]);
				if (firstDate === 0) {
					firstDate = lastDate = prevTS = doc.ts;
					outputE = "[" + (+from) + ",0]";
					outputS = "[" + (+from) + ",0]";
					outputSOC = "[" + (+from) + "," + soc + "],null";
				}
				if (doc.ts >= lastDate) {
					if (doc.ts > lastDate + increment) {
						if (maxE != -1000) {
							outputE += ",[" + (+lastDate + halfIncrement) + "," + maxE + "]";
							outputE += ",[" + (+lastDate + increment) + "," + minE + "]";
						}
						if (maxS != -1000)
							outputS += ",[" + (+lastDate + halfIncrement) + "," + (+maxS + minS) / 2 + "]";
						if (maxSOC != -1000)
							outputSOC += ",[" + (+lastDate + halfIncrement)  + "," + (+maxSOC + minSOC) / 2 + "]";
						lastDate = doc.ts;
						if (+maxE > +gMaxE) gMaxE = maxE;
						if (+minE < +gMinE) gMinE = minE;
						if (+maxS > +gMaxS) gMaxS = maxS;
						maxE = maxS = maxSOC = -1000;
						minE = minS = minSOC = 1000;
					}
					if (energy > 0) cumulE += energy * (doc.ts - prevTS);
					if (energy < 0) cumulR += energy * (doc.ts - prevTS);
					if (energy > maxE) maxE = energy;
					if (energy < minE) minE = energy;
					if (speed > maxS) maxS = speed;
					if (speed < minS) minS = speed;
					if (soc > maxSOC) maxSOC = soc;
					if (soc < minSOC) minSOC = soc;
					prevTS = doc.ts;
				}
			});
			cumulE = cumulE / 3600000;
			cumulR = cumulR / 3600000;
			if (cumulE > 1) {
				cumulES = cumulE.toFixed(1) + "kWh";
				cumulRS = (-cumulR).toFixed(1) + "kWh";
			} else {
				cumulES = (cumulE * 1000).toFixed(0) + "Wh";
				cumulRS = (-cumulR * 1000).toFixed(0) + "Wh";
			}
			var chartEnd = lastDate;

			// now look for data in the aux collection

			collection = db.collection("tesla_aux");
			var maxAmp = 0, maxVolt = 0, maxMph = 0, maxPower = 0;
			var outputAmp = "", outputVolt = "", outputPower = "";
			var amp, volt, power;
			lastDate = +from;
			collection.find({"chargeState": {"$exists": true},
					 "ts": {$gte: +from, $lte: +to}}).toArray(function(err,docs) {
				if (argv.verbose) console.log("Found " + docs.length + " entries in aux DB");
				outputAmp = "[" + (+firstDate) + ",0]";
				outputVolt = "[" + (+firstDate) + ",0]";
				outputPower = "[" + (+firstDate) + ",0]";
				comma = "";
				docs.forEach(function(doc) {
					amp = volt = 0;
					if(doc.chargeState.charging_state === 'Charging') {
						if (doc.chargeState.charger_actual_current !== undefined) {
							if (doc.chargeState.charger_actual_current !== 0) {
								amp = doc.chargeState.charger_actual_current;
							} else {
								amp = doc.chargeState.battery_current;
							}
							outputAmp += ",[" + doc.ts + "," + amp + "]";
							lastDate = doc.ts;
						}
						if (doc.chargeState.charger_voltage !== undefined) {
							volt = doc.chargeState.charger_voltage;
							outputVolt += ",[" + doc.ts + "," + volt + "]";
							lastDate = doc.ts;
						}
						if (lastDate == doc.ts) { // we had valid values
							power = parseFloat(volt) * parseFloat(amp) / 1000;
							outputPower += ",[" + doc.ts + "," + power.toFixed(1) + "]";
							if (power > maxPower) {
								maxPower = power;
								maxAmp = amp;
								maxVolt = volt;
								maxMph = doc.chargeState.charge_rate;
							}
						}
					} else if (doc.chargeState.charging_state === 'Disconnected' ||
						   doc.chargeState.charging_state === 'Complete' ||
						   doc.chargeState.charging_state === 'Pending' ||
						   doc.chargeState.charging_state === 'Starting' ||
						   doc.chargeState.charging_state === 'Stopped') {
						outputAmp += ",[" + doc.ts + ",0]";
						outputVolt += ",[" + doc.ts + ",0]";
						outputPower += ",[" + doc.ts + ",0]";
					}
					if (doc.chargeState.battery_range !== undefined) {
						outputRange += comma + "[" + doc.ts + "," + doc.chargeState.battery_range + "]";
						comma = ",";
					}
				});
				outputAmp += ",[" + (lastDate + 60000) + ",0]";
				outputVolt += ",[" + (lastDate + 60000) + ",0]";
				outputPower += ",[" + (lastDate + 60000) + ",0]";
				outputAmp += ",[" + (+chartEnd) + ",0]";
				outputVolt += ",[" + (+chartEnd) + ",0]";
				outputPower += ",[" + (+chartEnd) + ",0]";

				db.close();
				fs.readFile(__dirname + "/energy.html", "utf-8", function(err, data) {
					if (err) throw err;
					var fD = new Date(firstDate);
					var startDate = (fD.getMonth() + 1) + "/" + fD.getDate() + "/" + fD.getFullYear();
					gMinE = +gMinE - 10;
					gMaxE = +gMaxE + 10;
					if (2 * gMaxS > +gMaxE) {
						gMaxS = +gMaxS + 5;
						gMaxE = 2 * gMaxS;
					} else {
						gMaxS = gMaxE / 2;
					}
					gMinS = gMinE / 2;
					var response = data.replace("MAGIC_NAV", nav)
						.replace("MAGIC_ENERGY", outputE)
						.replace("MAGIC_SPEED", outputS)
						.replace("MAGIC_SOC", outputSOC)
						.replace("MAGIC_START", startDate)
						.replace("MAGIC_MAX_ENG", gMaxE)
						.replace("MAGIC_MIN_ENG", gMinE)
						.replace("MAGIC_MAX_SPD", gMaxS)
						.replace("MAGIC_MIN_SPD", gMinS)
						.replace("MAGIC_CUMUL_E", cumulES)
						.replace("MAGIC_CUMUL_R", cumulRS)
						.replace("MAGIC_VOLT", outputVolt)
						.replace("MAGIC_AMP", outputAmp)
						.replace("MAGIC_POWER", outputPower)
						.replace("MAGIC_RANGE", outputRange)
						.replace("MAGIC_MAX_VOLT", maxVolt)
						.replace("MAGIC_MAX_AMP", maxAmp)
						.replace("MAGIC_MAX_KW", maxPower.toFixed(1))
						.replace("MAGIC_MAX_MPH", maxMph)
						.replace("MAGIC_CAPACITY", capacity)
						.replace("MAGIC_DISPLAY_SYSTEM", '"' + system + '"');
					res.end(response, "utf-8");
					if (argv.verbose) console.log("delivered", outputSOC.length,"records and", response.length, "bytes");
				});
			});
		});
	});
});

function countCharge(ts) {
	if (!countCharge.start)
		countCharge.start = ts;
}
function stopCountingCharge(ts) {
	if (countCharge.start && ts - countCharge.start > 60000) // at least a minute
		countCharge.chargeInt.push([countCharge.start,ts]);
	countCharge.start = null;
}
function countVamp(ts) {
	if (!countVamp.start)
		countVamp.start = ts;
}
function stopCountingVamp(ts) {
	if (countVamp.start && ts - countVamp.start > 60000) // at least a minute
		countVamp.vampInt.push([countVamp.start,ts]);
	countVamp.start = null;
}
function calculateDelta(d1, d2) {
	var cS1 = d1.chargeState, cS2 = d2.chargeState;
	if (!cS1 || !cS2 || !cS1.battery_level || !cS2.battery_level || cS2.battery_range > cS1.battery_range) {
		return 0;
	}
	// var ratedWh = 5 * ((cS1.battery_level * capacity / cS1.battery_range) + (cS2.battery_level * capacity / cS2.battery_range));
	// let's use the data that we seem to are converging on in the forums instead:
	var ratedWh = (capacity == 85) ? 286 : 267;
	var delta = ratedWh * (cS1.battery_range - cS2.battery_range);
//	if (argv.verbose) { // great for debugging
//		console.log(new Date(d1.ts), new Date(d2.ts), "ratedWh", ratedWh.toFixed(1),
//			    "delta range", (cS1.battery_range - cS2.battery_range).toFixed(1) ,"delta", delta.toFixed(1));
//	}
	return delta / 1000;
}

app.get('/test', ensureAuthenticated, function(req, res) {
	MongoClient.connect(mongoUri, function(err, db) {
		if(err) {
			console.log('error connecting to database:', err);
			return;
		}
		var output = "";
		var collection = db.collection("tesla_aux");
		var options = { 'sort': [['chargeState.battery_range', 'desc']] };
		collection.find({"chargeState": {$exists: true}}).toArray(function(err,docs) {
			var comma = "";
			docs.forEach(function(doc) {
				if (doc.chargeState.battery_level !== undefined) {
					output += comma + "\n[" + doc.chargeState.battery_level + "," + doc.chargeState.battery_range + "]";
					comma = ',';
				}
			});
			db.close();
			fs.readFile(__dirname + "/test.html", "utf-8", function(err, data) {
				if (err) throw err;
				res.send(data.replace("MAGIC_TEST", output))
			});
		});
	});
});
app.get('/stats', ensureAuthenticated, function(req, res) {
	var debugStartTime = new Date().getTime();
	var path = req.path;
	var params = "";
	if (req.query.lang !== undefined)
		params = "&lang=" + req.query.lang;
	if (req.query.metric === "true")
		params += "&metric=true";
	var dates = new parseDates(req.query.from, req.query.to);
	countVamp.vampInt = [];
	countCharge.chargeInt = [];
	countVamp.start = null;
	from = makeDate(dates.fromQ);
	to = makeDate(dates.toQ);
	if (req.query.to === undefined || req.query.to.split('-').length < 6 ||
	    req.query.from === undefined || req.query.from.split('-').length < 6) {
		res.redirect('/stats?from=' + dates.fromQ + '&to=' + dates.toQ + params);
		return;
	}
	var outputD = "", outputC = "", outputA = "", comma, firstDate = 0, lastDay = 0, lastDate = 0, distHash = {}, useHash = {};
	var outputWD = "", outputWC = "", outputWA = "", commaW, distWHash = {}, useWHash ={};
	MongoClient.connect(mongoUri, function(err, db) {
		if(err) {
			console.log('error connecting to database:', err);
			return;
		}
		res.setHeader("Content-Type", "text/html");
		collection = db.collection("tesla_stream");
		if (argv.verbose)
			console.log("starting DB request after", new Date().getTime() - debugStartTime, "ms");
		collection.find({"ts": {"$gte": from.getTime(), "$lte": to.getTime()}}).toArray(function(err,docs) {
			if (argv.verbose)
				console.log("processing data after", new Date().getTime() - debugStartTime, "ms");
			// this is really annoying; the values from the database frequently aren't sorted by
			// timestamp, even if you didn't edit the data. So we need to sort here - the mongoDB
			// sort function fails if the amount of data becomes too large :-(
			docs.sort(function(a,b){return a.ts - b.ts;});
			var vals = [];
			var odo, energy, state, soc;
			var dist, kWh, ts, midnight, used;
			var week, distW = 0, kWhW = 0, usedW = 0, chargeW = 0;
			var startOdo, charge, minSOC, maxSOC, increment, kWs;
			if (docs === null) {
				if (argv.verbose) {
					console.log(err);
					console.log("no output for stream from", +from, "to", +to);
				}
				return;
			}
			function updateWValues(f) {
				var lw = weekNr(lastDate);
				var ld = new Date(lastDate);
				if (lw == week && f !== true)
					return;
				var wts = ld.getTime() - 24 * 3600 * 1000 * ld.getDay() - 3600 * 1000 * ld.getHours()
							- 60 * 1000 * ld.getMinutes() - 1000 * ld.getSeconds() - ld.getMilliseconds();
				outputWD += commaW + "[" + wts + "," + distW + "]";
				outputWC += commaW + "[" + wts + "," + chargeW + "]";
				commaW = ",";
				distWHash[wts+""] = distW;
				useWHash[wts+""] = kWhW;
				distW = chargeW = distW = kWhW = 0;
			}
			function updateValues() {
				stopCountingVamp(lastDate);
				stopCountingCharge(lastDate);
				charge += increment * capacity / 100;
				chargeW += charge;
				dist = odo - startOdo;
				distW += dist;
				kWh = kWs / 3600;
				kWhW += kWh;
				ts = new Date(lastDate);
				midnight = new Date(ts.getFullYear(), ts.getMonth(), ts.getDate(), 0, 0, 0);
				outputD += comma + "[" + (+midnight)  + "," + dist + "]";
				outputC += comma + "[" + (+midnight)  + "," + charge + "]";
				distHash[midnight.getTime()+""] = dist;
				useHash[midnight.getTime()+""] = kWh;
			}
			docs.forEach(function(doc) {
				var day = new Date(doc.ts).getDay();
				week = weekNr(doc.ts);
				vals = doc.record.toString().replace(",,",",0,").split(",");
				odo = parseFloat(vals[2]);
				soc = parseFloat(vals[3]); // sadly, this is an integer today :-(
				energy = parseInt(vals[8]);
				state = vals[9];
				if (firstDate === 0) {
					firstDate = doc.ts - 1;
					lastDay = day;
					startOdo = odo;
					minSOC = 101; maxSOC = -1; kWs = 0; charge = 0; increment = 0; comma = ""; commaW = "";
				}
				if (day != lastDay) {
					updateValues();
					updateWValues();
					lastDay = day;
					startOdo = odo;
					minSOC = 101; maxSOC = -1; kWs = 0; charge = 0; increment = 0; comma = ",";
				}
				// this is crude - it would be much better to get this from
				// the aux database and use the actual charge info
				if (state != 'R' && state != 'D') { // we are not driving
					if (energy < 0) { // parked & charging
						stopCountingVamp(doc.ts);
						countCharge(doc.ts);
						if (soc < minSOC) minSOC = soc;
						if (soc > maxSOC) maxSOC = soc;
						increment = maxSOC - minSOC;
					} else { // parked & consuming
						countVamp(doc.ts);
						stopCountingCharge(doc.ts);
						// if we were charging before, add the estimate to the total
						// this a quite coarse as SOC is in full percent - bad granularity
						if (increment > 0) {
							charge += increment * capacity / 100;
							increment = 0; minSOC = 101; maxSOC = -1;
						}
					}
				} else {
					// we're driving - add up the energy used / regen
					if (lastDate > 0)
						kWs += (doc.ts - lastDate) / 1000 * (energy - 0.12); // this correction is needed to match in car data???
					stopCountingVamp(doc.ts);
				}
				lastDate = doc.ts;
			});

			// we still need to add the last day
			updateValues();
			updateWValues(true);
			// now analyze the charging data
			collection = db.collection("tesla_aux");
			collection.find({"chargeState": {$exists: true}, "ts": {$gte: +from, $lte: +to}}).toArray(function(err,docs) {
				var i = 0, vampirekWh = 0, day, lastDay = -1, lastDate = null, comma = "", outputY = "", outputWY = "", commaW = "";
				var j = 0, chargekWh = 0, outputCN = "", usedkWh = 0, outputUsed = "", outputWCN = "", outputWUsed = "";
				var chargekWhW = 0, vampirekWhW = 0, usedW = 0;
				var vState1 = null;
				var cState1 = null;
				var uState1 = null;
				var lastDoc = null;
				var maxI = countVamp.vampInt.length;
				var maxJ = countCharge.chargeInt.length;
				function updateChargeWValues(f) {
					var ld, lw = weekNr(lastDate);
					if (lw == week && f !== true)
						return;
					// if we force the display we need to get the week from the last doc
					// that we had, otherwise we are showing last weeks data, so get it from lastDate
					if (f === true) {
						if (lastDoc !== null && lastDoc.ts !== undefined) {
							ld = new Date(lastDoc.ts);
						} else { // we have no data for this range
							outputWY += commaW + "null";
							outputWCN += commaW + "null";
							outputWUsed += commaW + "null";
							commaW = ',';
							return;
						}
					} else {
						ld = new Date(lastDate);
					}
					var wts = ld.getTime() - 24 * 3600 * 1000 * ld.getDay() - 3600 * 1000 * ld.getHours()
								- 60 * 1000 * ld.getMinutes() - 1000 * ld.getSeconds() - ld.getMilliseconds();
					outputWY += commaW + "[" + wts + "," + vampirekWhW + "]";
					outputWCN += commaW + "[" + wts + "," + chargekWhW + "]";
					outputWUsed += commaW + "[" + wts + "," + usedW + "]";
					dist = distWHash[wts+""];
					if (dist > 0) {
						outputWA += commaW + "[" + wts + "," + 1000 * usedW / dist + "]";
					} else {
						outputWA += commaW + "null";
					}
					commaW = ",";
					chargekWhW = 0, vampirekWhW = 0, usedW = 0;
				}
				function updateChargeValues(d) {
					if (vState1) {
						vampirekWh += calculateDelta(vState1, d);
						vState1 = d;
					}
					if (cState1) {
						chargekWh += calculateDelta(d, cState1);
						cState1 = d;
					}
					if (uState1) {
						usedkWh += calculateDelta(uState1, lastDoc);
						uState1 = d;
					}
					vampirekWhW += vampirekWh;
					chargekWhW += chargekWh;
					ts = new Date(lastDate);
					midnight = new Date(ts.getFullYear(), ts.getMonth(), ts.getDate(), 0, 0, 0).getTime()+"";
					outputY += comma + "[" + midnight + "," + vampirekWh + "]";
					outputCN += comma + "[" + midnight + "," + chargekWh + "]";
					// depending on what failed, sometimes the discrete integral is better (missing charge data)
					// but usually the charge data derived usage is more accurate. Picking the higher of the two
					// seems to get us the best results.
					used = Math.max(useHash[midnight+""], usedkWh);
					// used = usedkWh;
					usedW += usedkWh;
					outputUsed += comma + "[" + midnight + "," + used + "]";
					dist = distHash[midnight+""];
					if (dist > 0) {
						outputA += comma + "[" + midnight  + "," + 1000 * used / dist + "]";
					} else {
						outputA += comma + "null";
					}
					comma = ",";
				}
				docs.forEach(function(doc) {
					day = new Date(doc.ts).getDay();
					week = weekNr(doc.ts);
					if (!doc || !doc.chargeState || !doc.chargeState.battery_level)
						return;
					if (day != lastDay) {
						if (lastDate) {
							updateChargeValues(doc);
							updateChargeWValues();
						}
						lastDate = doc.ts;
						lastDay = day;
						vampirekWh = 0;
						chargekWh = 0;
						usedkWh = 0;
					}
					lastDoc = doc;
					if (uState1 === null && vState1 === null && cState1 === null)
						uState1 = doc;
					if (i < maxI && vState1 === null && doc.ts >= countVamp.vampInt[i][0]) {
						if (uState1 !== null && doc !== null && uState1.ts < doc.ts) {
							usedkWh += calculateDelta(uState1, doc);
						}
						uState1 = null;
						vState1 = doc;
					}
					if (i < maxI && doc.ts >= countVamp.vampInt[i][1]) {
						vampirekWh += calculateDelta(vState1, doc);
						vState1 = null;
						uState1 = doc;
						i++;
					}
					if (j < maxJ && cState1 === null && doc.ts >= countCharge.chargeInt[j][0]) {
						if (uState1 !== null && doc !== null && uState1.ts < doc.ts) {
							usedkWh += calculateDelta(uState1, doc);
						}
						uState1 = null;
						cState1 = doc;
					}
					if (j < maxJ && doc.ts >= countCharge.chargeInt[j][1]) {
						chargekWh += calculateDelta(doc, cState1);
						cState1 = null;
						uState1 = doc;
						j++;
					}
				});
				updateChargeValues(lastDoc);
				updateChargeWValues(true);
				db.close();
				fs.readFile(__dirname + "/stats.html", "utf-8", function(err, data) {
					if (err) throw err;
					var fD = new Date(firstDate);
					var startDate = (fD.getMonth() + 1) + "/" + fD.getDate() + "/" + fD.getFullYear();
					var response = data
						.replace("MAGIC_NAV", nav)
						.replace("MAGIC_DISTANCE", outputD)
						.replace("MAGIC_WDISTANCE", outputWD)
						.replace("MAGIC_CHARGE", outputCN)
						.replace("MAGIC_WCHARGE", outputWCN)
						.replace("MAGIC_AVERAGE", outputA)
						.replace("MAGIC_WAVERAGE", outputWA)
						.replace("MAGIC_KWH", outputUsed)
						.replace("MAGIC_WKWH", outputWUsed)
						.replace("MAGIC_VKWH", outputY)
						.replace("MAGIC_WVKWH", outputWY)
						.replace("MAGIC_START", startDate)
						.replace("MAGIC_DISPLAY_SYSTEM", '"' + system + '"');
					res.end(response, "utf-8");
					if (argv.verbose)
						console.log("total processing time", new Date().getTime() - debugStartTime, "ms");
				});

			});
		});
	});
});

app.get('/trip', ensureAuthenticated, function(req, res) {
	res.setHeader("Content-Type", "text/html");
	fs.readFile(__dirname + "/trip.html", "utf-8", function(err, data) {
		if (err) throw err;
		res.end(data.replace("MAGIC_NAV", nav), "utf-8");
	});
});

//ADDED KSE
app.post('/scheduler', ensureAuthenticated, function(req, res) {
    //console.dir(req);


    var safe = 0;
    var pluggedin = 0;
    var notify = 0;
    var active = 0;

    var MongoClient = require('mongodb').MongoClient
      , format = require('util').format;

      MongoClient.connect('mongodb://127.0.0.1:27017/' +argv.db , function(err, db) {
        if(err) {throw err};

        var collection = db.collection('tesla_scheduler');

            for (var i=1;i<21;i++) {
                var postrecord = {};
                console.dir([i] + "<br>");
                //Set record to update and populate fields with value
                postrecord["_id"] = i;
                postrecord["SCHEDULERID"] = i;
                if (req.param([i]+'_Active')) { postrecord["Active"] = 1;} else {postrecord["Active"] = 0;}
                if (req.param([i]+'_MON')) { postrecord["MON"] = 1;}
                if (req.param([i]+'_TUE')) { postrecord["TUE"] = 1;}
                if (req.param([i]+'_WED')) { postrecord["WED"] = 1;}
                if (req.param([i]+'_THU')) { postrecord["THU"] = 1;}
                if (req.param([i]+'_FRI')) { postrecord["FRI"] = 1;}
                if (req.param([i]+'_SAT')) { postrecord["SAT"] = 1;}
                if (req.param([i]+'_SUN')) { postrecord["SUN"] = 1;}
                if (req.param([i]+'_SAFE')) { postrecord["SAFE"] = 1;} else { postrecord["SAFE"] = 0;}
                if (req.param([i]+'_PLUGGEDIN')) { postrecord["PLUGGEDIN"] = 1;} else { postrecord["PLUGGEDIN"] = 0;}
                if (req.param([i]+'_PLUGGEDINDISREGARD')) { postrecord["PLUGGEDINDISREGARD"] = 1;} else { postrecord["PLUGGEDINDISREGARD"] = 0;}
                if (req.param([i]+'_NOTIFY')) { postrecord["NOTIFY"] = 1;} else { postrecord["NOTIFY"] = 0;}
                if (req.param([i]+'_TIME')) { postrecord["TIME"] = req.param(i+'_TIME');}
                if (req.param([i]+'_COMMAND')) { postrecord["COMMAND"] = req.param(i+'_COMMAND');}
                if (req.param([i]+'_TEMPPERCENT')) { postrecord["TEMPPERCENT"] = req.param(i+'_TEMPPERCENT');}
                if (!req.param([i]+'_TEMPPERCENT')) { postrecord["TEMPPERCENT"] = "" ;}
                if (req.param('SafeAbove')) { postrecord["SAFEABOVE"] = req.param('SafeAbove') ;}
                //console.dir(postrecord);
                collection.update({_id: i }, postrecord ,{ upsert: true }, function() {});
            }
        var collection_mailer = db.collection('tesla_mailer');
        postrecord = {};
        if (req.param('NotifyTo')) { postrecord["NotifyTo"] = req.param('NotifyTo');}
        if (req.param('NotifySMTP')) { postrecord["NotifySMTP"] = req.param('NotifySMTP');}
        if (req.param('NotifySMTPPort')) { postrecord["NotifySMTPPort"] = req.param('NotifySMTPPort');}
        if (req.param('NotifySMTPAuth')) { postrecord["NotifySMTPAuth"] = req.param('NotifySMTPAuth');}
        if (req.param('NotifySMTPUser')) { postrecord["NotifySMTPUser"] = req.param('NotifySMTPUser');}
        if (req.param('NotifySMTPPass')) { postrecord["NotifySMTPPass"] = req.param('NotifySMTPPass');}
        collection_mailer.update({_id: 1 }, postrecord ,{ upsert: true }, function() {});



        });

    //records saved - redirect to show page
    res.writeHead(302, {
          'Location': '/scheduler'
          //add other headers here...
        });
    res.end();

});


app.get('/scheduler', ensureAuthenticated, function(req, res) {
	// friendly welcome screen
  
      var MongoClient = require('mongodb').MongoClient
      , format = require('util').format;

      MongoClient.connect('mongodb://127.0.0.1:27017/' +argv.db , function(err, db) {
        if(err) {throw err};

            var collection_mailer = db.collection('tesla_mailer');
            var collection = db.collection('tesla_scheduler');
               var row = 0;
               var table = "";
              // Locate all the entries using find
              collection.find().limit(20).sort({SCHEDULERID:1}).toArray(function(err, results) {
               if(err) {throw err};

                 if (results.length == 0) {

                   //Insert 20 new empty records
                   for (var i=1;i<21;i++) {
                    collection.insert({_id: i ,SCHEDULERID:1 , Active : 0, MON : 0, TUE : 0, WED : 0, THU : 0, FRI : 0, SAT : 0, SUN : 0, TIME : "00:05", SAFE : 0, PLUGGEDIN : 0, PLUGGEDINDISREGARD : 0, COMMAND : 1, NOTIFY : 0, TEMPPERCENT : 0, SAFEABOVE : 25}, function() {});
                   }

                    //Insert an empty record in the mailer table
                    collection_mailer.insert({_id: 1 ,NotifyTo : "Enter Value ", NotifySMTP : "empty", NotifySMTPPort : "empty", NotifySMTPUser : "empty", NotifySMTPPass : "empty", NotifySMTPAuth : "empty"}, function() {});
                   
                   //redirect to self
                   res.writeHead(302, {
                        'Location': '/scheduler'
                        //add other headers here...
                        });
                   res.end();

                 }

                 if (results.length >= 1 ) {  

                 results.forEach(function(result) {
					row++;

                  //console.dir(result);
                  table += "<tr>";
                  table += "<td>" + row + "</td>";
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_Active\" value=\"1\" type=\"checkbox\"";
                  if(result.Active == 1) {table += " checked "; };
                  table += "></td>";
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_MON\" value=\"1\" type=\"checkbox\"";
                  if(result.MON == 1) {table += " checked "; };
                  table += "></td>";
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_TUE\" value=\"1\" type=\"checkbox\"";
                  if(result.TUE == 1) {table += " checked "; };
                  table += "></td>"
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_WED\" value=\"1\" type=\"checkbox\"";
                  if(result.WED == 1) {table += " checked "; };
                  table += "></td>";
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_THU\" value=\"1\" type=\"checkbox\"";
                  if(result.THU == 1) {table += " checked "; };
                  table += "></td>"
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_FRI\" value=\"1\" type=\"checkbox\"";
                  if(result.FRI == 1) {table += " checked "; };
                  table += "></td>";
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_SAT\" value=\"1\" type=\"checkbox\"";
                  if(result.SAT == 1) {table += " checked "; };
                  table += "></td>";
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_SUN\" value=\"1\" type=\"checkbox\"";
                  if(result.SUN == 1) {table += " checked "; };
                  table += "></td>";
                  table += "<td style=\"text-align: left;\"><input pattern=\"^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$\"";
                  table += "    maxlength=\"5\"";
                  table += "    size=\"5\"";
                  table += "    name=\"" + row + "_TIME\"";
                  table += "    style=\"width: 40px\"";
                  if(result.TIME != "") {table += "value = \"" + result.TIME + "\""; };
                  table += "    type=\"text\">";
                  table += "</td>";
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_SAFE\" value=\"1\" type=\"checkbox\"";
                  if(result.SAFE == 1) {table += " checked "; };
                  table += "></td>";
                  table += "<td style=\"text-align: center;\"><input name=\"" + row + "_PLUGGEDIN\" value=\"1\" type=\"checkbox\"";
                  if(result.PLUGGEDIN == 1) {table += " checked "; };
                  table += "> - <input name=\"" + row + "_PLUGGEDINDISREGARD\" value=\"1\" hidden type=\"checkbox\"";
                  if(result.PLUGGEDINDISREGARD == 1) {table += " checked "; };
                  table += "></td>";
                  table += "<td style=\"text-align: left;\">";
                  table += "  <select required=\"required\" name=\"" + row + "_COMMAND\">";
                  table += "    <optgroup label=\"Command\">";
                  table += "      <option value=\"1\" ";
                  if(result.COMMAND == "1") {table += "selected = \"selected\""; };
                  table += ">HVAC: On</option>";
                  table += "      <option value=\"2\" ";
                  if(result.COMMAND == "2") {table += "selected = \"selected\""; };
                  table += ">HVAC: Off</option>";
                  table += "      <option value=\"3\" ";
                  if(result.COMMAND == "3") {table += "selected = \"selected\""; };
                  table += ">HVAC: Set Temp</option>";
                  table += "      <option value=\"4\" ";
                  if(result.COMMAND == "4") {table += "selected = \"selected\""; };
                  table += ">Charge: Start</option>";
                  table += "      <option value=\"5\" ";
                  if(result.COMMAND == "5") {table += "selected = \"selected\""; };
                  table += ">Charge: Stop</option>";
                  table += "      <option value=\"6\" ";
                  if(result.COMMAND == "6") {table += "selected = \"selected\""; };
                  table += ">Charge: STD</option>";
                  table += "      <option value=\"7\" ";
                  if(result.COMMAND == "7") {table += "selected = \"selected\""; };
                  table += ">Charge: Max</option>";
                  table += "      <option value=\"8\" ";
                  if(result.COMMAND == "8") {table += "selected = \"selected\""; };
                  table += ">Charge: Percent</option>";
                  table += "      <option value=\"9\" ";
                  if(result.COMMAND == "9") {table += "selected = \"selected\""; };
                  table += ">Charge: Check unplugged</option>";
                  table += "    </optgroup>";
                  table += "  </select>";
                  table += "</td>";
                  table += "<td><input pattern=\"^[1-9][0-9]?$|^100$\" size=\"3\" placeholder=\"Temp: 17-32/ 0-100 Charge\"";
                  table += "    name=\"" + row + "_TEMPPERCENT\"";
                  if(result.TEMPPERCENT != "") {table += "value = \"" + result.TEMPPERCENT + "\""; };
                  table += "    type=\"text\">";
                  table += "</td>";
                  table += "<td><input name=\"" + row + "_NOTIFY\" value=\"1\" type=\"checkbox\"";
                  if(result.NOTIFY == 1) {table += " checked "; };
                  table += "></td>";
                  table += "</tr>";


	            });

                var MAGIC_NotifyTo = "";
                var MAGIC_NotifySMTP = "";
                var MAGIC_NotifySMTPPort = "";
                var MAGIC_NotifySMTPUser = "";
                var MAGIC_NotifySMTPPass = "";

                
                    collection_mailer.find().limit(1).sort({_id:1}).toArray(function(err, results_mailer) {
                         if(err) {throw err};
                         //console.dir(results_mailer);
                         MAGIC_NotifyTo = results_mailer[0].NotifyTo ;
                         MAGIC_NotifySMTP = results_mailer[0].NotifySMTP ;
                         MAGIC_NotifySMTPPort = results_mailer[0].NotifySMTPPort ;
                         MAGIC_NotifySMTPUser = results_mailer[0].NotifySMTPUser ;
                         MAGIC_NotifySMTPPass = results_mailer[0].NotifySMTPPass ;
                         //console.dir(MAGIC_NotifyTo);

                    });    

                //console.dir(htmldata);
	            fs.readFile(__dirname + "/scheduler.html", "utf-8", function(err, data) {
		            if (err) throw err;
		            res.send(data.replace("MAGIC_NAV",nav)
			             .replace("MAGIC_HTML", table)
                         .replace("MAGIC_NotifyTo", MAGIC_NotifyTo)
                         .replace("MAGIC_NotifySMTP", MAGIC_NotifySMTP)
                         .replace("MAGIC_NotifySMTPPort", MAGIC_NotifySMTPPort)
                         .replace("MAGIC_NotifySMTPUser", MAGIC_NotifySMTPUser)
                         .replace("MAGIC_NotifySMTPPass", MAGIC_NotifySMTPPass)
                         );
                });

            }
        // Let's close the db
            
        });
       
       });
   
 
});
//ADDED KSE END

app.get('/fahrtenbuch', ensureAuthenticated, function(req, res) {
	var path = req.path;
	var dates = new parseDates(req.query.from, req.query.to);
	var from = makeDate(dates.fromQ);
	var to = makeDate(dates.toQ);
	res.setHeader("Content-Type", "text/html");
	fs.readFile(__dirname + "/fahrtenbuch.html", "utf-8", function(err, data) {
		if (err) throw err;
		var lltable = "[0,0,0,0]";
		var table = "<thead><tr><th colspan=9 id='title'>Fahrtenbuch</th></tr>\n";
		table += "<tr><th colspan=6 class='left' id='Fahrer'>Fahrer: <span id='fahrername'>Tesla Fahrer</span></th><th colspan=3 class='left'>Abgabedatum:</th></tr>\n";
		table += "<tr><th colspan=2>Abfahrt</th><th colspan=2>Ankunft</th>";
		table += "<th rowspan=2>Wegstrecke</th><th rowspan=2>Reisezweck</th><th rowspan=2>Auto<br>Kennzeichen</th>";
		table += "<th rowspan=2>KM Stand am<br>Zielort</th><th rowspan=2>Unterschrift</th></tr>";
		table += "<tr><th>Datum</th><th>Zeit</th><th>Datum</th><th>Zeit</th></tr></thead>";
		MongoClient.connect(mongoUri, function(err, db) {
			if(err) {
				console.log('error connecting to database:', err);
				return;
			}
			collection = db.collection("trip_data");
			// strangely the timestamps end up in the database as strings
			var searchString = {$and: [ {'from': {$gte: ""+from.getTime()}}, {'to': {$lte: ""+to.getTime()}} ] };
			collection.find(searchString).toArray(function(err,docs) {
				var row = 0;
				if (argv.verbose) console.log("got datasets:", docs.length);
				table += "<tbody>\n";
				docs.forEach(function(doc) {
					row++;
					table += "<tr>";
					var depart = new Date(+doc.from);
					var arrive = new Date(+doc.to);
					table += "<td>" + depart.getDate() + "." + (depart.getMonth() + 1) + "." + (1900 + depart.getYear()) +"</td>";
					table += "<td>" + depart.getHours() + ":" + lZ(depart.getMinutes())  + "</td>";
					table += "<td>" + arrive.getDate() + "." + (arrive.getMonth() + 1) + "." + (1900 + arrive.getYear()) +"</td>";
					table += "<td>" + arrive.getHours() + ":" + lZ(arrive.getMinutes())  + "</td>";
					table += "<td>" + (1.609 * parseFloat(doc.dist)).toFixed(1) + "km</td>";
					if (doc.type === "business")
						table += "<td>" + doc.name + "</td>";
					else
						table += "<td>privat</td>";
					table += "<td><span class='kennzeichen'/></td>";
					table += "<td>" + (1.609 * parseFloat(doc.odo)).toFixed(1) + "</td>";
					table += "<td></td>";
					table += "</tr>\n";
					// the client side of this then fills in the addresses
					table += "<tr><td colspan=4 id='start" + row + "'/></td><td colspan=2 id='stop" + row + "'></td></tr>\n";
					if (doc.type === "business" && doc.start !== undefined && doc.end !== undefined)
						lltable += ",[" + doc.start.lat + "," + doc.start.lng + "," + doc.end.lat + "," + doc.end.lng + "]";
					else
						lltable += ",[0,0,0,0]";
				});
				table += "</tbody>\n";
				db.close();
				res.end(data.replace("MAGIC_NAV", nav)
						.replace("MAGIC_TRIP_TABLE", table)
						.replace("MAGIC_ADDR_TABLE", lltable), "utf-8");
			});
		});
	});
});

// that's all it takes to deliver the static files in the otherfiles subdirectory
app.use(express.static(__dirname + '/otherfiles'));

app.listen(argv.port);

if (!argv.silent) console.log("Server running on port " + argv.port);
