/**
 * Base class for uploads and transfers.
 *
 * @private
 */
Ext.define("Ext.space.files.Transfer", {
    /**
     * Object reference to the transfer management object for this item.
     * @private
     */
    manager: null,
 
    /**
     * Internal promise that tracks transfer completion
     * @type {Ext.space.Promise}
     * @private
     */
    done: null,
 
    /**
     * Internal event handlers
     * @type {Object}
     * @private
     */
    events: null,
 
    /**
     * Field name for the internal identifier for this transfer
     * @type {String}
     */
    idField: null,
 
    /**
     * File key for the saved file
     * @type {Number}
     */
    fileKey: null,
 
    /**
     * Source/Destination URL
     * @type {String}
     */
    url: null,
 
    /**
     * Progress so far
     * @type {Number}
     */
    bytesTransferred: 0,
 
    /**
     * Final size
     * @type {Number}
     */
    totalBytes: 0,
 
    /**
     * transfer status
     * @type {Boolean}
     */
    isComplete: false,
 
    /**
     * Transfer status, that lags behind isComplete (so we can check to see if the
     * Transfer just finished, or has been complete for a little while)
     * @type {Boolean}
     * @private
     */
    everCompleted: false,
 
    /**
     * When the transfer initiated
     * @type {Date}
     */
    dateStarted: null,
 
    /**
     * @private
     */
    constructor: function(args) {
        this.done = new Ext.space.Promise();
        this.events = {
            progress: new Ext.space.Observable()
        };
        this._updateWith(args);
    },
 
    /**
     * Create handlers for transfer completion and/or error.
     *
     * @param {Function} onsuccess (optional) Callback invoked when the transfer is
     *                             completed. Receives two parameters: the file on
     *                             disk, and the transfer object (the Upload or
     *                             Download) itself.
     * @param {Function} onerror (optional) Callback invoked when the transfer is
     *                           canceled or errors out for some reason.
     * @return {Ext.space.Promise} Promise that resolves after the transfer itself is
     *                       resolved and the onsuccess/onerror callback is fired
     *                       (as in regular promise chaining).
     */
    then: function(onsuccess, onerror) {
        return this.done.then(onsuccess, onerror);
    },
 
    /**
     * Check this transfer's progress.
     *
     * @return {Ext.space.Promise} Promise which will fulfill when progress is fetched and
     *                       updated into this object (and which is resolved with this
     *                       transfer as a parameter too).
     */
    getProgress: function() {
        return this.manager ? this.manager.getProgress(this) : null;
    },
 
    /**
     * Cancel this transfer.
     *
     * @return {Ext.space.Promise} Promise which will fulfills when the transfer is
     *                       successfully canceled. If the transfer is already done,
     *                       the promise will reject.
     */
    cancel: function() {
        return this.manager ? this.manager.cancel(this) : null;
    },
 
    /**
     * Bulk update this transfer with the data provided.
     *
     * @private
     * @param {Object} source Object with data to overwrite onto this transfer
     */
    _updateWith: function(source) {
        if (source) {
            if (source[this.idField]) { this[this.idField] = source[this.idField]; }
            if (source.fileKey) { this.fileKey = source.fileKey; }
            if (source.url) { this.url = source.url; }
            if (source[this.bytesTransferredField]) { this[this.bytesTransferredField] = source[this.bytesTransferredField]; }
            if (source.totalBytes) { this.totalBytes = source.totalBytes; }
            if (source.isComplete) { this.isComplete = true; }
            if (source.dateStarted) { this.dateStarted = new Date(source.dateStarted * 1000); }
 
            // fire a progress event if we have sufficient data and we're not done 
            if (this[this.idField] && !this.everCompleted) {
                this.events.progress.invokeListeners(this);
                this.everCompleted = this.isComplete;
            }
        }
        return this;
    },
 
    /**
     * Wire up event listeners for the transfer.
     *
     * There is currently only one event applications can listen for: 'progress',
     * which means something has been updated.
     *
     * @param {String} event Event name to listen for (start, progress)
     * @param {Function} callback Callback to invoke when the event fires
     * @return {Object} The download object, to facilitate method chaining
     */
    on: function(event, callback) {
        var events = this.events;
 
        // only register events we support; otherwise just silently swallow it 
        if (events[event]) {
            events[event].addListener(callback);
        }
 
        return this;
    }
});