/* Copyright 2009 Palm, Inc.  All rights reserved. */


Photos.Common.RowRequester = Class.create({

	defaultLoadTimeoutDelay: 0.01,
	defaultRequestChunk: 1,
	defaultWindowSize: -1,
	defaultViewableSize: -1,

	windowSizeAll: -1,
	viewableSizeAll: -1,

	/*
	 * viewableSize
	 * 		Load only this many after a resume but do not
	 * 		drop extras.
	 *
	 * windowSize
	 * 		Limit the possible requests to this many around
	 * 		the middle row, after sorting. Drop the extras.
	 *
	 * outstandingRequests
	 * 		Limit the number of active, outstanding, callbacks
	 * 		in progress before loading more of the windowSize.
	 *
	 * loadTimeout
	 * 		Time in seconds of how long to wait when not paused
	 * 		before trying to execute more requests.
	 */
	initialize: function(w, viewableSize, windowSize, outstandingRequests, loadTimeout) {
		this.window = w || window;
		this.viewableSize = parseInt(viewableSize ||
					this.defaultViewableSize, 10);
		this.windowSize = parseInt(windowSize ||
					this.defaultWindowSize, 10);
		this.maxOutstandingRequests = parseInt(outstandingRequests ||
					this.defaultRequestChunk, 10);
		this.loadTimeoutDelay = parseFloat(loadTimeout ||
					this.defaultLoadTimeoutDelay, 10);
		this.requests = [];
		this.requestsSinceResume = 0;
		this.outstandingRequests = 0;
		this.paused = true;
		this.middleRow = 0;
	},

	pause: function() {
		if (this.paused) {
			return true;
		}

		Mojo.Log.info("Pausing row requester.");

		this.paused = true;
		this._stopTimeout();
		return true;
	},

	resume: function(middleRow) {
		if (!this.paused) {
			return false;
		}

		this.middleRow = middleRow || this.middleRow;
		this.paused = false;
		Mojo.Log.info("Resuming requests at offset", this.middleRow);
		this.requests.sort(this._compareRow.bind(this));
		if (this.windowSize > 0) {
			var maxSize = Math.min(	this.windowSize,
						this.requests.length);
			Mojo.Log.info("Limiting window to", maxSize);
			for (var r = maxSize; r < this.requests.length; r++) {
				Mojo.Log.info("Dropping out of window", r);
				this.requests[r].drop();
			}
			this.requests.splice(maxSize);
		}
		this.requestsSinceResume = 0;
		this._loadMore();
		return true;
	},

	dropRequests: function() {
		for (var i = 0; i < this.requests.length; i++) {
			this.requests[i].drop();
		}
		this.requests = [];
	},

	load: function(row, execCallback, dropCallback) {
		this.requests[this.requests.length] = {
			row: row,
			exec: execCallback || this._requestComplete.bind(this),
			drop: dropCallback || this._nothing
		};

		if (this.loadTimeout === undefined) {
			this._loadMore();
		}
	},

	cleanup: function() {
		this.dropRequests();
		this._stopTimeout();
	},

	doWork: function(amount, doneCallback) {
		this.destinedWork = Math.min(amount, this.requests.length);
		Mojo.Log.info("Doing a bit of work",
			this.destinedWork, this.requests.length);

		if (this.destinedWork === 0) {
			doneCallback();
			return;
		}

		this.workDoneCallback = doneCallback;
		var doneOne = this._workDone.bind(this);

		for (var i = 0; i < amount; i++) {
			var request = this.requests.shift();
			if (!request) {
				break;
			}
			request.exec(doneOne);
		}
	},

	_workDone: function() {
		this.destinedWork--;
		if (this.destinedWork === 0) {
			this.workDoneCallback();
		}
	},

	_nothing: function() {
	},

	_compareRow: function(a, b) {
		return Math.abs(a.row - this.middleRow) -
			Math.abs(b.row - this.middleRow);
	},

	_loadMore: function(delay) {
		if (this.viewableSize > 0 &&
				this.requestsSinceResume >= this.viewableSize) {
			this._stopTimeout();
			return;
		}

		if (this.loadTimeout !== undefined) {
			return;
		}

		this.loadTimeout = this.window.setInterval(
				this._handleLoadTimeout.bind(this),
				(delay || this.loadTimeoutDelay) * 1000.0);
	},

	_stopTimeout: function() {
		if (this.loadTimeout === undefined) {
			return;
		}
		this.window.clearInterval(this.loadTimeout);
		delete this.loadTimeout;
	},

	_handleLoadTimeout: function() {
		if (this.paused) {
			this._stopTimeout();
			return;
		}

		while (this.outstandingRequests < this.maxOutstandingRequests) {
			var request = this.requests.shift();
			if (!request) {
				this._stopTimeout();
				return;
			}

			this.outstandingRequests++;
			this.requestsSinceResume++;
			request.exec(this._requestComplete.bind(this));
		}
	},

	_requestComplete: function() {
		this.outstandingRequests--;
		this._loadMore();
	}
});

