﻿// ----------- devices panel helper class ------------ //
Ext.ux.ClientDevicesPanelHelper = Ext.extend(Ext.util.Observable, {

    // internal fields
    m_aButtonImageIds: [],
    m_iDropStateDeviceId: g_default_xPOPropertyId,      // id of device button in drop state (ir -1 for none)
    m_iHoverDeviceId: g_default_xPOPropertyId,       // id of device button is in hover state (or -1 for none)
    m_iOnStateDeviceId: g_default_xPOPropertyId,    // id of device button is in on state (or -1 for none)
    m_deviceInfoDataPollTimerId: null,
    m_buttonData: null,
    m_bPendingDeletion: false, // if this flag is true, ignore any ajax requests coming back - specifically onRetrieveDeviceInfoData


    // delegates
    m_defaultTabClickDelegate: null,
    m_setDescriptionDelegate: null,

    // delegate setters
    setDefaultTabClickDelegate: function(theDelegate) {
        this.m_defaultTabClickDelegate = theDelegate;
    },

    setSetDescriptionDelegate: function(theDelegate) {
        this.m_setDescriptionDelegate = theDelegate;
    },


    // callbacks
    m_autoTooltipCallback: null,

    // callback setters
    setAutoTooltipCallback: function(theCallback) {
        this.m_autoTooltipCallback = theCallback;
    },


    // standard constructor
    constructor: function(defaultDeviceId) {
        this.m_deviceId = defaultDeviceId;

        Ext.ux.ClientDevicesPanelHelper.superclass.constructor.call(this);
    },

    // standard initializer
    initComponent: function() {
        Ext.ux.ClientDevicesPanelHelper.superclass.initComponent.apply(this, arguments);

        // extra args have to go here as base class initComponent doesn't get called for some reason
        var config = {};

        Ext.apply(this, config);
        Ext.apply(this.initialConfig, config);
    },


    // initialize the devices panel menu with the specified array of tabs
    initialize: function(buttons) {
        this.m_buttonData = buttons;

        for (var i = 0; i < buttons.length; i++) {
            if (!buttons[i].isDisabled) {
                $("#" + buttons[i].linkId + "").bind("click", buttons[i], delegate(this, this.handleButtonClick));
                $.preloadImages(buttons[i].activeImagePath, buttons[i].hoverImagePath, buttons[i].dropTargetImagePath, buttons[i].inactiveImagePath, buttons[i].onImagePath);
                $.merge(this.m_aButtonImageIds, [[buttons[i].imageId, buttons[i].inactiveImagePath]]);

                // set correct drag drop group depending on whether we're configuring the cloud or a device
                var dragDropGroups = [];

                if (buttons[i].intData == g_cloud_deviceId) { // device id
                    dragDropGroups = new Array(
                    g_dragDropGroup_trackUpload,
                    g_dragDropGroup_playlistUpload,
                    g_dragDropGroup_playlistEntryUpload);
                }
                else {
                    dragDropGroups = new Array(
                        g_dragDropGroup_trackDownload,
                        g_dragDropGroup_playlistDownload,
                        g_dragDropGroup_playlistEntryDownload);
                }

                // set up drop targets
                initializeDragDrop(
                    dragDropGroups,
                    buttons[i].imageId,
                    buttons[i].activeImagePath,
                    buttons[i].dropTargetImagePath,
                    buttons[i].inactiveImagePath,
                    buttons[i].hoverImagePath,
                    buttons[i].intData, // device id
                    delegate(this, this.checkIsActive),
                    delegate(this, this.checkAllowDropOnActive),
                    delegate(this, this.checkIsHover),
                    delegate(this, this.checkIsOnState),
                    delegate(this, this.setIsDropState),
                    delegate(this, this.handleDrop));

                // bind hover images
                $.bindHoverImages(
                    buttons[i].imageId,
                    buttons[i].hoverImagePath,
                    buttons[i].inactiveImagePath,
                    buttons[i].intData,
                    delegate(this, this.checkIsActive),
                    null, null,
                    delegate(this, this.checkIsDropState),
                    delegate(this, this.checkIsOnState),
                    delegate(this, this.setIsHover));
            }
        }

        // start polling for new devices / clientdevicesfiles        
        // have to call with 'this' or our checkXXX handlers are undefined in subsequent functions
        this.startRetrieveDeviceInfoDataPolling();
    },

    // return true if the specified deviceId is the currently active device
    checkIsActive: function(deviceId) {
        return deviceId == this.m_deviceId;
    },

    // return true if we're allowed to drop the specified type of thing on the specified device when active
    checkAllowDropOnActive: function(type) {

        var allowDrop = false;

        if ((type == g_extJsGrid_dataTypePlaylist) || (type == g_extJsGrid_dataTypePlaylistEntry)) {
            allowDrop = true;
        }

        return allowDrop;
    },

    // store the device id of the device being hovered over
    // also store the type of the thing doing the hovering
    setIsHover: function(deviceId, type) {
        this.m_iHoverDeviceId = deviceId;
        this.m_hoverObjectType = type;
    },

    // return true if a device image is currently being hovered over
    checkIsHover: function(deviceId) {
        return (this.m_iHoverDeviceId == deviceId);
    },

    // set a flag indicating whether a button in this set is in the drop state
    setIsDropState: function(deviceId) {
        this.m_iDropStateDeviceId = deviceId;
    },

    // return true if a device image is currently being dropped over
    checkIsDropState: function(deviceId) {
        return (this.m_iDropStateDeviceId == deviceId);
    },

    // set a flag indicating whether a button in this set is in the on state
    setIsOnState: function(deviceId) {
        this.m_iOnStateDeviceId = deviceId;
    },

    // return true if a device image is currently being dropped over
    checkIsOnState: function(deviceId) {
        return (this.m_iOnStateDeviceId == deviceId);
    },

    // get current device id
    getDeviceId: function() {
        return this.m_deviceId;
    },

    // change images and call target function
    handleButtonClick: function(event) {

        this.selectDeviceInt(
            event.data.intData,
            event.data.imageId,
            event.data.activeImagePath,
            event.data.altText);

        event.preventDefault();
    },

    // select a device programmatically
    selectDevice: function(deviceId) {

        // loop through devices to find the right one
        for (var iDevice = 0; iDevice < this.m_buttonData.length; iDevice++) {
            if (this.m_buttonData[iDevice].intData == deviceId) {

                this.selectDeviceInt(
                    deviceId,
                    this.m_buttonData[iDevice].imageId,
                    this.m_buttonData[iDevice].activeImagePath,
                    this.m_buttonData[iDevice].altText);

                break;
            }
        }
    },

    // internals of device selection
    selectDeviceInt: function(deviceId, imageId, activeImagePath, altText) {

        $("#" + imageId + "").attr("src", activeImagePath);     // change image clicked on
        $.showInactiveImages(imageId, this.m_aButtonImageIds);  // change other images to inactive

        this.m_deviceId = deviceId;                             // store deviceId for tab clicking

        // stop flashing (if indeed it is flashing)
        this.flashDeviceIconStop(this.m_deviceId);

        // call external delegate
        if (this.m_defaultTabClickDelegate) {
            this.m_defaultTabClickDelegate();
        }

        // call external delegate
        if (this.m_setDescriptionDelegate) {
            this.m_setDescriptionDelegate(altText);
        }
    },

    // handler function called when a button image is dropped on
    handleDrop: function(dropppedOnId, data) {
        var sourceDeviceId = data.selections[0].data.deviceid;

        if (dropppedOnId == g_cloud_deviceId) {
            this.handleDroppedOnCloud(data);
        }
        else {
            this.handleDroppedOnDevice(data, dropppedOnId);
        }
    },

    // items dropped on cloud so upload from device to cloud
    handleDroppedOnCloud: function(data) {
        var type    = data.selections[0].data.type;
        var idArray = [];

        // create idarray of correct type
        if ((type === g_extJsGrid_dataTypeClientDeviceFile)) {
            idArray = fileHashHelper_createFileHashIdArray(data.selections);
            type = g_extJsGrid_dataTypeFileHash;
        }
        else {
            idArray = idHelper_createIdArray(data.selections);
        }

        ajax_setQueueStatus(this.m_deviceId, g_cloud_deviceId, type, idArray, true, g_lowUserPriorityDefault, true);
    },

    // items dropped on device so download from cloud to device
    handleDroppedOnDevice: function(data, dropppedOnId) {
        var type    = data.selections[0].data.type;
        var idArray = [];
        
        // create idarray of correct type
        if ((type === g_extJsGrid_dataTypeClientDeviceFile) || (type === g_extJsGrid_dataTypeClientFile)) {
            idArray = trackHelper_createTrackIdArray(data.selections);
            type = g_extJsGrid_dataTypeTrack;
        }
        else {
            idArray = idHelper_createIdArray(data.selections);
        }

        ajax_setQueueStatus(g_cloud_deviceId, dropppedOnId, type, idArray, false, g_lowUserPriorityDefault, true);
    },

    // start the polling for new devices / updated device info
    startRetrieveDeviceInfoDataPolling: function() {

        // clear any old and start new timer
        if (this.m_deviceInfoDataPollTimerId) {
            clearTimeout(this.m_deviceInfoDataPollTimerId);
        }
        m_deviceInfoDataPollTimerId = setTimeout(delegate(this, this.retrieveDeviceInfoData), g_retrieveDeviceInfoDataPollingTime_milliseconds);
    },

    // stop the polling for new devices / updated device info
    stopRetrieveDeviceInfoDataPolling: function() {
        clearTimeout(this.m_deviceInfoDataPollTimerId);
    },

    // retrieve up-to-date device info
    retrieveDeviceInfoData: function() {

        // post ajax request to server
        $.ajax({
            url: g_deviceInfoDataAsyncUrl, // note: global url
            type: g_method_get,
            dataType: g_dataType_json,
            contentType: g_contentType_json,
            success: delegate(this, this.onRetrieveDeviceInfoDataSuccess),
            error: delegate(this, this.onRetrieveDeviceInfoDataError)
        });
    },

    // device info request has completed successfully
    onRetrieveDeviceInfoDataSuccess: function(response, textStatus) {

        // check that that the object hasn't been flagged for deletion while the ajax request was taking place
        // if it is, ignore the result of the request
        if (!this.m_bPendingDeletion) {
            if (response.status == 200) {
                // success
                var json = JSON.parse(response.responseText);
                if (json) {
                    this.processDeviceInfoData(json);
                }
                else {
                    // parsing failed; log an error and restart timer
                    ajax_logError(response);
                    this.startRetrieveDeviceInfoDataPolling();
                }
            }
            else if (response.status == 304) {
                // Not Modified - restart timer
                this.startRetrieveDeviceInfoDataPolling();
            }
            else {
                // failed for some other reason - restart timer
                this.startRetrieveDeviceInfoDataPolling();
            }
        }
    },

    // device info request failed
    onRetrieveDeviceInfoDataError: function(request, textStatus, error) {

        // check that that the object hasn't been flagged for deletion while the ajax request was taking place
        // if it is, ignore the result of the request
        if (!this.m_bPendingDeletion) {
            // restart timer
            this.startRetrieveDeviceInfoDataPolling();
        }
    },

    // process device info data
    processDeviceInfoData: function(data) {

        if (data.reload === true) {
            // new device configuration (devices added or removed) - reload panel
            // (timer is restarted when panel reloads)
            clientDevicesPanelHelper_delete();
            ajax_loadDevicesPanelContent();
        }
        else {
            // no need to reload panel so restart timer
            this.startRetrieveDeviceInfoDataPolling();

            // flash device icons where new ClientDeviceFiles have been found
            if (data.clientdeviceviews) {
                for (var iDevice = 0; iDevice < data.clientdeviceviews.length; iDevice++) {
                    if (data.clientdeviceviews[iDevice].newclientdevicefiles === true) {
                        this.flashDeviceIconStart(data.clientdeviceviews[iDevice].id);
                    }
                }
            }
        }
    },

    // flash the icon of the device with the specified id
    flashDeviceIconStart: function(deviceId) {

        // loop through devices to find the right one
        for (var iDevice = 0; iDevice < this.m_buttonData.length; iDevice++) {
            if (this.m_buttonData[iDevice].intData == deviceId) {
                // found the device; start flashing the image                
                $.flashImageStart(
                    this.m_buttonData[iDevice].imageId,
                    this.m_buttonData[iDevice].hoverImagePath,
                    this.m_buttonData[iDevice].dropTargetImagePath,
                    this.m_buttonData[iDevice].inactiveImagePath,
                    this.m_buttonData[iDevice].onImagePath,
                    deviceId,
                    this.checkIsActive,
                    this.checkIsDropState,
                    this.checkIsHover,
                    this.setIsOnState);

                // call the auto tiiltip delegate to show the auto tooltip
                this.m_autoTooltipCallback(this.m_buttonData[iDevice].linkId);

                break;
            }
        }
    },

    // stop flashing the icon of the device with the specified id
    flashDeviceIconStop: function(deviceId) {

        // loop through devices to find the right one
        for (var iDevice = 0; iDevice < this.m_buttonData.length; iDevice++) {
            if (this.m_buttonData[iDevice].intData == deviceId) {
                // found the device; start flashing the image
                flashImageStop(this.m_buttonData[iDevice].imageId);
                break;
            }
        }
    },

    // inform the object of impending death
    setPendingDeletion: function() {
        this.m_bPendingDeletion = true;

        // stop any timers
        this.stopRetrieveDeviceInfoDataPolling();
    }
});

// inform the clientDevicesPanelHelper it's about to be deleted (so it can stop timers & ignore any returning ajax requests)
// also delete it for good measure (not 100% convinced this works).
function clientDevicesPanelHelper_delete() {
    clientDevicesPanelHelper.setPendingDeletion();
    delete clientDevicesPanelHelper;
    clientDevicesPanelHelper = null;
}

// asynchronously load the devices panel body to the content returned by the ajax request
// (the ViewUserControl rendered by the MusicController)
function ajax_loadDevicesPanelContent() {

    $.ajax({
        url: g_devicesPanelUserControlAsyncUrl, // note: global url
        type: g_method_get,
        dataType: g_dataType_html,
        success: function(data) {
            $("#music_left").html(data);
        }
        // don't worry about error handling here as is non-fatal
    });
}