/**
 * @author Jae Cho
 * set of utility functions
 */



/**
 * context bind method
 * in JS, the keyword 'this' means the current closure instead of the
 * object in which the method is defined. This causes problems in callback
 * methods. Use this bind method to bind object to the method
 * got it from http://fn-js.info/snippets/bind
 * @param obj
 * @param fun
 * @param args
 */
function bindContext(obj, fun, args) {
  return function() {
    if (obj === true)
      obj = this;
    var f = typeof fun === "string" ? obj[fun] : fun;

    return f.apply(obj, Array.prototype.slice.call(args || [])
        .concat(Array.prototype.slice.call(arguments)));
  };
}



/**
* CookieUtil
* utility class for handling cookies
* copied from dojo.cookie
*/
function CookieUtil (){
  this.cookie = function(name,value,props){
	var c = document.cookie;
	if(arguments.length == 1){
		var idx = c.lastIndexOf(name+'=');
		if(idx == -1){ return null; }
		var start = idx+name.length+1;
		var end = c.indexOf(';', idx+name.length+1);
		if(end == -1){ end = c.length; }
		return decodeURIComponent(c.substring(start, end)); 
	}else{
		props = props || {};
		value = encodeURIComponent(value);
		if(typeof(props.expires) == "number"){
			var d = new Date();
			d.setTime(d.getTime()+(props.expires*24*60*60*1000));
			props.expires = d;
		}
		document.cookie = name + "=" + value
			+ (props.expires ? "; expires=" + props.expires.toUTCString() : "")
			+ (props.path ? "; path=" + props.path : "")
			+ (props.domain ? "; domain=" + props.domain : "")
			+ (props.secure ? "; secure" : "");
		return null;
    }
  };
     /**
     * set AG Status cookie
     * @param key
     * @param value
     */
    this.setAgStatus = function(key,value){
      var status = this.cookie("widgetStatus");
      if (status) {
        var _agStatus = JSON.parse(status,function(key,value){
            return value;
        });
        if(!_agStatus){_agStatus = new Object();}
        _agStatus[key] = value;
        this.cookie("widgetStatus",JSON.stringify(_agStatus), {"expires":1});
      }
    };

    /**
     * get AG Status value for key
     * @param key
     */
    this.getAgStatus = function(key){

      var status = this.cookie("widgetStatus");
      if (status) {
        var _agStatus =  JSON.parse(status,function(key,value){
            return value;
        });
        if(_agStatus) return _agStatus[key];
        }
            return null;

    };

    /**
     * erase cookie
     * @param name
     */
    this.eraseCookie = function(name) {
        createCookie(name,"",-1);
    };
}


/**
 * StringUtil
 */
function StringUtil() { }

/** delimiters used to input multiple email addess */
StringUtil.EMAIL_DELIMITER_PATTERN = /[\,\r\n\f\v:]+/;

/** pattern for email */
StringUtil.EMAIL_PATTERN = /^(?:[\w\.])+@(?:[\w]+)(?:\.(?:[\w])+)+/;

/** pattern for name jae <jaecho@yo.com> */
StringUtil.NAME_PATTERN1 = /<([\S+]+)>\s*([\w ]+)/;
StringUtil.NAME_PATTERN2 = /([\w ]+)\s*<([\S+]+)>/;


/** returns true if str is a valid email */
StringUtil.isValidEmail = function(str) {
    return str.match(this.EMAIL_PATTERN);
};

StringUtil.trim = function (stringToTrim) {
    return stringToTrim.replace(/^\s+|\s+$/g,"");
};


/** @return array of email objects for each entry
* status - valid, invalid
* email - parsed email address
* name - name if < > is provided
*/
StringUtil.parseEmails = function(emails) {
    var entries = emails.split(this.EMAIL_DELIMITER_PATTERN);
    var parsedEntries = new Array();

    for (var i = 0; i < entries.length; i++) {

        var string = entries[i];
        if (!string) continue;

        string = this.trim(string);

        var parsedEntry  = this.parseEntry(string);
        parsedEntries.push(parsedEntry);

        if (this.EMAIL_PATTERN.test(parsedEntry.email)) {
            parsedEntry.status = "valid";
        } else {
            parsedEntry.status = "invalid";
        }
    }
    return parsedEntries;
};

/**
 * parse an entry in email list string into name and email address.
 * @param string
 */
StringUtil.parseEntry = function(string) {
    var res;
    var parsedEntry = new Object();
    parsedEntry.string = string;

    res = string.match(this.NAME_PATTERN1);
    if (res) {
        parsedEntry.name = res[2];
        parsedEntry.email = res[1];
        return parsedEntry;
    }

    res = string.match(this.NAME_PATTERN2);
    if (res) {
        parsedEntry.email = res[2];
        parsedEntry.name = res[1];
        return parsedEntry;
    }

    parsedEntry.email = string;
    return parsedEntry;
};



/** return a map containing request query parameters and values in string
 * for example, request with http://yo.com?v0=1&v1=2 will return
 * { "v0" : "1", "v1" : "2" }
 * @param queryStr value of window.location.search variable
 */
StringUtil.getQueryParams = function(queryStr) {

    var query = queryStr.substring(1);
    var vars = query.split("&");
    for (var i=0;i<vars.length;i++) {
        var pair = vars[i].split("=");
        vars[pair[0]] = pair[1];
    }
    return vars;
};

/** return a string chopped up into n characters, separated by spaces.
 * This is used to force a wrap on extra long words, to prevent display issues
 *
 * @param str = string to be chopped
 * @param charMax = number of characters per segment
 */
StringUtil.wordChopper = function(str, charMax){
    if (!str) return str;

    var strArr = str.split(" ");
    for (i=0;i<strArr.length;i++){
       var charCount = strArr[i].length;
       if (charCount < charMax){
           //don't modify the string
           continue;
       }
      var segCount = charCount / charMax;
      var newStr = "";
      for (j=0; j<=segCount;j++){
          newStr += strArr[i].substr(j*charMax,charMax) + " ";
      }
      strArr[i] = newStr;
    }
  return strArr.join(" ");
}
/**
 * DateUtil
 */
function DateUtil() { }

DateUtil.SECOND_MS = 1000;
DateUtil.MIN_MS = DateUtil.SECOND_MS * 60;
DateUtil.HOUR_MS = DateUtil.MIN_MS * 60;
DateUtil.DAY_MS = DateUtil.HOUR_MS * 24;


DateUtil.calculateAge = function(time1, time2) {
    var diff = time1 - time2;
    return DateUtil.formatAge(diff);
};

DateUtil.formatAge = function(ageInMs) {

    var diffInUnit;
    var unit;

    if (ageInMs <= DateUtil.MIN_MS) {
        diffInUnit = Math.round(ageInMs / DateUtil.SECOND_MS);
        unit = diffInUnit == 1 ? "second" : "seconds";
    } else if (ageInMs <= DateUtil.HOUR_MS) {
        diffInUnit = Math.round(ageInMs / DateUtil.MIN_MS);
        unit = diffInUnit == 1 ? "minute" : "minutes";
    } else if (ageInMs <= DateUtil.DAY_MS) {
        diffInUnit = Math.round(ageInMs / DateUtil.HOUR_MS);
        unit = diffInUnit == 1 ? "hour" : "hours";
    } else {
        diffInUnit = Math.round(ageInMs / DateUtil.DAY_MS);
        unit = diffInUnit == 1 ? "day" : "days";
    }

    return diffInUnit + " " + unit;
};

DateUtil.dojoDifference = function(date1, date2, period) {
    return dojo.date.difference(date1, date2, period);
};

function ProfileUtil() {};

/**
 * get profile image path
 * @param args - map of various argments
 * - userId or userUuid - one of them is required
 * - size (optional) - "large" or "small (default)"
 * If true, server will show image in moderation queue if present.
 * If true, append date string so that browser does not cache the image
 */
ProfileUtil.getImg = function(args) {
    var path = "/dynamic/photo/userPic?";
    var defaultPath = "/static/images/user_pic_default_";
    var self = false;
    var cutil = new CookieUtil();

    if (!args.userPicSet && typeof args.userPicSet != "undefined"){
        if (args.size)return defaultPath + args.size + ".jpg";
        else return defaultPath + "small.jpg";
    }
    if (args.userId) {
        path += "userId=" + args.userId;
        if (cutil.cookie("aguseridsc") == args.userId) self = true;

    } else if (args.userUuid) {
        path += "userUuid=" + args.userUuid;
        if (cutil.cookie("aguseruuidsc") == args.userUuid) self = true;

    } else {
        return null;
    }

    if (args.size == "big") path += "&thumbnail=false";
    else path += "&thumbnail=true";

    if (self) {
        path += "&self=true";
        path += "&date=" + (new Date()).getTime();
    }
    return path;
};

/* Plugin to clear form data
   Usage: $('form').clearForm()  or $('#registration_form').clearForm()
 */
$.fn.clearForm = function() {
 return this.each(function() {
   var type = this.type, tag = this.tagName.toLowerCase();
   if (tag == 'form')
     return $(':input',this).clearForm();
   if (type == 'text' || type == 'password' || tag == 'textarea')
     this.value = '';
   else if (type == 'checkbox' || type == 'radio')
     this.checked = false;
   else if (tag == 'select')
     this.selectedIndex = 0;
 });
};


// Event classes are used for inter-module communication
// There are three main methods - subscribe, publish, unsubscribe
function AgEvent() {
    this.subscribers = [];

    // publish the event to all subscribers
    this.publish = function(data) {
      for (var index in this.subscribers){
         this.subscribers[index](data);
        }
    };
    // subscribe a callback method to an event
    this.subscribe = function(callback) {
        this.subscribers.push(callback);
        return callback;
    };

    // unsubscribe a callback method from an event
    this.unsubscribe = function(callback) {
        var subscribersNew =[];
        for (var currentCallback in this.subscribers){
         if (callback != currentCallback){
           subscribersNew.push(currentCallback);
         };
        };
        this.subscribers = subscribersNew;
    };

    // subscribe to the event only once
    this.subscribeOnce = function(callback) {
        var wrapper = new AgEventWrapper();
        wrapper._event = this;
        wrapper._callback = callback;
        wrapper._subscribingCallback = bindContext(wrapper, wrapper.call);
        this.subscribers.push(wrapper._subscribingCallback);
    };
};


/** used to implement subscribeOnce */
function AgEventWrapper() {
    this._event = null;
    this._callback = null;
    this._subscribingCallback = null;
    
    this.call = function(data) {
        this._callback(data);
        this._event.unsubscribe(this._subscribingCallback);
    };
};

// Events
AgEvent.LOGIN = new AgEvent();
AgEvent.CANCEL_LOGIN = new AgEvent();
AgEvent.REGISTER = new AgEvent();
AgEvent.CANCEL_REGISTER = new AgEvent();
AgEvent.LOGOUT = new AgEvent();
