/*
 * Copyright (c) 2008, 2009 Glam Media
 *
 * Dual licensed under the Apache License v2.0 and 
 * GNU General Public License v2.0 or later
 *
 * http://developer.glam.com/license
 */

var glam = glam || {};
glam.metrics = {};

glam._metrics = function() {};

/*
tdata must contain:
publisherId
pageviewId
sessionId
adId
adTile
*/

// start tracking an individual gadget
glam._metrics.prototype.beginTracking = function(gadget, tdata) {
    // set up base tracking
    if (!this.tdata) {
        this.tdata = { loadtime: new Date().getTime(),
                       reportCount: 0,
                       lastReport: 0,
                       publisherId: tdata.publisherId || 0,
                       pageviewId: tdata.pageviewId || 0,
                       sessionId: tdata.sessionId || 0,
                       gadgets: {} // maps gadget-id to various gadget-related data that i should doc
                     };
        var thiz = this;
        setTimeout(function() { thiz.reportEvents(thiz) }, 100);
    } else {
        gadgets.log("gadget tracking already initialized");
    }
        
    if (!this.tdata.gadgets[gadget.mid]) { // XXX don't use mid!
        this.tdata.gadgets[gadget.mid] = { // XXX don't use mid!
            data: { 
                developerId: gadget.developerId || 0,
                appId: gadget.appId || 0,
                installId: gadget.installId || 0,
                gadgetId: tdata.adTile || 0,
                adId: tdata.adId || 0,
                idType: tdata.idType || 0,
                view: gadget.view || 0,
                refer: tdata.refer || 0
            },
            single: {},
            duration: {},
            active: 0 // # of active events
        }
        //console.log(this.tdata.gadgets);
    } else {
        gadgets.log("gadget:" + gadget.mid + " has already been registered"); // XXX don't use mid!
    }
};

glam._metrics.prototype.trackEvent = function(id, event_code, param) {
    var elapsed = new Date().getTime() - this.tdata.loadtime;
    var event_key = event_code;
    if (param) {
        event_key += ":" + param;
    }    
    gadgets.log("event occurred:" + event_key + " after " + elapsed + "ms on gadget:" + id);
    var d = this.tdata.gadgets[id];
    if (!d) {
        gadgets.log("error recording event!");
        return;
    }
    d.dirty = 1;
    if (!d.single[event_key]) {
        d.single[event_key] = [elapsed];
    } else {
        d.single[event_key].push(elapsed);
    }        
};

/*
Set an isActive flag in the event tracking data, this holds the start time for
the latest event start.  stopEvent removes this and copies the elapsed time to the
duration field and increments the count.  This field must count as a dirty bit
and cause a report each time, otherwise incremental updates would not be sent during
a long-lived state.
*/
glam._metrics.prototype.startEvent = function(id, event_code, param) {
    var elapsed = (new Date().getTime() - this.tdata.loadtime) + 1; // cheat so it's never 0
    
    var event_key = event_code;
    if (param) {
        event_key += ":" + param;
    }    
    gadgets.log("event started:" + event_key + " after " + elapsed + "ms on gadget:" + id);
    
    var d = this.tdata.gadgets[id];
    if (!d) {
        gadgets.log("error recording event!");
        return;
    }    
    var mod = false;
    if (!d.duration[event_key]) {
        d.duration[event_key] = {
            'event': event_code, // repetitive but convenient
            'start': elapsed,    // only set when the event is created
            'param': param,
            'duration': 0,
            'count': 1,
            'active': elapsed
        };
        mod = true;
    } else { // restarting an event that has already occurred once
        var e = d.duration[event_key];
        if (!e.active) { // ignore multiple successive start events
            e.active = elapsed;
            e.count += 1;
            mod = true;
        }
    }
    if (mod) {
        d.dirty = 1;
        d.active = 1; // += 1; XXX don't be strict
    }
};

glam._metrics.prototype.stopEvent = function(id, event_code, param) {
    var elapsed = new Date().getTime() - this.tdata.loadtime;
    
    var event_key = event_code;
    if (param) {
        event_key += ":" + param;
    }        
    gadgets.log("event stopped:" + event_key + " after " + elapsed + "ms on gadget:" + id);
    
    var d = this.tdata.gadgets[id];
    if (!d) {
        gadgets.log("error recording event!");
        return;
    }    
    var e = d.duration[event_key];
    if (e && e.active) {
        var t = elapsed - e.active;
        e.duration += t;
        delete e.active;
        
        d.dirty = 1;
        d.active = 0; // -= 1; XXX don't be strict
    }
};

glam._metrics.prototype.reportEvents = function(thiz) {
    /* reporting codes look like:
       "e:foo,s:100+102+110;e:bar,s:333"
       "e:foo,s:1294,c:4,d:7442;e:bar,s:2222"
       
       ':' is k/v delim
       '+' is value delim (special case for repeated events)
       ',' is k/v pair delim
       ';' is event delim
    */
    for (var key in thiz.tdata.gadgets) {
        var d = thiz.tdata.gadgets[key];
        
        var sinceLastReport = new Date().getTime() - thiz.tdata.lastReport;
        var activeEventLimit = 1000 * Math.min(60, Math.pow(2, thiz.tdata.reportCount));
        // console.log("since last report:" + sinceLastReport + "  activeEventLimit:" + activeEventLimit);
        
        if (d.dirty || (d.active > 0 && sinceLastReport > activeEventLimit)) {
            var data = [];
            // bundle up the single events
            // form: 'e:NAME,s:T1+T2+T3' (optional p:PARAM)
            for (var evt in d.single) {
                var key = evt.split(':'); // EVENT:PARAM
                var tmp = "e:" + key[0] + ",s:" + d.single[evt].join('+');
                if (key[1]) {
                    tmp += ",p:" + key[1];
                }                        
                data.push(tmp);
            }
            // bundle up the duration events
            // form: 'e:NAME,s:T1,c:COUNT,d:DURATION'
            for (var evt in d.duration) {
                var e = d.duration[evt];
                var dur = e.duration;
                if (e.active) {
                    var now = new Date().getTime() - thiz.tdata.loadtime;
                    var sinceStart = now - e.active;
                    // console.log(e.event + " active for " + sinceStart);
                    dur += sinceStart;
                }
                dur = Math.min(dur, 240000); // 4m max
                var tmp = "e:" + e.event + ",s:" + e.start + ",c:" + e.count + ",d:" + dur;
                if (e.param) {
                    tmp += ",p:" + e.param;
                }
                data.push(tmp);
            }
    
            // now report it
            if (data.length) {
                // console.log("events for gadget:" + key);
                // build the basic id param string for this gadget XXX should only do this once
        
                var pre = "a=e&b=" + d.data.developerId + "&m=" + d.data.appId + "&i=" + d.data.installId + "&g=" + d.data.gadgetId + "&v=" + d.data.view + "&d=" + d.data.adId + '&t=' + d.data.idType;
                // page specific data that doesn't change
                pre += "&p=" + thiz.tdata.publisherId + "&w=" + thiz.tdata.pageviewId + "&s=" + thiz.tdata.sessionId;
            
                var img = new Image();
                var rand = (Math.random()).toString(36).slice(-4);
                //var url = 'http://track.tinker.com:8888/clear.gif?r=' + rand + '&' + pre + '&e=' + data.join(';');
                var url = 'http://www13.glam.com/clear.gif?r=' + rand + '&' + pre + '&e=' + data.join(';');
                if (d.data.refer) {
                    url += "&o=" + encodeURIComponent(d.data.refer);
                    d.data.refer = null; // only send this in the first report
                }                
                img.src = url;
                
                thiz.tdata.lastReport = new Date().getTime();
                thiz.tdata.reportCount += 1;                
            }
            // clear out single events so we don't resent them on each report
            d.single = {};
            d.dirty = 0;
            // TODO: if an event's duration has hit the max, deschedule it so no future events will be reported                    
        }
    }
    if ((new Date().getTime() - thiz.tdata.loadtime) < 600000)  {//10 min
        setTimeout(function() { thiz.reportEvents(thiz); }, 5000);
    }
}

glam.metrics = new glam._metrics();


