/**
 * @author sundar
 */
ZOOMIN.namespace("ZOOMIN.util");

(function() {
    var util = ZOOMIN.util.Util; //shorthand
    var DEFAULT_OPTIONS = {
        pageSize : 30,
        total : 0,
        elPhotoListContainer : null,
        getRequestCallback : null,
        updateHtmlCallback : null,
		getDataCallback : null,
		idField : "ImageId",
		arrField : "photos", 
        cache : true
    };
    var DEFAULT_AJAX_OPTIONS = {
        type : "GET",
        cache : false,
        async : true,
        timeout : 10000
    };
    /**
    * @param {Object} options
    */
    ZOOMIN.util.PhotoList = function(options) {
        options = util.initializeOptions(options, DEFAULT_OPTIONS);
        //assign all values
        for (var i in DEFAULT_OPTIONS) {
            if (util.hasOwnProperty(DEFAULT_OPTIONS, i)) {
                this[i] = options[i];
            }
        }
		this.dataStore = new ZOOMIN.util.Datastore(options);
		//check for callbacks
        if (!$.isFunction(this.getRequestCallback) &&
		!$.isFunction(this.getDataCallback)) {
            throw new Error("Error: need atleast getRequestCallback or getDataCallback");
        }
        if (!$.isFunction(this.updateHtmlCallback)) {
            throw new Error("Error: no update html callback passed");
        }
        if (typeof options.initData === "object") {
            if ($.isArray(options.initData) && $.isArray(options.page)) {
				if (options.initData.length === options.page.length) {
					for (var i = 0; i < options.initData.length; i++) {
						this.dataStore.addPageData(options.initData[i], options.page[i]);
					}
				} else {
					throw new Error("array mismatch");
				}
            } else {
				if ($.isArray(options.initData[this.arrField])) {
					this.dataStore.addPageData(options.initData[this.arrField], options.page);
				} else {
					this.dataStore.addPageData(options.initData, options.page);
				}
			}
        }
    };

    var PL = ZOOMIN.util.PhotoList; //shorthand
    /**
    * this event gets triggered before a page change happens
    * @usage 
    * $(oPhotoList).bind(ZOOMIN.util.PhotoList.EVENT_BEFORE_CHANGE, function () {
    *  	//do something
    *  });
    */
    PL.EVENT_BEFORE_CHANGE = "beforeChange";

    /**
    * this event gets triggered before a page change happens
    */
    PL.EVENT_AFTER_CHANGE = "afterChange";
    /**
    * this gets triggered when error occurs while getting data
    */
    PL.EVENT_ERROR = "error";
    /**
    * 
    */
    ZOOMIN.util.PhotoList.prototype = {
        /**
        * current page
        */
        page : -1,
        /**
        * size of page
        */
        pageSize : 0,
        /**
        * total number of photos
        */
        total : 0,
		/**
		 * @type {Object}
		 */
		dataStore : null,
		/**
        * before making an ajax request this function will be called for url
        * @usage
        * @param {Object} { 
        * page : page new page to be switched
        * pageSize : size of the page
        * total : total number of photos
        * }
        * @return {Object} properties for ajax
        */
        getRequestCallback : null,
        /**
        * this will get the data. this is a synchronous callback.
        * @param {Object} { 
        * page : page new page to be switched
        * pageSize : size of the page
        * total : total number of photos
        * }
        * @return {Object} data
        */
        getDataCallback : null,
        /**
        * this callback will get the html from the object
        * @param {Object} data 
        * @return {String} this html will be appended to the photoElement
        */
        updateHtmlCallback : null,
		/**
		 * field name of the id field to be searched on
		 */
		idField : null,
		arrField : null,
		/**
        * 
        */
        cache : false,
        loading : false,
        loadingPage : -1,
        currentRequest : null,
		reloadPage : function() {
			//fire event before change
            $(this).trigger(PL.EVENT_BEFORE_CHANGE, {
                oldPage: this.page,
                newPage: this.page,
                pageSize: this.pageSize
            });
            //get the data
            this.getData(this.page);
		},
        /**
        * changes the page
        * @param {Number} page
        * @return {Boolean} whether it was successful
        */
        changePage : function(page) {
            var res = false;
            //check whether it is a vaild page

            if (typeof page === "number" && page > -1 && page != this.page && page != this.loadingPage) {
                //check the current page
                if (this.loading) {
                    //cancel the request if in progress
                    this.currentRequest.abort();
                    this.release();
                }

                //fire event before change
                $(this).trigger(PL.EVENT_BEFORE_CHANGE, {
                    oldPage: this.page,
                    newPage: page,
                    pageSize: this.pageSize
                });
                //get the data
                this.getData(page);
            }
            return res;
        },
		reset : function () {
			this.page = -1;
			this.clearCache();
			if (this.loading) {
                //cancel the request if in progress
                this.currentRequest.abort();
                this.release();
            }
		},
        /**
        * 
        * @param {Object} data
        */
        onData : function(data) {
            //add it to cache if cacheable
            if (this.cache) {
                this.dataStore.addPageData(data, this.page);
            }
            //update html
            if ($.isFunction(this.updateHtmlCallback)) {
                this.updateHtmlCallback(data);
            }
        },
        getData : function(page) {
            var res = true;
            var ob, data, options;

            this.loading = true;
            this.loadingPage = page;
            //check in cache for the page
            if (this.dataStore.isPageLoaded(page)) {
                data = this.dataStore.getPageData(page);
            } else {
                options = {
                    page: page,
                    pageSize: this.pageSize,
                    total: this.total
                };
                //get the url from callback
                if ($.isFunction(this.getRequestCallback)) {
                    ob = this.getRequestCallback(options);
                    //make ajax call
                    this.currentRequest = this.makeAjaxCall(ob);
                } else {
                    if ($.isFunction(this.getDataCallback)) {
                        data = this.getDataCallback(options);
                    }
                }
            }
            if (typeof data === "object") {
                this.onSuccess(data);
            }
            return res;
        },
		onError : function(req, status, err) {
            //do something
            var page = this.loadingPage;
            this.release();
            $(this).trigger(PL.EVENT_ERROR, {
                page: page,
                pageSize: this.pageSize,
                status: status,
                error: err
            });
        },
        release : function() {
            this.loading = false;
            this.loadingPage = -1;
            this.currentRequest = null;
        },
        onSuccess : function(data) {
            this.page = this.loadingPage;
            this.release();
			
			if ($.isArray(data[this.arrField])) {
				//do something
            	data = data[this.arrField];
			}
            this.onData(data);
            $(this).trigger(PL.EVENT_AFTER_CHANGE, {
                page: this.page,
                pageSize: this.pageSize,
                data: data
            });
        },
        clearCache : function(page) {
            this.dataStore.clear(page);
        },
        makeAjaxCall : function(ob) {
            ob = util.initializeOptions(ob, DEFAULT_AJAX_OPTIONS);
            //assign handlers
            ob.success = util.bind(this.onSuccess, this);
            ob.error = util.bind(this.onError, this);

            return $.ajax(ob);
        },
        /**
        * changes the size of each page. it also tries to switch to page
        * which has the first element on the current page
        * @param {Object} pageSize
        */
        changePageSize : function(pageSize) {
            //compute the position of new page
            var newPage = this.calculatePage(pageSize);
            //set values
            this.pageSize = pageSize;
            //clear current data
            this.dataStore.changePageSize(this.pageSize)

            this.changePage(newPage);
			
            return newPage;
        },
        calculatePage : function(pageSize) {
            var firstPosition = this.page * this.pageSize;
            var newPage = Math.floor(firstPosition / pageSize);
            //TODO: add some better logic
            return newPage;
        },
		/**
		 * updates the data with values
		 * @param {Object} id id value to be searched for
		 * @param {object} ob name value pairs to be updated
		 */
		updateData : function (id, ob) {
			return this.dataStore.updateData(id, ob);
		},
		getItem : function (id) {
			return this.dataStore.getItem(id);
		},
		getNextItem : function (id) {
			return this.dataStore.getNextItem(id);
		},
		getPrevItem : function (id) {
			return this.dataStore.getPrevItem(id);
		},
		deleteItem : function (id) {
			var pos, item;
			//get position
			pos = this.dataStore.getPosition(id);
			if (typeof pos.page !== "undefined") {
				item = this.dataStore.deleteItem(id);
				//check if item in the current page
				if (this.page === pos.page) {
					//update the html
					this.reloadPage();
				}
			}
			if (typeof item !== "undefined") {
				this.total = this.dataStore.total;
			}
			
			return item;
		},
		getPage : function (id) {
			return this.dataStore.getPage(id)
		}
    };

})();