<commit_before>var async = require('async');
var http = require('http');
var url = require('url');
var elo = require('../public/js/elo');
var twitter_mod = require('./twitter_module');
var rec = require('./recommend');
var mailer = require('../public/js/mailer');
var winston = require('winston');

function Match(config){
	this.config = config;
};
module.exports = Match;

Match.prototype.list = function(req, res){
	var that = this;
	var matches = [];
	var players = [];
	async.parallel([
		function(pcb){ // Get All Available Players
			that.config.Players.find(function (err, players) {
				if (err){ // TODO handle err
					console.log(err)
					winston.info(err);
				} else{
					that.collectionToJSON(players, pcb);
				}

			});
		},
		function(pcb){ // Get all Matches
			that.config.Matches.find(function (err, matches) {
				if (err){ // TODO handle err
					console.log(err)
					winston.info(err);
				} else{
					pcb(null,matches)

				}

			});
		}
	], function(error, args){
		var myPlayers = args[0];
		var myMatches = args[1];
		myMatches.forEach(function(match, i){
			myPlayers.forEach(function(player, j){
				if(player["_id"] == match.redPlayer){
					match.redPlayerDetails = player;
				}
				if(player["_id"] == match.bluePlayer){
					match.bluePlayerDetails = player;
				}
			});
		});
		res.render('matches', { title: 'Matches Played', matches: myMatches, players: myPlayers });

	});
};

Match.prototype.collectionToJSON = function(players, cb) {
	var docs = [];
	async.each(players, function(player, f){
		docs.push(player.toJSON({virtuals: true}));
		f();
	}, function(err) {
		cb(err, docs);
	});
};

Match.prototype.singleMatch = function(req, res){
	var matchId = req.params.id;
	//console.log("MatchID", matchId);
	var that = this;
	async.parallel([
		function(pcb){ // Get All Available Players
			that.config.Players.find(function (err, players) {
				if (err){ // TODO handle err
					console.log(err)
					winston.info(err);
				} else{
					that.collectionToJSON(players, pcb);
				}
			});
		},
		function(pcb){ // Get all Matches
			that.config.Matches.findById(matchId, function (err, matchInfo) {
				//console.log("SingleMatchInfo", matchInfo);
				pcb(null, matchInfo);
			});
		}
	], function(err, args){
		//we should make sure it found something first...
		if(err){
			res.json({"Success": false, "Error": err});
		}
		if(args[0] && args[1]){
			var myPlayers = args[0];
			var matchDetails = args[1];
			myPlayers.forEach(function(player, j){
				if(player["_id"] == matchDetails.redPlayer){
					matchDetails.redPlayerDetails = player;
				}
				if(player["_id"] == matchDetails.bluePlayer){
					matchDetails.bluePlayerDetails = player;
				}
			});
			res.json(matchDetails)
		} else {
			res.json({"Success": false, "Error": 'no record found'});
		}
	});
};

// gets a list of all of the matches
Match.prototype.json = function(req, res){
	var that = this;
	var query = {deleted: false};
	var _url = url.parse(req.url, true);
	if(typeof _url.query.playerID !== "undefined"){
		var playerId = _url.query.playerID;
		query = {deleted: false, $or: [ {'bluePlayer': playerId},  {'redPlayer': playerId} ]};
	};
	getMatchAndPlayerInfo(req, res, that, query, _url);
};

// gets a list of all of the deleted matches
Match.prototype.delList = function(req, res){
	var that = this;
	var query = {deleted: true};
	var _url = url.parse(req.url, true);
	getMatchAndPlayerInfo(req, res, that, query, _url);
};

Match.prototype.add = function(req, res){
	var redPlayerObj = req.body.redPlayer;
	var bluePlayerObj = req.body.bluePlayer;
	var redPlayer = req.body.redPlayer._id;
	var bluePlayer = req.body.bluePlayer._id;
	//validate your inputs
	if(validateVar(redPlayer) || validateVar(bluePlayer)){
		console.log("Match Add Failed: ", 'no player ID');
		res.json({
			success: false,
			error: 'no player ID'
		});
		return;
	};
	if(redPlayer == bluePlayer){
		console.log("Match Add Failed: ", 'same Player ID');
		res.json({
			success: false,
			error: 'same player ID'
		});
		return;
	};
	var games = [
		{
			redScore: parseInt(req.body["game1RedPlayer"]) || 0,
			blueScore: parseInt(req.body["game1BluePlayer"]) || 0
		},
		{
			redScore: parseInt(req.body["game2RedPlayer"]) || 0,
			blueScore: parseInt(req.body["game2BluePlayer"]) || 0
		}
	];

	if(req.body["game3RedPlayer"] || req.body["game3BluePlayer"] ){
		games.push({
			redScore: parseInt(req.body["game3RedPlayer"]) || 0,
			blueScore: parseInt(req.body["game3BluePlayer"]) || 0
		});
	};
	var gameData = findWinner(games, redPlayer, bluePlayer);
	var redName = redPlayerObj.fname+' "'+redPlayerObj.nickname+'" '+redPlayerObj.lname;
	var blueName = bluePlayerObj.fname+' "'+bluePlayerObj.nickname+'" '+bluePlayerObj.lname;
	
	
	var tweet_status = 'New Match: '+ redName+' VS '+blueName+' Score: '+games[0].redScore+'-'+games[0].blueScore
		+', '+games[1].redScore+'-'+games[1].blueScore;
	
	if (games.length > 2) {
		tweet_status += ', '+games[2].redScore+'-'+games[2].blueScore;
	} else {
		//lost in 2 straight games.
	}



   var that = this;
    async.parallel({
        winner: function(pcb){
            that.config.Players.findById(gameData.winner, function(err,winner) {
                pcb(null,winner);
            });
        },
        loser: function(pcb){
            that.config.Players.findById(gameData.loser, function(err,loser) {
                pcb(null,loser);
            });
        },
    },
    function(error,args) {
        var winner = args.winner;
        var loser = args.loser;
        var ratingChange = adjustRatings(gameData.games,winner,loser);

        // save the red and blue players with the ratings change and create the match
        async.parallel([
            function(pcb){
                winner.save(function(err, player) {
                    if (err) {
                        console.log('Save winning player failed: ', err);
                        winston.info(err);
                        pcb(err);
                    } else {
                        console.log('Winning player rating updated');
                        pcb(null, player);
                    }
                });
            },
            function(pcb){
                winner.save(function(err, player) {
                    if (err) {
                        console.log('Save winning player failed: ', err);
                        winston.info(err);
                        pcb(err);
                    } else {
                        console.log('Winning player rating updated');
                        pcb(null, player);
                    }
                });
            },
            function(pcb){
                var newMatch = new that.config.Matches({
                    winner: gameData.winner,
                    loser: gameData.loser,
                    games: gameData.games,
                    ratingChange: ratingChange,
                    winnerRating: winner.rating,
                    loserRating: loser.rating
                });

                newMatch.save(function (err, newMatch) {
                    if (err){ // TODO handle the error
                        console.log("Match Add Failed: ", err);
                        winston.info(err);
                        pcb({success: false, error: err});
                    } else {
                        pcb(null, newMatch.toJSON({virtual: true}));
                    };
                });
            }
		], function(err, data) {
			if (err) {
				winston.info(err);
				res.json(500, err);
			} else {
				res.json({
					success: true,
					match: data
				});
				//adding tweet code;
				twitter_mod.update_status(tweet_status);
			}
        });

    });

};

Match.prototype.delete = function(req, res){
	var matchID = req.params.id;
	var that = this;

	async.parallel({
	    match: function(pcb){ // Remove Player
		that.config.Matches.update({"_id": matchID}, {deleted: true, removedDate: new Date()}, function(err){
		    if (err){ // TODO handle err
			console.log(err)
			winston.info(err);
		    } else{
			pcb(null);
		    }
		});
	    },
            players: function(pcb){
                that.config.Players.find(function(err,players) {
                    pcb(null,players);
                });
            },
            matches: function(pcb){
                that.config.Matches.find({deleted: false}, function(err,matches) {
                    pcb(null,matches);
                });
            }

	}, function(error, args){
		if(error){
			res.json({"Success": false, "Error": error});
		}else{
			//this is broken, i'm not sure what your trying to do here
            //replayMatches(args.players,args.matches);
		    res.json({"Success": true});
		}
	});
};

Match.prototype.rebuildRatings = function(req, res) {
    var that = this;
    async.parallel({
        players: function(pcb){
            that.config.Players.find(function(err,players) {
                that.collectionToJSON(players, pcb);
            });
        },
        matches: function(pcb){
            that.config.Matches.find({deleted: false}).sort({createdDate:1}).exec(function(err,matches) {
                pcb(null,matches);
            });
        }

    },
    function(error,args) {
        var players = args.players;
        var matches = args.matches;
        replayMatches(players,matches);
        res.json({
            sucess: true,
            players: players,
            matches: matches
        });
    });
};

var replayMatches = function(players,matches) {
    var playerHash = {};
    players.forEach(function(player,i) {
        player.rating = 1200;
        playerHash[player._id] = player;
    });
    matches.forEach(function(match,i) {
        var red = playerHash[match.redPlayer];
        var blue = playerHash[match.bluePlayer];

        var ratingChange = adjustRatings(match.games,red,blue);
	match.ratingChange = ratingChange;
	match.redPlayerRating = red.rating;
	match.bluePlayerRating = blue.rating;

	console.log("MATCH", match);
	match.save(function(err, match){
	    if(err){
	    winston.info(err);
		console.log("**** ERROR saving match");
	    }else{
		console.log("*** Match Updated Successful", match);
	    }
	});
    });

	console.log("PLAYERS", players);

    players.forEach(function(player,i) {
        player.save(function(err,player) {
            if (err) {
            	winston.info(err);
                console.log('Save player failed: ', err);
            } else {
                console.log('Player rating updated');
            }
        });
    });
};

var adjustRatings = function(games,winner,loser) {
    var result = games.length === 2 ? 1 : 0.75;
    var ratingChange = elo.delta(winner.rating, loser.rating, result);
    console.log(winner.lname + ' gains ' + ratingChange + ' points from ' + loser.lname);
    winner.rating += ratingChange;
    loser.rating -= ratingChange;

    console.log('New winner rating: ' + winner.rating);
    console.log('New loser rating: ' + loser.rating);
	return ratingChange;
};

var findWinner = function(games, red, blue) {
	var last = games[games.length - 1];
	var whoWon = whoWon(last);
	var newGames = [];
	games.forEach(function(game, i){
		var newGame = {
			winnerScore: whoWon === 'R' ? game.redScore : game.blueScore,
			loserScore: whoWon === 'B' ? game.blueScore : game.redScore
		};
		newGames.push(newGame);
	});
	return {
		winner: whoWon === 'R' ? red : blue,
		loser: whoWon === 'B' ? blue : red,
		games: newGames
	};
};

// var determineResult = function(games) {
    // var resultString = '';
    // games.forEach(function(game,i) {
        // resultString += whoWon(game);
    // });
    // if (resultString == 'RR') {
        // console.log('Red sweep');
        // return 1;
    // } else if (resultString == 'BB') {
        // console.log('Blue sweep');
        // return 0;
    // } else if (resultString.slice(-1) == 'R') {
        // console.log('Red wins split match');
        // return 0.75;
    // } else {
        // console.log('Blue wins split match');
        // return 0.25;
    // }
// };

var whoWon = function(game) {
    if (game.redScore > game.blueScore) {
        return 'R';
    } else {
        return 'B';
    }
};

var getMatchAndPlayerInfo = function(req, res, that, query, _url) {
	async.parallel([
		function(pcb){ // Get All Available Players
			that.config.Players.find(function (err, players) {
				if (err){ // TODO handle err
					console.log(err)
					winston.info(err);
				} else{
					that.collectionToJSON(players, pcb);
				}
			});
		},
		function(pcb){ // Get all Matches
		    that.config.Matches.find(query).sort({dateTime: -1}).execFind(function (err, matches) {
				if (err){ // TODO handle err
					console.log(err)
					winston.info(err);
				} else{
					pcb(null,matches)
				}
			});
		}
	], function(error, args){
		var myPlayers = args[0];
		var myMatches = args[1];
		myMatches.forEach(function(match, i){
			myPlayers.forEach(function(player, j){
				if(player["_id"] == match.redPlayer){
					myMatches[i].redPlayerDetails = player;
				}
				if(player["_id"] == match.bluePlayer){
					myMatches[i].bluePlayerDetails = player;
				}
			});
		});
		res.json(myMatches)
	});

};

Match.prototype.recMatches = function recMatches() {
    console.log('starting rec');
    var that = this;
    async.parallel({
        players: function(pcb){
            that.config.Players.find({email:{$exists:true}}).sort({rating:-1}).execFind(function(err,players) {
                pcb(null,players);
            });
        },
        matches: function(pcb){
            that.config.Matches.find({deleted: false}, function(err,matches) {
                pcb(null,matches);
            });
        }

    }, function(error,args) {
        var players = args.players;
        var matches = args.matches;
        var pairs = rec.buildPairs(players, matches);
        mailer.sendRecMatches(pairs);
    });
}

/**<commit_msg>Clean up more red/blue methods<commit_after>var async = require('async');
var http = require('http');
var url = require('url');
var elo = require('../public/js/elo');
var twitter_mod = require('./twitter_module');
var rec = require('./recommend');
var mailer = require('../public/js/mailer');
var winston = require('winston');

function Match(config){
	this.config = config;
};
module.exports = Match;

Match.prototype.collectionToJSON = function(players, cb) {
	var docs = [];
	async.each(players, function(player, f){
		docs.push(player.toJSON({virtuals: true}));
		f();
	}, function(err) {
		cb(err, docs);
	});
};

Match.prototype.singleMatch = function(req, res){
	this.config.Matches.findById(req.params.id).populate('winner loser').exec(function (err, match) {
		if (err) res.json({Success: false, "Error": err});
		else if (!match) res.json({Success: false, "Error": "no record found"});
		else res.json(match);
	});
};

// gets a list of all of the matches
Match.prototype.json = function(req, res){
	var id = req.query.playerID;
	var query = id ? {deleted: false} : {deleted: false, $or: [ {'winner': id}, {'loser': id} ]};
	this.config.Matches.find(query).sort({dateTime: -1}).populate('winner loser').exec(function (err, matches) {
		if (err){ // TODO handle err
			console.log(err)
			winston.info(err);
		} else res.json(matches);
	});
};

// gets a list of all of the deleted matches
Match.prototype.delList = function(req, res){
	this.config.Matches.find({deleted: true}).sort({dateTime: -1}).populate('winner loser').exec(function (err, matches) {
		if (err){ // TODO handle err
			console.log(err)
			winston.info(err);
		} else res.json(matches);
	});
};

Match.prototype.add = function(req, res){
	var redPlayerObj = req.body.redPlayer;
	var bluePlayerObj = req.body.bluePlayer;
	var redPlayer = req.body.redPlayer._id;
	var bluePlayer = req.body.bluePlayer._id;
	//validate your inputs
	if(validateVar(redPlayer) || validateVar(bluePlayer)){
		console.log("Match Add Failed: ", 'no player ID');
		res.json({
			success: false,
			error: 'no player ID'
		});
		return;
	};
	if(redPlayer == bluePlayer){
		console.log("Match Add Failed: ", 'same Player ID');
		res.json({
			success: false,
			error: 'same player ID'
		});
		return;
	};
	var games = [
		{
			redScore: parseInt(req.body["game1RedPlayer"]) || 0,
			blueScore: parseInt(req.body["game1BluePlayer"]) || 0
		},
		{
			redScore: parseInt(req.body["game2RedPlayer"]) || 0,
			blueScore: parseInt(req.body["game2BluePlayer"]) || 0
		}
	];

	if(req.body["game3RedPlayer"] || req.body["game3BluePlayer"] ){
		games.push({
			redScore: parseInt(req.body["game3RedPlayer"]) || 0,
			blueScore: parseInt(req.body["game3BluePlayer"]) || 0
		});
	};
	var gameData = findWinner(games, redPlayer, bluePlayer);
	var redName = redPlayerObj.fname+' "'+redPlayerObj.nickname+'" '+redPlayerObj.lname;
	var blueName = bluePlayerObj.fname+' "'+bluePlayerObj.nickname+'" '+bluePlayerObj.lname;
	
	
	var tweet_status = 'New Match: '+ redName+' VS '+blueName+' Score: '+games[0].redScore+'-'+games[0].blueScore
		+', '+games[1].redScore+'-'+games[1].blueScore;
	
	if (games.length > 2) {
		tweet_status += ', '+games[2].redScore+'-'+games[2].blueScore;
	} else {
		//lost in 2 straight games.
	}



   var that = this;
    async.parallel({
        winner: function(pcb){
            that.config.Players.findById(gameData.winner, function(err,winner) {
                pcb(null,winner);
            });
        },
        loser: function(pcb){
            that.config.Players.findById(gameData.loser, function(err,loser) {
                pcb(null,loser);
            });
        },
    },
    function(error,args) {
        var winner = args.winner;
        var loser = args.loser;
        var ratingChange = adjustRatings(gameData.games,winner,loser);

        // save the red and blue players with the ratings change and create the match
        async.parallel([
            function(pcb){
                winner.save(function(err, player) {
                    if (err) {
                        console.log('Save winning player failed: ', err);
                        winston.info(err);
                        pcb(err);
                    } else {
                        console.log('Winning player rating updated');
                        pcb(null, player);
                    }
                });
            },
            function(pcb){
                loser.save(function(err, player) {
                    if (err) {
                        console.log('Save losing player failed: ', err);
                        winston.info(err);
                        pcb(err);
                    } else {
                        console.log('Losing player rating updated');
                        pcb(null, player);
                    }
                });
            },
            function(pcb){
                var newMatch = new that.config.Matches({
                    winner: gameData.winner,
                    loser: gameData.loser,
                    games: gameData.games,
                    ratingChange: ratingChange,
                    winnerRating: winner.rating,
                    loserRating: loser.rating
                });

                newMatch.save(function (err, newMatch) {
                    if (err){ // TODO handle the error
                        console.log("Match Add Failed: ", err);
                        winston.info(err);
                        pcb({success: false, error: err});
                    } else {
                        pcb(null, newMatch.toJSON({virtual: true}));
                    };
                });
            }
		], function(err, data) {
			if (err) {
				winston.info(err);
				res.json(500, err);
			} else {
				res.json({
					success: true,
					match: data
				});
				//adding tweet code;
				twitter_mod.update_status(tweet_status);
			}
        });

    });

};

Match.prototype.delete = function(req, res){
	var matchID = req.params.id;
	var that = this;

	async.parallel({
	    match: function(pcb){ // Remove Player
		that.config.Matches.update({"_id": matchID}, {deleted: true, removedDate: new Date()}, function(err){
		    if (err){ // TODO handle err
			console.log(err)
			winston.info(err);
		    } else{
			pcb(null);
		    }
		});
	    },
            players: function(pcb){
                that.config.Players.find(function(err,players) {
                    pcb(null,players);
                });
            },
            matches: function(pcb){
                that.config.Matches.find({deleted: false}, function(err,matches) {
                    pcb(null,matches);
                });
            }

	}, function(error, args){
		if(error){
			res.json({"Success": false, "Error": error});
		}else{
			//this is broken, i'm not sure what your trying to do here
            //replayMatches(args.players,args.matches);
		    res.json({"Success": true});
		}
	});
};

Match.prototype.rebuildRatings = function(req, res) {
    var that = this;
    async.parallel({
        players: function(pcb){
            that.config.Players.find(function(err,players) {
                that.collectionToJSON(players, pcb);
            });
        },
        matches: function(pcb){
            that.config.Matches.find({deleted: false}).sort({createdDate:1}).exec(function(err,matches) {
                pcb(null,matches);
            });
        }

    },
    function(error,args) {
        var players = args.players;
        var matches = args.matches;
        replayMatches(players,matches);
        res.json({
            sucess: true,
            players: players,
            matches: matches
        });
    });
};

var replayMatches = function(players,matches) {
    var playerHash = {};
    players.forEach(function(player,i) {
        player.rating = 1200;
        playerHash[player._id] = player;
    });
    matches.forEach(function(match,i) {
        var red = playerHash[match.redPlayer];
        var blue = playerHash[match.bluePlayer];

        var ratingChange = adjustRatings(match.games,red,blue);
	match.ratingChange = ratingChange;
	match.redPlayerRating = red.rating;
	match.bluePlayerRating = blue.rating;

	console.log("MATCH", match);
	match.save(function(err, match){
	    if(err){
	    winston.info(err);
		console.log("**** ERROR saving match");
	    }else{
		console.log("*** Match Updated Successful", match);
	    }
	});
    });

	console.log("PLAYERS", players);

    players.forEach(function(player,i) {
        player.save(function(err,player) {
            if (err) {
            	winston.info(err);
                console.log('Save player failed: ', err);
            } else {
                console.log('Player rating updated');
            }
        });
    });
};

var adjustRatings = function(games,winner,loser) {
    var result = games.length === 2 ? 1 : 0.75;
    var ratingChange = elo.delta(winner.rating, loser.rating, result);
    console.log(winner.lname + ' gains ' + ratingChange + ' points from ' + loser.lname);
    winner.rating += ratingChange;
    loser.rating -= ratingChange;

    console.log('New winner rating: ' + winner.rating);
    console.log('New loser rating: ' + loser.rating);
	return ratingChange;
};

var findWinner = function(games, red, blue) {
	var last = games[games.length - 1];
	var whoWon = whoWon(last);
	var newGames = [];
	games.forEach(function(game, i){
		var newGame = {
			winnerScore: whoWon === 'R' ? game.redScore : game.blueScore,
			loserScore: whoWon === 'B' ? game.blueScore : game.redScore
		};
		newGames.push(newGame);
	});
	return {
		winner: whoWon === 'R' ? red : blue,
		loser: whoWon === 'B' ? blue : red,
		games: newGames
	};
};

// var determineResult = function(games) {
    // var resultString = '';
    // games.forEach(function(game,i) {
        // resultString += whoWon(game);
    // });
    // if (resultString == 'RR') {
        // console.log('Red sweep');
        // return 1;
    // } else if (resultString == 'BB') {
        // console.log('Blue sweep');
        // return 0;
    // } else if (resultString.slice(-1) == 'R') {
        // console.log('Red wins split match');
        // return 0.75;
    // } else {
        // console.log('Blue wins split match');
        // return 0.25;
    // }
// };

var whoWon = function(game) {
    if (game.redScore > game.blueScore) {
        return 'R';
    } else {
        return 'B';
    }
};

Match.prototype.recMatches = function recMatches() {
    console.log('starting rec');
    var that = this;
    async.parallel({
        players: function(pcb){
            that.config.Players.find({email:{$exists:true}}).sort({rating:-1}).execFind(function(err,players) {
                pcb(null,players);
            });
        },
        matches: function(pcb){
            that.config.Matches.find({deleted: false}, function(err,matches) {
                pcb(null,matches);
            });
        }

    }, function(error,args) {
        var players = args.players;
        var matches = args.matches;
        var pairs = rec.buildPairs(players, matches);
        mailer.sendRecMatches(pairs);
    });
}

/**
{'fraction_non_alphanumeric': 0.15513660329126058, 'fraction_numerical': 0.004114075730947435, 'mean_word_length': 2.2326662362814718, 'pattern_counts': {'":': 14, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1260713', 'n_tokens_mistral': 8536, 'n_tokens_neox': 7754, 'n_words': 1578}