/**
 * PollModel
 * @module pollmodel
 * @requires yahoo, dom, event, element, profiler, yuiloader
 */

function isString(){
    if (typeof arguments[0] == 'string') 
        return true;
    if (typeof arguments[0] == 'object') {
        var criterion = arguments[0].constructor.toString().match(/string/i);
        return (criterion != null);
    }
    return false;
}

/*
 *         0123456789
 * convert mm/dd/yyyy to yyyymmdd for sorting
 */
function iso_date(us_date){
	return us_date.substring(6) + us_date.substring(0, 2) + us_date.substring(3, 5);
}

function copy_clip(meintext){
    if (window.clipboardData) {
        window.clipboardData.setData("Text", meintext);
    }
    else {
        var flashcopier = 'flashcopier';
        if (!document.getElementById(flashcopier)) {
            if (!YAHOO.util.FlashDetect.installed) {
                alert('You have to have Adobe Flash installed to use this functionality.');
            }
            var divholder = document.createElement('div');
            divholder.id = flashcopier;
            document.body.appendChild(divholder);
        }
        document.getElementById(flashcopier).innerHTML = '';
        var divinfo = '<embed src="/js/_clipboard.swf" FlashVars="clipboard=' + encodeURIComponent(meintext) + '" width="0" height="0" type="application/x-shockwave-flash"></embed>';
        document.getElementById(flashcopier).innerHTML = divinfo;
    }
}

var indexOfChoice = function(container, obj){
    var result = -1;
    for (var i = 0; i < container.length; i++) {
        if (container[i].date == obj) {
            result = i;
            break;
        }
    };
    return result;
};

var indexOfChoice2 = function(container, obj){
    var result = -1;
    for (var i = 0; i < container.length; i++) {
        if (container[i].optionId == obj) {
            result = i;
            break;
        }
    };
    return result;
};

/**
 * Queue Class <br>
 * <strong>Singleton.</strong>
 * @class Queue
 * @constructor
 */
var Queue = new function() {
	var that = this,
		running = false,
		q = [];

	/**
	 * 
	 */
	this.run = function() {				
		if (running) {
			return this;
		}		
		if (!q[0]) {
			return this;
		}
		
		var obj = q.shift();
		
		/**
		 * @method handleSuccess
		 * @param {Object} o
		 * @private
		 */
		var handleSuccess = function(o) {
	        if(obj.spinners) {
	        	var i;
	        	while(i = obj.spinners.shift()) {
	        		i.stop();
	        	}
	        }
			running = false;
			that.onAfterRequest.fire(o);
            that.run();
        };
        
        /**
         * @method handleFailure
         * @param {Object} o
         * @private
         * TODO: error handling
         */
        var handleFailure = handleSuccess;
                
        var callback =  obj.callback || {
            success: handleSuccess,
            failure: handleFailure            
        };
        
        if(obj.spinners) {
        	var i;
        	for(i=0;i<obj.spinners.length;i++) {
        		obj.spinners[i].start();
        	}
        }
        
        that.onBeforeRequest.fire();
        var request = YAHOO.util.Connect.asyncRequest(obj.method || 'POST', obj.url, callback, obj.params);
	};
	
	/**
	 * @param {Object} obj 
	 */
	this.add = function(obj) {
		q.push(obj);
		that.run();
	};
	
	/**
	 * @event onBeforeRequest
	 */
	this.onBeforeRequest = new YAHOO.util.CustomEvent('onBeforeRequest', that);
	
	/**
	 * @event onAfterRequest
	 */
	this.onAfterRequest = new YAHOO.util.CustomEvent('onAfterRequest', that);
}();

/**
 * The PollModel Class
 * @class PollModel
 * @constructor
 * @param {Object} Config
 */
var PollModel = function(config){
    var that = this;
    config = config ||
    {};
    
    /**
     * Choices
     * @property Choices
     * @type Array[] (Date string[])
     */
	/**
	 * Participants
     * @property Participants
     * @type Array[] (Participant[])
	 */
	/**
	 * Votes
     * @property Votes
     * @type Array[] (Vote[])
	 */
	/**
	 * statistics
     * @property statistics
     * @type Array[]
	 */
	/**
	 * Result
     * @property Result
	 */
	/**
	 * dirty
     * @property dirty
     * @type boolean
     * @default false
	 */      
    /**
     * Poll Id
     * @property id
     * @type string
     */
     
    this.Choices = [];	
    this.Participants = [];	
    this.Votes = {};	
    this.statistics = {};	
    this.Result = null;	
    this.ResultId = null;
    this.ResultText = null;
    this.dirty = false;
    this.id = config.id;
    this.lastIndex = 0;
    
    this.incIndex = function() {
    	this.lastIndex=this.lastIndex+1;
    	return this.lastIndex;
    }
    
	/**
	 * setVote
	 * @param {Object} choiceId
	 * @param {Object} pref
	 * @param {Object} voterId
	 */
    this.setVote = function(choiceId, pref, voterId){
    	if(that.Result || that.ResultId) return false;
        var found = false;
        var votes = that.Votes[choiceId];
        if (votes) {
            for (var i = 0; i < votes.length; i++) {
                if (votes[i].voterId == voterId) {
                    votes[i].pref = pref;
                    found = true;
                    break;
                }
            }
        }
        if (!found) {
            if (!votes) {
                that.Votes[choiceId] = [];
            }
            that.Votes[choiceId].push({
                "optionId": choiceId,
                "voterId": voterId,
                "pref": pref
            });
        }
        that.generateStatistics();
        that.dirty = true;
        that.onVoteModified.fire(choiceId, voterId, pref);
    };
    
    this.setResult = function(choiceId) {
    	var oldResult = that.ResultId;
    	var choice = that.getChoiceById(choiceId);
    	that.Result=choice.date;
    	that.ResultId=choiceId;
    	that.ResultText=choice.text;
    	that.onResultModified.fire(oldResult,that.ResultId);
    }
    
	/**
	 * @param {Object} voterId
	 * @param {Object} opinion
	 */
    this.setOpinion = function(voterId, opinion){
        that.getVoterById(voterId).opinion = opinion;
        that.onOpinionModified.fire(voterId, opinion);
    };
    
	/**
	 * 
	 */
    this.generateStatistics = function(){
        that.statistics = {};
        var fc = that.getChoicesFlat();
        var i;
        for (i in fc) {
        	if(!fc.hasOwnProperty(i)) continue;
            var option = fc[i].optionId.toString();
            
            that.statistics[option] = {
                yes: 0,
                maybe: 0,
                no: 0,
                total: 0
            };
            
            var votes = that.Votes[option];
            if (votes) {
                for (var j = 0; j < votes.length; j++) {
                    var vote = votes[j];
                    switch (Number(vote.pref)) {
                        case 1:
                            that.statistics[option].yes++;
                            that.statistics[option].total++;
                            break;
                        case 2:
                            that.statistics[option].maybe++;
                            that.statistics[option].total++;
                            break;
                        case 3:
                            that.statistics[option].no++;
                            that.statistics[option].total++;
                            break;
                    }
                }
            }
            
            // make up missing votes
            if (that.statistics[option].total < that.Participants.length) {
                that.statistics[option].maybe += that.Participants.length - that.statistics[option].total;
                that.statistics[option].total = that.Participants.length;
            }
        }
    };
    
	/**
	 * 
	 * @param {Object} choiceId
	 */
    this.getChoiceById = function(choiceId){
    	var i,o;
    	var choice, fchoice;

    	for(i=0;i<that.Choices.length;i++) {
    		choice =that.Choices[i]; 
    		if(choice.optionId != choiceId) {
    			if( choice.free !== undefined ) {
	    			for(o=0;o<choice.free.length;o++) {
	    				fchoice = choice.free[o]; 
	    				if(fchoice.optionId == choiceId) {
	    					return fchoice;
	    				}    				
	    			}    				
    			}
    		}
    		else {
    			return choice;
    		}
    	}
    };
    
	/**
	 * 
	 * @param {Object} date
	 */
    this.getChoiceByDate = function(date){
        return that.Choices[indexOfChoice(that.Choices, date.toString("MM/dd/yyyy"))];
    };
    
	/**
	 * 
	 * @param {Object} dateStr
	 */
    this.getChoiceByDateStr = function(dateStr){
        return that.Choices[indexOfChoice(that.Choices, dateStr)];
    };
    
	/**
	 * 
	 * @param {Object} date
	 */
    this.getChoiceIdByDate = function(date){
        return that.Choices[indexOfChoice(that.Choices, date.toString("MM/dd/yyyy"))].optionId;
    };
    
	/**
	 * 
	 * @param {Object} dateStr
	 */
    this.getChoiceIdByDateStr = function(dateStr){
        return that.Choices[indexOfChoice(that.Choices, dateStr)].optionId;
    };
    
	/**
	 * 
	 */
    this.getChoices = function(){
    	var fc = that.getChoicesFlat();
    	var i;
    	var emptys=[];
    	var choice;
    	
    	for(i in fc) {
    		if(fc.hasOwnProperty(i)) {
    			choice = fc[i];
    			if(choice.free === undefined && !choice.text) {
    				emptys.push(choice.optionId);
    			}
    		}
    	}
    	
    	for(i=0;i<emptys.length;i++) {
    		that.removeChoiceById(emptys[i]);
    	}
    	
        return YAHOO.lang.JSON.stringify(that.Choices);
    };
    
    this.getChoicesFlat = function() {
    	var i,o;
    	var choice;
    	var fc = {};
    	
    	for(i=0; i<that.Choices.length; i++) {
    		choice = that.Choices[i];
    		fc[("choice_"+choice.optionId)]=choice;
    		if(choice.free) {
    			for(o=0; o<choice.free.length; o++) {
    				fc[("choice_"+choice.free[o].optionId)]=choice.free[o];
    			}
    		}
    	}
    	return fc;
    }
    
	/**
	 * 
	 * @param {Object} participantId
	 */
    this.getVotesByParticipant = function(participantId){
    	var fc = that.getChoicesFlat();
        var votes = [];
        var i;
      
        for (i in fc) {
        	if(!fc.hasOwnProperty(i)) continue;
        	
            var option = fc[i].optionId;
            var vs = that.Votes[option];
            var found = false;
            if (vs) {
                for (var j = 0; j < vs.length; j++) {
                    if (vs[j].voterId == participantId) {
                        votes.push(vs[j]);
                        found = true;
                        break;
                    }
                }
            }
            if (!found) {
                votes.push({
                    "optionId": option,
                    "voterId": participantId,
                    "pref": 2 // maybe
                });
            }
        }
        return votes;
    };
    
	/**
	 * 
	 * @param {Object} choiceId
	 */
    this.getVotesByChoiceId = function(choiceId){
        var votes = [];
        if (that.Votes[choiceId]) {
            for (var i = 0; i < that.Votes[choiceId].length; i++) {
                votes.push(that.Votes[choiceId][i]);
            }
        }
        if (votes.length < that.Participants.length) {
            for (var i = 0; i < that.Participants.length; i++) {
                var found = false;
                var len = votes.length;
                for (var j = 0; j < len; j++) {
                    if (votes[j].voterId == that.Participants[i].id) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    votes.push({
                        "optionId": choiceId,
                        "voterId": that.Participants[i].id,
                        "pref": 2 // maybe by default
                    });
                }
            }
        }
        return votes;
    };
    
	/**
	 * 
	 * @param {Object} optionId
	 * @param {Object} voterId
	 */
    this.getVoteByVoterChoice = function(optionId, voterId){
        var votes = that.Votes[optionId.toString()];
        if (votes) {
            for (var i = 0; i < votes.length; i++) {
                if (votes[i].voterId == voterId) {
                    return Number(votes[i].pref);
                }
            }
        }
        return 2; // maybe by default
    };
    
	/**
	 * 
	 * @param {Object} choiceStr
	 */
    this.getVotesByDateStr = function(choiceStr){
        return that.getVotesByChoiceId(that.getChoiceIdByDateStr(choiceStr));
    };
    
	/**
	 * 
	 * @param {Object} date
	 */
    this.getVotesByDate = function(date){
        return that.getVotesByDateStr(date.toString("MM/dd/yyyy"));
    };
    
	/**
	 * 
	 * @param {Object} voterId
	 */
    this.getVoterById = function(voterId){
        for (var i = 0; i < that.Participants.length; i++) {
            if (that.Participants[i].id == voterId) {
                return that.Participants[i];
            }
        }
    };
    
	/**
	 * 
	 * @param {Object} date
	 */
    this.addChoice = function(date,text){
    	var i;
    	var newIndex = that.incIndex(); 
        var newChoice = {
            "date": date,
            "optionId": newIndex,
            "free":[]
        };
        if(text) {
        	newChoice.text=text;
        }
        
        if(date) {
	        // insert new choice ordered
	        for (i = 0; i < that.Choices.length; i++) {
	            if (iso_date(date) < ((that.Choices[i].date) ? iso_date(that.Choices[i].date) : iso_date("01/01/2020"))) 
	                break;
	        }        	
        }
        else {
        	i=that.Choices.length;
        }
        that.Choices.splice(i, 0, newChoice);
        that.generateStatistics();
        that.dirty = true;
        that.onChoicesSet.fire();
        
        return newIndex;
    }
    
    this.addTextChoice = function(choice,text) {
    	if(choice.free === undefined) {
    		choice.free = [];
    	}
    	var newIndex = that.incIndex();
    	choice.free.push({
    		"date":choice.date,
    		"text":text,
    		"optionId":newIndex
    	});
        that.dirty = true;
        that.onChoicesSet.fire();
        return newIndex;
    }
        
    this.addTextChoiceByDateStr = function(date,text) {
        var choiceIndex = indexOfChoice(that.Choices, date);
        if (choiceIndex != -1) {
            return that.addTextChoice(that.Choices[choiceIndex],text);
        }
    	
    }

    this.addTextChoiceById = function(id,text) {
        var choice = that.getChoiceById(id);
        if (choice) {
            return that.addTextChoice(choice,text);
        }
    	
    }
    
    
	/**
	 * 
	 * @param {Object} date
	 */
    this.removeChoice = function(date){
        that.removeChoiceByDateStr(date);
    }
    
	/**
	 * 
	 * @param {Object} choiceStr
	 */
    this.removeChoiceByDateStr = function(choiceStr){
        var choiceIndex = indexOfChoice(that.Choices, choiceStr);
        if (choiceIndex != -1) {
            that.removeChoiceById(that.Choices[choiceIndex].optionId);
        }
    };
    
	/**
	 * 
	 * @param {Object} choiceId
	 */
    this.removeChoiceById = function(choiceId){
    	var i,o;
    	var choice, fchoice;

    	for(i=0;i<that.Choices.length;i++) {
    		choice =that.Choices[i]; 
    		if(choice.optionId != choiceId) {
    			if( choice.free !== undefined ) {
	    			for(o=0;o<choice.free.length;o++) {
	    				fchoice = choice.free[o]; 
	    				if(fchoice.optionId == choiceId) {
	    					choice.free.splice(o,1);
	    					break;
	    				}    				
	    			}    				
    			}
    		}
    		else {
    			that.Choices.splice(i,1);
    			break;
    		}
    	}
    	that.generateStatistics();
	    that.onChoiceRemoved.fire();
    };
    
	/**
	 * 
	 * @param {Object} choiceIndex
	 */
    this.removeChoiceByIndex = function(choiceIndex){
        if (!that.Choices[choiceIndex]) {
            return;
        }
        var optionId = that.Choices[choiceIndex].optionId.toString();
        var date = that.Choices[choiceIndex].date;
        if (that.Votes[optionId]) 
            that.Votes[optionId] = null;
        that.Choices.splice(choiceIndex, 1);
    };
    

    /**
	 * 
	 * @param {Object} participantId
	 */
    this.removeParticipantById = function(participantId){
		var reload = function(o) {
			that.loadData(YAHOO.lang.JSON.parse(o.responseText));
		}
		var obj = {
	            params: "removeparticipant=" + participantId,
	            url: '/vote/' + pollModel.id + '/',
	            spinners:[],
	            callback: {
	                success: reload,
	                failure: reload                    					
				}
			};
		Queue.add(obj);
    };
    
    this.recalculateLastIndex = function() {
    	var i;
    	var fc;
       	fc = that.getChoicesFlat();
       	for(i in fc) {
       		if(fc.hasOwnProperty(i) && that.lastIndex<fc[i].optionId) {
       			that.lastIndex=fc[i].optionId;
       		}
       	}	
    }
    
	/**
	 * 
	 * @param {Object} data
	 */
    this.loadData = function(data){
    	var i;
        // migration
        if (data.Choices) {
            if (data.Choices.length > 0 && isString(data.Choices[0])) {
                for (i = 0; i < data.Choices.length; i++) {
                    that.Choices.push({
                        "date": data.Choices[i],
                        "optionId": i + 1,
                        "free":[]
                    });
                }
            }
            else {
                that.Choices = data.Choices || [];
                that.recalculateLastIndex();
                for(i=0;i<that.Choices.length;i++) {
                	if(that.Choices[i].free === undefined) {
                		that.Choices[i].free=[];
                	}
                }
            }
        }
        if (data.Votes) {
            if (data.Votes.constructor == Array) {
                for (var i = 0; i < data.Votes.length; i++) {
                    var choice = data.Votes[i].optionId.toString();
                    if (!that.Votes[choice]) 
                        that.Votes[choice] = [];
                    that.Votes[choice].push({
                        "optionId": new Number(choice),
                        "voterId": data.Votes[i].voterId,
                        "pref": data.Votes[i].pref
                    });
                }
            }
            else {
                that.Votes = data.Votes ||
                {};
            }
        }
        that.Participants = data.Participants || [];
        that.Result = data.Result || null;
        that.ResultId = data.ResultId || null;
        if (config.tempname) {
            that.Participants.push({
                id: 'tempId',
                email: '',
                name: config.tempname,
                opinion: ''
            });
            for (var i = 0; i < that.Choices.length; i++) {
                var option = that.Choices[i].optionId.toString();
                if (!that.Votes[option]) 
                    that.Votes[option] = [];
                that.Votes[option].push({
                    optionId: new Number(that.Choices[i].optionId),
                    voterId: 'tempId',
                    pref: 2
                });
            }
        }
        
        that.generateStatistics();
        that.onDataLoaded.fire();
    };
    
	/**
	 * 
	 * @param {String} pollId
	 */
    this.load = function(pollId){
        if (!pollId) 
            return;
        
        var handleSuccess = function(o){
            that.loadData(YAHOO.lang.JSON.parse(o.responseText));
        };
        
        var handleFailure = function(o){
        };
        
        var callback = {
            success: handleSuccess,
            failure: handleFailure,
            argument: {}
        };
        
        var request = YAHOO.util.Connect.asyncRequest('GET', '/rpc/polldata?pollId=' + pollId + '&pId=' + YAHOO.util.Cookie.get("pId"), callback);
    }
    
    this.reload = function() {
        this.Choices = [];	
        this.Participants = [];	
        this.Votes = {};	
        this.statistics = {};	
        this.Result = null;	
        this.dirty = false;
    	this.load(this.id);
    }
    
    
	/**
	 * 
	 */
    this.loadFromCookie = function(){
		that.load(YAHOO.util.Cookie.get("pollId"));
	}
    
    /* Events */
	/**
	 * @event onDataLoaded
	 */
    this.onDataLoaded = new YAHOO.util.CustomEvent("onDataLoaded", that);
	
	/**
	 * @event onVoteModified
	 */
    this.onVoteModified = new YAHOO.util.CustomEvent("onVoteModified", that);

    this.onResultModified = new YAHOO.util.CustomEvent("onResultModified", that);
	
	/**
	 * @event onChoiceRemoved
	 */
    this.onChoiceRemoved = new YAHOO.util.CustomEvent("onChoiceRemoved", that);
	
	/**
	 * @event onChoicesSet
	 */
    this.onChoicesSet = new YAHOO.util.CustomEvent("onChoicesSet", that);
	
	/**
	 * @event onOpinionModified
	 */
    this.onOpinionModified = new YAHOO.util.CustomEvent('onOpinionModified', that);
	
	/**
	 * @event onVoteRemoved
	 */
    this.onVoteRemoved = new YAHOO.util.CustomEvent('onVoteRemoved', that);
	
	/**
	 * @event onParticipantRemoved
	 */
    this.onParticipantRemoved = new YAHOO.util.CustomEvent('onParticipantRemoved', that);
};
