/**
 * Module Name: sstats
 * Description: Simple Stats, in the absence of good ones.
 */
var _ = require('underscore')._,
    async = require('async');

var sstats = function(dbot) {
    if(!_.has(dbot.db, 'ssinception')) dbot.db.ssinception = new Date().getTime();

    // This needs to be somewhere else
    this.isUpperCase = function(word) {
        return _.all(word.split(''), function(c) {
            return c == c.toUpperCase(); 
        });
    };

    this.internalAPI = {
        // I'm not a huge fan of this but it's better than all the code
        // repetition
        'highscore': function(key, property, callback) {
            var pList = {}, 
                pPointer = property;
            if(!_.isArray(pPointer)) {
                pPointer = property.split('.');
            }

            this.db.scan(key, function(item) {
                var id = item.id;
                for(var i=0;i<pPointer.length;i++) {
                    if(_.has(item, pPointer[i])) {
                        item = item[pPointer[i]]; 
                    } else {
                        item = null; break;
                    }
                }

                if(item) {
                    pList[id] = item;
                }
            }, function() {
                var pCounts = _.chain(pList)
                    .pairs()
                    .sortBy(function(p) { return p[1]; })
                    .reverse()
                    .first(10)
                    .value();

                async.eachSeries(pCounts, function(pCount, next) {
                    dbot.api.users.getUser(pCount[0], function(err, user) {
			if(user) {
				pCount[0] = user.primaryNick;
			}
			next();
                    });
                }, function() {
                    callback(pCounts);
                }.bind(this));
            });
        }.bind(this),

        'channelHighscore': function(key, server, channel, property, callback) {
            var cId = channel + '.' + server;
            var newProperty = ['channels', cId, property];
            this.internalAPI.highscore(key, newProperty, callback);
        }.bind(this),

        'formatHighscore': function(string, pCounts) {
            var output = string;
            for(var i=0;i<pCounts.length;i++) {
                output += pCounts[i][0] + " (" + pCounts[i][1] + "), ";
            }
            return output.slice(0, -2);
        }
    };

    this.listener = function(event) {
        if(event.channel == event.user) return; // ignore PMs
        event.cStats.lines++;
        event.uStats.lines++;

        var message = event.message.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()]/g, "");
        var cId = event.channel + '.' + event.server;
        var words = message.split(' '),
            wCount = words.length,
            capitals = 0,
            curses = 0;
        _.each(words, function(word) {
            if(this.isUpperCase(word)) capitals++;
            if(_.any(this.config.curses, function(curse) { return word.toLowerCase().indexOf(curse) != -1; })) {
                curses++;
            }
        }, this);

        event.uStats.words += wCount;
        event.uStats.capitals += capitals;
        event.uStats.curses += curses;
        event.uStats.last = new Date().getTime();

        event.cStats.words += wCount;
        event.cStats.capitals += capitals;
        event.cStats.curses += curses;

        if(!_.has(event.uStats.channels, cId)) {
            event.uStats.channels[cId] = { 
                'lines': 1,
                'words': wCount,
                'capitals': capitals,
                'curses': curses
            };
        } else {
            event.uStats.channels[cId].lines++;
            event.uStats.channels[cId].words += wCount;
            event.uStats.channels[cId].capitals += capitals;
            event.uStats.channels[cId].curses += curses;
        }

        // Look for tracked words.
        if(event.message.charAt(0) != '~') {
            var wMap = {}; // Why reduce isn't working idk
            _.each(words, function(word) {
                word = word.toLowerCase();
                if(!_.has(wMap, word)) wMap[word] = 0;
                wMap[word]++;
            });
            _.each(wMap, function(count, word) {
                this.api.getTrackedWord(word, function(tWord) {
                    if(tWord) {
                        tWord.total += count;
                        if(!_.has(tWord.channels, cId)) tWord.channels[cId] = 0;
                        if(!_.has(tWord.users, event.rUser.id)) tWord.users[event.rUser.id] = 0;
                        tWord.channels[cId] += count;
                        tWord.users[event.rUser.id] += count;
                        this.db.save('tracked_words', word, tWord, function() {}); 
                    }
                }.bind(this));
            }, this);
        }

        this.db.save('channel_stats', event.cStats.id, event.cStats, function() {});
        this.db.save('user_stats', event.uStats.id, event.uStats, function() {});
    }.bind(this);
    this.on = 'PRIVMSG';

    this.onLoad = function() {
        // Preload user stats
        dbot.instance.addPreEmitHook(function(event, callback) {
            if(!event.rUser) return callback();
            this.api.getUserStats(event.rUser.id, function(uStats) {
                if(uStats) {
                    event.uStats = uStats;
                    callback();
                } else {
                    this.api.createUserStats(event.rUser.id, function(uStats) {
                        event.uStats = uStats;
                        callback();
                    });
                }
            }.bind(this));
        }.bind(this));

        // Preload channel stats
        dbot.instance.addPreEmitHook(function(event, callback) {
            if(!event.channel) return callback();
            var id = event.channel + '.' + event.server;
            this.api.getChannelStats(id, function(cStats) {
                if(cStats) {
                    event.cStats = cStats;
                    callback();
                } else {
                    this.api.createChannelStats(id, function(cStats) {
                        event.cStats = cStats;
                        callback();
                    });
                }
            }.bind(this));
        }.bind(this));

        dbot.api.event.addHook('~mergeusers', function(server, oldUser, newUser) {
            this.api.getUserStats(oldUser.id, function(ouStats) {
                this.api.getUserStats(newUser.id, function(nuStats) {
                    _.each(ouStats, function(stat, key) {
                        if(key != 'creation' && key != 'id') {
                            if(_.isObject(stat)) {
                                _.each(ouStats[key], function(stat, sKey) {
                                    _.each(ouStats[key][sKey], function(stat, ssKey) {
                                    if(_.has(nuStats[key][sKey], ssKey)) {
                                        nuStats[key][sKey][ssKey] += stat; 
                                    }
                                    });
                                });
                            } else {
                                nuStats[key] += stat; 
                            }
                        }
                    });
                    this.db.del('user_stats', oldUser.id, function() {});
                    this.db.save('user_stats', newUser.id, nuStats, function() {});
                }.bind(this));
            }.bind(this));

            this.db.scan('tracked_words', function(tWord) {
                if(_.has(tWord.users, oldUser.id)) {
                    if(_.has(tWord.users, newUser.id)) {
                        tWord.users[newUser.id] += tWord.users[oldUser.id];
                    } else {
                        tWord.users[newUser.id] = tWord.users[oldUser.id];
                    }
                    delete tWord.users[oldUser.id];
                    this.db.save('tracked_words', tWord.word, tWord, function() {});
                }
            }.bind(this), function() {});
        }.bind(this));
    }.bind(this);
};

exports.fetch = function(dbot) {
    return new sstats(dbot);
};