/*
var http = require('http');

//create a server object:
http.createServer(function (req, res) {
  res.write('Hello World!'); //write a response to the client
  res.end(); // end the response
}).listen(2188);

*/

/*

process.on('uncaughtException', function(err) {
    console.log(err);	
	//var err = new Error();
	if (typeof(err) == 'object')
		console.log(err.stack);
})
*/
console.log('starting news server');

var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({port:2188});

var mysql      = require('mysql');
var db = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : 'dpadhel@tjqj',
  database : 'news',
  //multipleStatements: true,
});

var db0 = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : 'dpadhel@tjqj',
  database : 'news',
  multipleStatements: true,
});


var all = {};

function checkerr(err){
	if (err) 
		throw err;
}

var tgtStart = Date.now();
function timeGetTime()
{
	return Date.now()-tgtStart;	
}


wss.on('connection', function(conn){
	var client = {
		id: '', //보통은 macaddress 를 쓴다
		sock: null,
		pingtime: timeGetTime(),
		pingstoretime: timeGetTime(),
		sendSysMsg: function(s){
			try {
				this.sock.send(JSON.stringify({
					syscmd: 'sysmsg',
					msg: s,					
				}));
			}catch(err) {
				console.log(err);
				//console.log(err.stack);
			}
		}, 
		updatePing: function(){
			var me = this;
			var t = timeGetTime();			
			this.pingtime = t;
			//db.query('insert into message_listener (id, pingtime) values (?, NOW()) on duplicate key update pingtime=NOW()', [client.id], checkerr);
			if (t - this.pingstoretime > 1000*55) { // 핑을 아무리 자주 보내도 db업데이트는 55초에 1번
				this.pingstoretime = timeGetTime();	
				db.query('update subscriber set pingtime=NOW() where id=?', [me.id], checkerr);
			}
		}
	};
    conn.on("message", function (s) {
		client.sock = conn;
		client.updatePing();
		//console.log('MESSAGE: ', s);
		//console.log(o);
		var o = JSON.parse(s);
		if (o.cmd == 'subscribe')
		{
			//if (!all[o.groupid])
				//all[o.groupid] = [];
			//var group = all[o.groupid];
			//if (client.groupid)
				
			if (client.id && client.id != o.id) {
				console.log('id mismatch');
				return;
			}
			client.id = o.id;
			client.groupid = o.groupid;
			all[client.id] = client;
			db.query("insert into subscriber (groupid, id) values (?, ?) on duplicate key update groupid=?;", [client.groupid, client.id, client.groupid], checkerr);
			// db.query('insert into message_listener (id, pingtime) values (?, NOW()) on duplicate key update pingtime=NOW()', [client.id], checkerr);
			// client.updatePing();
			return;
		}
		if (client.id == '') {
			console.log('client is not registered', o);
			client.sendSysMsg('client is not registered');
			return;
			//throw 'client is not registered';
		}		
    });
	
    conn.on("close", function (code, reason) { // 이 이벤트는 늦게 발생할 수 도 있다
        console.log(client.id + ' - ' + reason + ": Connection closed")
		delete client.sock;
		delete all[client.id];
    })
});



var _isdbupdating = false;
setInterval(function () {
	if (_isdbupdating) {
		console.log('DB is busy');
		return;
	}
	_isdbupdating = true;
	try {	
		// if (Math.random() < 0.05) console.log('send thread is alive (this message appears in random)\n');	
		//console.log('sql1');
		//db0.query("select * from message where `read`=0 and TIME_TO_SEC(TIMEDIFF(NOW(), creationtime)) < 60 limit 1000", function(err, rows){ // 2.5초
		db0.query("select * from news where `sent`=0 and creationtime > ADDDATE(NOW(), INTERVAL -6000 second) limit 1000", function(err, rows) { // 0.0005 초  (쿼리 시간이 어마어마하게 차이남)
			_isdbupdating = true;		
		
			if (rows.length > 0)
				console.log('message count to send is ' + rows.length);
			checkerr(err);
			var t = timeGetTime();
			var sql = '';
			
			for(var i in rows) {
				var r = rows[i];
				news = JSON.stringify(r);
				db0.query("select * from subscriber where groupid=?", [r.groupid], function(err, rows){
					for (var i in rows) {
						var ser = rows[i];
						var client = all[ser.id];
						if (!client)
							continue;
						try {
							console.log('sending');
							client.sock.send(news);
							// console.log('message send '+ r.groupid + ' to '+r.receiver);
						} catch(err) {
							console.log(err.stack);
						}
					}					
				});
				sql += mysql.format("update news set `sent`=1 where no=?;\n", [r.no]);
			}
			if (sql)
				console.log(sql);
			if (sql == '') {
				_isdbupdating = false;
				return;
			}
			try {
				console.log('sql2');
				db0.query(sql, function(err){
					_isdbupdating = false;
					checkerr(err);
				});
			} catch (err){
				_isdbupdating = false;
				console.log(err.stack);
			}
		});
	} catch(err) {
		_isdbupdating = false;
		console.log(err.stack);
	}
	//_isdbupdating = false;
}, 100);

// ping clean

setInterval(function() {
	console.log('this server is alive.\nconnection count: '+Object.keys(all).length);	

	var t = timeGetTime();
	for(var i in all) {
		var client = all[i];
		if (t - client.pingtime > 1000*60*3) // ping timeGetTime 3분 드롭
		{
			console.log('KILLING by late pinging: ' + client.id);
			client.sock.close();
			client.sock = null;
			all[i] = null; // 이미 끊어진 놈은 'close'이벤트가 발생 안할 수 있으므로 수동 정리 필요함
			delete all[i];
			
			continue;
		}
	}	
}, 5000);
/*
setInterval(function(){
	global.gc(); //running gc
	console.log('running gc');		
}, 1000*30);

*/