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

/**
 * @name download-controller.js
 * @fileOverview Handles the control for all file downloads.
 */

DownloadController = Class.create({
	
	initialize: function(controller) {
		
		this._controller = controller;
		this._panelModels = $A();
		this._downloads = $A();
	},
	
	setup: function() {

		this._controller.setupWidget('download-panel-scroller', {mode: 'vertical'});
		this._controller.setupWidget('download-panel-list', {
			itemTemplate: 'download/download-model',
			listTemplate: 'download/download-container',
			swipeToDelete: true,
			autoconfirmDelete: true,
			onItemRendered: this._onItemRendered.bind(this),
			itemsCallback: this._itemsCallback.bind(this)
		});
		
		// For easy access to the elements we store handles.
		this._downloadPanel = this._controller.get('download-panel-container');
		this._downloadPanel.hide();
		
		this._downloadPanelScroller = this._controller.get('download-panel-scroller');
		this._downloadPanelList = this._controller.get('download-panel-list');		
		
		// We are only interested in icon taps and list entry deletions.
		this._listRequestDeleteHandler = this._listRequestDelete.bind(this);
		this._progressTapHandler = this._progressTap.bind(this);
		
		Mojo.Event.listen(this._downloadPanelList, Mojo.Event.listDelete, this._listRequestDeleteHandler); 
		Mojo.Event.listen(this._downloadPanelList, Mojo.Event.listTap, this._progressTapHandler);

		this._live = true;
  	},
  
	cleanup: function() {
	
		Mojo.Event.stopListening(this._downloadPanelList, Mojo.Event.listDelete, this._listRequestDeleteHandler);
		Mojo.Event.stopListening(this._downloadPanelList, Mojo.Event.listTap, this._progressTapHandler);
		
		this._live = false;
		this._cancelAll();
	},
	
	/**
 	 * Start the download of a resource.
 	 * 
 	 * @param {String} url The resource to download
 	 */
	download: function(url, callback) {
		
		try {

			// Store the new model and requests
			var model = new DownloadModel(url);
			model.request = new DownloadRequest(url);
			this._downloads.push(model);
			
			// Update the visible download models.
			var offset = this._panelModels.length;
			this._panelModels.push(model);
			this._updatePanelVisibility();
			this._downloadPanelList.mojo.noticeAddedItems(offset, [model]);
			
			// Attempt to download from the given URL
			model.request.download({
				onDownload: function(success, status, response) {
					
					if (success) {
						
						switch (status) {
						case 'start':
							model.start(response.ticket, response.target);
							break;
							
						case 'progress':
							model.progress(response.amountReceived, response.amountTotal);
							break;
							
						case 'complete':
							model.complete(response.mimetype, response.target);
							break;
							
						case 'abort':
							model.abort(response.mimetype, response.target);
							break;
							
						case 'interrupted':
							model.error();
							break;

						default:
							model.error();
							break;							
						}
					} else {		
						model.error();
					}			

					// Only perform model updates IF the model hasn't been deleted && 
					// the controller is still live. We can still get 'no watcher' warnings
					// because we appear to have a small window where the list entry has
					// been swiped deleted before the model deleted flag is set.
					if (!model.deleted && this._live) {

						this._controller.modelChanged(model);
					}
					
					// Call the user callback
					callback(success, status);
				}.bind(this)
			});
		}
		catch (e) {
			Mojo.Log.logException(e, '#downloadResource');
		}
	},
	
	count: function() {
		
		return this._downloads.length;
	},
	
	_cancelAll: function() {
		
		this._downloads.each(function(download) {

			download.request.cancel({
				onCancel: function(success, response){
					if (success) {
						// Don't do anything..Download manager will send an abort.
					} else {
						// Do nothing?
					}
				}.bind(this)
			});			
		});
	},
	
	_itemsCallback: function(listWidget, offset, limit) {
		
		this._downloadPanelList.mojo.noticeUpdatedItems(0, this._panelModels);
	},
	
	_onItemRendered: function(list, item, node) {
		// On initial render make sure we always display the item
		// at the bottom of the list (the most recently added).
		this._downloadPanelScroller.mojo.revealBottom();
	},
	
	/**
	 * The user just swipe deleted the item from the list. If the download is currently 
	 * in progress then it is now canceled!
	 * 
	 * @param {String} event
	 */
	_listRequestDelete: function(event) {

		var model = event.item;		
		if (model) {
			
			model.request.cancel({});			

			var idx = this._downloads.indexOf(model);
			if (idx !== -1) {
				this._downloads.splice(idx, 1);
			}

			idx = this._panelModels.indexOf(model);
			if (idx !== -1) {
				this._panelModels.splice(idx, 1);
			}
		}
		
		this._updatePanelVisibility();		
	},
	
	_progressTap: function(event) {
		try {
			
			this._modelTap(event.item);
			
		} catch(e) {

			Mojo.Log.logException(e, '#_progressTap');
		}	
	},
	
	_modelTap: function(model) {
		
		var status = model.status;			
		var request = model.request;
		
		if (status === 'complete') {
			// Launch the download
			request.open({
				file: model.file,
				mimetype: model.mimetype,
				onOpen: function(success, response) {
					
					if (success) {
						// Remove the request and model
						var idx = this._downloads.indexOf(model);
						if (idx !== -1) {
							this._downloads.splice(idx, 1);
						}
						
						idx = this._panelModels.indexOf(model);
						if (idx !== -1) {
							this._panelModels.splice(idx, 1);
							this._downloadPanelList.mojo.noticeRemovedItems(idx, 1);
						}
						this._updatePanelVisibility();
					} else {
						this._showAlertDialog(response.errorText);
					}
				}.bind(this)
			});
		}
	},
	
	_updatePanelVisibility: function() {
		
		if (this._panelModels.length > 0) {
			// If we have a model to display show the scroller
			this._downloadPanel.show();			
		} else {
			// If we have no models to display then we hide the scroller.
			this._downloadPanel.hide();
		}
	},
	
	/**
	 * Display an alert dialog.
 	 */
	_showAlertDialog: function(message) {
		this._controller.showAlertDialog({
			onChoose: function(value) {},
			title: $L("Unable to Open File"),
			message:$L("This file format is not supported."),
			choices:[ {label:$L('OK'), value:'OK', type:'dismiss'} ]
		});
	}
});
