/**
 * Tesla namespace
 * @const
 */
var tesla = tesla || {};
/**
 * @type {boolean}
 */
tesla.DEBUG = true;
/**
 * @return {number}
 */
tesla.now = function(){
	return (+new Date)	
};
/**
 * utils namespace
 * @const
 */
tesla.utils = tesla.utils || {};
/**
 * @param {number} value
 * @return {string}
 */
tesla.utils.pack = function(value){
	return (value).toString(32);
};
/**
 * @param {string} value
 * @return {number}
 */
tesla.utils.unpack = function(value){
	return parseInt(value, 32)
};
/**
 * date namespace
 * @const
 */
tesla.date = tesla.date || {};
/**
 * @type {Array.<number>}
 */
tesla.date.DAY_MAP = [6, 0, 1, 2, 3, 4, 5];
/**
 * @param {Date} date
 * @return {number}
 */
tesla.date.getUTCWeek = function(date){
	var d = date.getUTCDate(), day = tesla.date.DAY_MAP[date.getUTCDay()];
	return Math.ceil((d - day) / 7) - 1;
};

/**
 * @param {Document} context
 * @constructor
 */
tesla.Cookie = function(context){
	this.document_ = context;	
};
/**
 * @type {number}
 */
tesla.Cookie.prototype.MAX_COOKIE_LENGTH = 3950;
/**
 * @type {RegExp}
 * @private
 */
tesla.Cookie.prototype.SPLIT_RE_ = /\s*;\s*/;
/**
 * @param {string} s
 * @private
 */
tesla.Cookie.prototype.setCookie_ = function(s){
	return this.document_.cookie = s;
};
/**
 * @return {string}
 * @private
 */
tesla.Cookie.prototype.getCookie_ = function(){
	return this.document_.cookie;
};
/**
 * @return {Array}
 * @private
 */
tesla.Cookie.prototype.getParts_ = function(){
	return (this.getCookie_() || '').split(this.SPLIT_RE_);
};
/**
 * @param {string} name
 * @return {boolean}
 */
tesla.Cookie.prototype.isValidName = function(name){
	return !(/[;=\s]/.test(name));
};
/**
 * @param {string} value
 * @return {boolean}
 */
tesla.Cookie.prototype.isValidValue = function(value){
	return !(/[;\r\n]/.test(value));
};
/**
 * @param {string} name
 * @param {string=} opt_default
 * @return {string|undefined}
 */
tesla.Cookie.prototype.get = function(name, opt_default){
	var nameEq = name + '=';
	var parts = this.getParts_();
	for(var i = 0, part; part = parts[i]; i++){
		if(part.indexOf(nameEq) == 0){
			return part.substr(nameEq.length);
		}
	}
	return opt_default;
};
/**
 * @throws {!Error} If the {@code name} fails #top100.cookies.isValidName.
 * @throws {!Error} If the {@code value} fails #top100.cookies.isValidValue.
 *
 * @param {string} name
 * @param {string} value
 * @param {number|Date=} opt_maxAge 
 * @param {string=} opt_path
 * @param {string=} opt_domain
 * @param {boolean=} opt_secure
 */
tesla.Cookie.prototype.set = function(name, value, opt_maxAge, opt_path, opt_domain, opt_secure){
	if(!this.isValidName(name)){
		throw new Error('Invalid cookie name "' + name + '"');	
	}
	if(!this.isValidValue(value)){
		throw new Error('Invalid cookie value "' + value + '"');
	}
	var domainStr = opt_domain ? ';domain=' + opt_domain : '';
	var pathStr = opt_path ? ';path=' + opt_path : '';
	var secureStr = opt_secure ? ';secure' : '';		
	var expiresStr;
	
	if (opt_maxAge instanceof Date){
		expiresStr = ';expires=' + opt_maxAge.toUTCString();
	}else if (opt_maxAge < 0) {
		expiresStr = '';
	} else if (opt_maxAge == 0) {
		var pastDate = new Date(1970, 1, 1);
		expiresStr = ';expires=' + pastDate.toUTCString();
	} else {
		var futureDate = new Date((+new Date) + opt_maxAge * 1000);
		expiresStr = ';expires=' + futureDate.toUTCString();
	}
	this.setCookie_(name + '=' + value + domainStr + pathStr + expiresStr + secureStr);
};
/**
 * @constructor
 */
tesla.VisitorTracker = function(){
	this.cookie_ = new tesla.Cookie(document);
};
/**
 * @type {string}
 * @private
 */
tesla.VisitorTracker.prototype.PREFIX_ = 'visitors';
/**
 * @type {number}
 * @private
 */
tesla.VisitorTracker.prototype.visitorTimeout_ = 65 * 24 * 60 * 60; // 65 day
/**
 * @type {string}
 * @private
 */
tesla.VisitorTracker.prototype.cookieNamePrefix_ = '__tesla_';
/**
 * @type {?string}
 * @private
 */
tesla.VisitorTracker.prototype.cookieDomain_ = null;
/**
 * @type {?string}
 * @private
 */
tesla.VisitorTracker.prototype.cookiePath_ = null;
/**
 * @enum {string}
 */
tesla.VisitorTracker.prototype.STATUS = {
	NEW: "new",
	RETURNED: "returned",
	REPEATED: "repeated"
};
/**
 * @enum {string}
 */
tesla.VisitorTracker.prototype.SLICE = {
	TOTAL: "total",
	MONTH: "monthly",
	WEEK: "weekly",
	DAY: "daily"
};
/**
 * @type {number}
 */
tesla.VisitorTracker.prototype.MAX_TIMESTAMP = 2145916555000; // 2037.12.31 23:55:55 UTC
/**
 * @type {number}
 */
tesla.VisitorTracker.prototype.MIN_TIMESTAMP = 1322697600000; // 2011.12.01 00:00:00 UTC
/**
 * @type {string}
 */
tesla.VisitorTracker.prototype.PART_SEPARATOR = '.';
/**
 * @type {string}
 */
tesla.VisitorTracker.prototype.PATH_SEPARATOR = '-';
/**
 * @type {RegExp}
 * @private
 */
tesla.VisitorTracker.prototype.SPLIT_RE_ = /\s*;\s*/;
/**
 * @param {number} timeoutMillis
 */
tesla.VisitorTracker.prototype.setVisitorTimeout = function(timeoutMillis){
	this.visitorTimeout_ = timeoutMillis;
};
/**
 * @return {number}
 */
tesla.VisitorTracker.prototype.getVisitorTimeout = function(){
	return this.visitorTimeout_;
};
/**
 * @param {string} prefix
 */
tesla.VisitorTracker.prototype.setCookieNamePrefix = function(prefix){
	this.cookieNamePrefix_ = prefix;
};
/**
 * @return {string}
 */
tesla.VisitorTracker.prototype.getCookieNamePrefix = function(){
	return this.cookieNamePrefix_;
};

/**
 * @return {string}
 */
tesla.VisitorTracker.prototype.getFullCookieName = function(accountId){
	return this.cookieNamePrefix_ + accountId;
};
/**
 * @param {string} name
 * @param {string=} opt_default
 * @return {string|undefined}
 */
tesla.VisitorTracker.prototype.getCookie = function(name, opt_default){
	return this.cookie_.get(name, opt_default);	
}
/**
 * @param {string} name
 * @param {string} value
 * @param {number|Date=} opt_maxAge 
 * @param {string=} opt_path
 * @param {string=} opt_domain
 */
tesla.VisitorTracker.prototype.setCookie = function(name, value, opt_maxAge, opt_path, opt_domain){
	this.cookie_.set(name, value, this.getVisitorTimeout(), this.getCookiePath(), this.getDomainName())
};
/**
 * @param {string} domain
 */
tesla.VisitorTracker.prototype.setDomainName = function(domain){
	this.domainName_ = domain;
};
/**
 * @return {string|undefined}
 */
tesla.VisitorTracker.prototype.getDomainName = function(){
	return this.domainName_ || window.location.hostname;
};
/**
 * @param {string} path
 */
tesla.VisitorTracker.prototype.setCookiePath = function(path){
	this.cookiePath_ = path;
};
/**
 * @return {string|undefined}
 */
tesla.VisitorTracker.prototype.getCookiePath = function(){
	return this.cookiePath_ || '/';
};
/**
 * @param {...} var_args
 * @return {string}
 */
tesla.VisitorTracker.prototype.createPath = function(var_args){
	return Array.prototype.slice.call(arguments).join(this.PART_SEPARATOR);
};
/**
 * @param {number} timestamp
 * @return {boolean}
 */
tesla.VisitorTracker.prototype.isValidPeriod = function(timestamp){
	return (timestamp > this.MIN_TIMESTAMP) && (timestamp < this.MAX_TIMESTAMP);
};
/**
 * @param {Object} tracker
 * @param {string=} opt_path
 */
tesla.VisitorTracker.prototype.trackVisitor = function(tracker, opt_path){
	/**
	 * @param {string=} opt_prefix
	 * @param {string=} opt_partSeparator
	 * @return {Function}
	 */
	function pathMaker(opt_prefix, opt_partSeparator){
		var partSeparator = opt_partSeparator || '.';
		/**
		 * @param {...} var_args
		 * @return {string}
		 */
		return function(var_args){
			var args = Array.prototype.slice.call(arguments);
			if(opt_prefix){
				args.unshift(opt_prefix);
			}
			return args.join(partSeparator);
		}
	}
	
	var accountId = tracker['_accountId'];
	var prefix = opt_path ? [opt_path, this.PREFIX_].join(this.PART_SEPARATOR) : this.PREFIX_;
	var timestamp = this.getCookie(this.getFullCookieName(accountId), '');
	var paths = [];
	var make = pathMaker(prefix);
	
	if(!timestamp){
		// EMPTY COOKIE, IT'S A NEW USER
		// reset timestamp
		timestamp = tesla.now();
		if(tesla.DEBUG){
			paths.push("debug.empty_cookie");
		}
		// absolute new visitor
		paths.push(
			make(this.SLICE.TOTAL, this.STATUS.NEW),
			make(this.SLICE.MONTH, this.STATUS.NEW),
			make(this.SLICE.WEEK, this.STATUS.NEW),
			make(this.SLICE.DAY, this.STATUS.NEW)
		);
	}else{
		timestamp = parseInt(timestamp, 10);
		if(!timestamp){
			// INVALID COOKIE VALUE, IT'S A NEW USER
			// reset timestamp
			timestamp = tesla.now();
			if(tesla.DEBUG){
				paths.push("debug.invalid_cookie_value");
			}
			// absolute new visitor
			paths.push(
				make(this.SLICE.TOTAL, this.STATUS.NEW),
				make(this.SLICE.MONTH, this.STATUS.NEW),
				make(this.SLICE.WEEK, this.STATUS.NEW),
				make(this.SLICE.DAY, this.STATUS.NEW)
			);
		}else{
			if(!this.isValidPeriod(timestamp)){
				// INVALID PERIOD, IT'S A NEW USER
				// reset timestamp
				timestamp = tesla.now();
				if(tesla.DEBUG){
					paths.push("debug.invalid_period");
				}
				// absolute new visitor
				// reset timestamp
				paths.push(
					make(this.SLICE.TOTAL, this.STATUS.NEW),
					make(this.SLICE.MONTH, this.STATUS.NEW),
					make(this.SLICE.WEEK, this.STATUS.NEW),
					make(this.SLICE.DAY, this.STATUS.NEW)
				);
			}else{
				var date = new Date(timestamp), current = new Date()
				if(timestamp > current.getTime()){
					// reset timestamp
					timestamp = tesla.now();
					if(tesla.DEBUG){
						paths.push("debug.first_visit_time_gt_current_time");
					}
					// absolute new visitor
					paths.push(
						make(this.SLICE.TOTAL, this.STATUS.NEW),
						make(this.SLICE.MONTH, this.STATUS.NEW),
						make(this.SLICE.WEEK, this.STATUS.NEW),
						make(this.SLICE.DAY, this.STATUS.NEW)
					);
				}else{
					// total repeated
					paths.push(make(this.SLICE.TOTAL, this.STATUS.REPEATED));
					// check year
					if(date.getUTCFullYear() == current.getUTCFullYear()){
						if(date.getUTCMonth() == current.getUTCMonth()){
							// month new
							paths.push(make(this.SLICE.MONTH, this.STATUS.NEW));
							if(tesla.date.getUTCWeek(date) == tesla.date.getUTCWeek(current)){
								// new week
								paths.push(make(this.SLICE.WEEK, this.STATUS.NEW));
								if(date.getUTCDate() == current.getUTCDate()){
									// day new
									paths.push(make(this.SLICE.DAY, this.STATUS.NEW));
								}else{
									// FIRST VISIT DAY < CURRENT DAY, IT'S RETURNED VISITOR
									if(tesla.DEBUG){
										paths.push("debug.day_lt");
									}
									paths.push(make(this.SLICE.DAY, this.STATUS.RETURNED));
								}
							}else{
								// FIRST VISIT WEEK < CURRENT WEEK, IT'S RETURNED VISITOR
								if(tesla.DEBUG){
									paths.push("debug.week_lt");
								}
								paths.push(
									make(this.SLICE.WEEK, this.STATUS.RETURNED),
									make(this.SLICE.DAY, this.STATUS.RETURNED)
								);
							}
						}else{
							// FIRST VISIT MONTH < CURRENT MONTH, IT'S RETURNED VISITOR
							if(tesla.DEBUG){
								paths.push("debug.month_lt");
							}
							paths.push(
								make(this.SLICE.MONTH, this.STATUS.RETURNED),
								make(this.SLICE.WEEK, this.STATUS.RETURNED),
								make(this.SLICE.DAY, this.STATUS.RETURNED)
							);
						}
					}else{
						// FIRST VISIT YEAR < CURRENT YEAR, IT'S RETURNED VISITOR
						if(tesla.DEBUG){
							paths.push("debug.year_lt");
						}
						// returned visitor
						paths.push(
							make(this.SLICE.MONTH, this.STATUS.RETURNED),
							make(this.SLICE.WEEK, this.STATUS.RETURNED),
							make(this.SLICE.DAY, this.STATUS.RETURNED)
						);
					}
				}
			}
		}
	}
	
	//console.log(paths)
	tracker["track"](paths.join(this.PATH_SEPARATOR));
	this.setCookie(this.getFullCookieName(accountId), ""+timestamp);
};

