/*global Mojo, Class */

/*****************************************************************************
**
** Name:          mojo_widget_find.js
**
** Description:   Find / Search Widget
**
** Author:        Ernest Duckworth
**
** Date:          09/24/2008
**
** Notes:
**
**  Mojo.Widget.Find: Widget designed to handle user request searches / find
**
**  Declared in a scene's view as follows:
**
**     <div id="findField" name="findField" x-mojo-element="Find">
**     </div>
**
**  Attributes passed to the controller:
**
**      {
**        enableNxtPrv  : Boolean, optional - force next and previous active
**                                            before search results are in
**        findFieldName :  String, optional - find field name
**        handleClear   : Boolean, optional - true to get clear text events
**        handleEnter   : Boolean, optional - true to submit on enter key
**        handleFilter  : Boolean, optional - true to get delayed filter events
**        hideSpinner   : Boolean, optional - true to hide icon
**        showOnKey     : Boolean, optional - true to show widget on keyevent
**        widgetTop     :  String, optional - top position of widget
**      };
**
**  Model passed to the controller:
**
**      {
**        delay: String, optional - time delay on filter event sends only
**                                  works if handleFilter is true and
**                                  default delay is 300ms
**      };
**
**  Setting up the widget:
**
**      this.controller.setupWidget('findField', attributes, model);
**
**      or
**
**      this.controller.setupWidget('findField', null, null);
**
**  Events:
**
**      Since this widget has 'go' button text will not be sent after a
**      default delay as in the filter field widget.
**
**      Mojo.Event.findClear is sent when the 'text field' is empty
**
**      Mojo.Event.findGo is sent when the 'go' button is pressed
**
**      Mojo.Event.findPrev is sent when the 'previous/up' button is pressed
**
**      Mojo.Event.findNext is sent when the 'next/down' button is pressed
**
**  Exposed Methods:
**
**       blur          : blur the text area
**       focus         : focus the text area
**       showGo        : show the default go icon
**       hideFind      : hide the widget
**       showFind      : show the widget
**       showResult    : show the result icon with result number
**       stopSpinner   : stop the spinner icon
**       startSpinner  : start the spinner icon
**
**
** Copyright (C) 2008 by Palm, Inc.  The material contained herein is the
** proprietary property of Palm, Inc.  Disclosure of this material is
** strictly prohibited except where permission is granted in writing.
**
*****************************************************************************/

Mojo.Widget.Find = Class.create({

    /** @private */
    setup: function() {
        //Mojo.Log.info('Mojo.Widget.Find: setup');

        Mojo.assert(this.controller.element,
            'Mojo.Widget.Find requires an element');

        // Some variables we need
        this.resultCount = 0;
        this.buttonsActive = false;
        this.spinnerRunning = false;
        this.default_delay = 300;
        this.spinner_id = 'activity_spinner';
        this.result_icon_id = 'result_icon';
        this.result_text_id = 'result_text';

        this.divPrefix =
            this.controller.scene.sceneId + this.controller.element.id;

        // Get the attributes
        this.enableNxtPrv = this.controller.attributes.enableNxtPrv || false;

        this.findFieldName = this.controller.attributes.findFieldName ||
                             this.divPrefix+'_list_find_text';

        this.handleClear = this.controller.attributes.handleClear || false;
        this.handleEnter = this.controller.attributes.handleEnter || false;
        this.handleFilter = this.controller.attributes.handleFilter || false;
        this.hideSpinner = this.controller.attributes.hideSpinner || false;
        this.showOnKey = this.controller.attributes.showOnKey || false;
        this.widgetTop = this.controller.attributes.widgetTop || undefined;

        // Get the model to figure our our delay
        if (this.controller.model && this.controller.model.delay) {
            this.delay = this.controller.model.delay;
        }
        else {
            this.delay = this.default_delay;
        }

        // Render the area
        this.sceneDiv = this.controller.scene.sceneElement;

        this.renderWidget();

        // Just forward the event
        if (this.handleFilter) {
            Mojo.Event.send(this.controller.element, Mojo.Event.filter,
                    { findString: this.findField.value, cancel: false });
        }

        this.controller.get('find_go').addClassName('active');

        // Setup our listeners on the buttons
        Mojo.Event.listen(this.controller.get('find_go'), Mojo.Event.tap,
                this.handleGoButton.bindAsEventListener(this), false);
        
        Mojo.Event.listen(this.controller.get('find_prev'), Mojo.Event.tap,
                this.handlePrevButton.bindAsEventListener(this), false);

        Mojo.Event.listen(this.controller.get('find_next'), Mojo.Event.tap,
                this.handleNextButton.bindAsEventListener(this), false);

        // Expose methods
        this.controller.exposeMethods([
                                        'blur',
                                        'focus',
                                        'showGo',
                                        'hideFind',
                                        'showFind',
                                        'showResult',
                                        'stopSpinner',
                                        'startSpinner'
                                      ]);

        this.controller.scene.pushCommander(this);

        // Set up the spinner widget (override class with our find class)
        if (!this.hideSpinner) {
            // Setup Spinner Widget
            this.spinnerAttributes = {
                                       property: 'p',
                                       spinnerSize: 'small'
                                     };

            this.spinnerModel = { p: false };

            this.controller.scene.setupWidget(this.spinner_id,
                                              this.spinnerAttributes,
                                              this.spinnerModel);
        }

        this.controller.instantiateChildWidgets(this.controller.element);
    },

    /** @private */
    renderWidget: function() {
        //Mojo.Log.info('Mojo.Widget.Find: renderWidget');

        var model = {
                      'divPrefix': this.divPrefix,
                      'findFieldName': this.findFieldName
                    };

        // TODO Until we get approved for this widget just keep it local
        var content = Mojo.View.render({ object: model, template: '../../javascripts/find' });

        // Draw this area into the div
        this.controller.element.insert(content);

        this.find = this.controller.get(this.divPrefix+'_'+'list_find');

        // Figure out where to place this widget
        this.originalTop = this.find.getStyle('top');

        if (this.widgetTop && this.widgetTop !== this.originalTop) {
            this.originalTop = this.widgetTop;
        }

        this.findField = this.controller.get(this.findFieldName);
        // Start with this widget hidden
        this.hideFind();

        // Handle key events and focus
        this.controller.listen(this.controller.scene.sceneElement, Mojo.Event.keydown, this.handleKeyDown.bind(this) );
        this.controller.listen(this.controller.scene.sceneElement, Mojo.Event.keyup, this.handleKeyUp.bind(this) );
        this.findField.focus();

        this.findField.setAttribute(Mojo.Gesture.consumesEnterAttribute, true);
    },

    /** @private */
    cleanup: function() {
        //Mojo.Log.info('Mojo.Widget.Find: cleanup');

        if (this.spinnerRunning) {
            this.stopSpinner();
        }
    },

    /** @private */
    sendClear: function() {
        //Mojo.Log.info('Mojo.Widget.Find: sendClear');

        Mojo.Event.send(this.controller.element, 'mojo-find-clear');
        this.controller.window.clearTimeout(this.clear);
    },

    /** @private */
    handleModelChanged: function(model, what) {
        //Mojo.Log.info('Mojo.Widget.Find: handleModelChanged');

        this.findField.value = '';

        // Handle the change

        // Let me see what happens here
        if (this.findOpen && this.showOnKey) {
            this.handleFindClose();
        }
    },

    /** @private */
    handleSelection: function(event) {
        //Mojo.Log.info('Mojo.Widget.Find: handleSelection');

        // Just forward the event
        if (this.handleFilter) {
            Mojo.Event.send(this.controller.element, Mojo.Event.filter);
        }
    },

    /** @private */
    cancelFind: function() {
        //Mojo.Log.info('Mojo.Widget.Find: cancelFind');

        this.hideIcon();
        this.resultCount = 0;
        this.controller.get(this.result_icon_id).setStyle('display: none');

        // If the spinner is running then lets cancel the search
        if (this.spinnerRunning) {
            this.stopSpinner();

            Mojo.Event.send(this.controller.element, 'mojo-find-go',
                    { findString: this.findField.value, cancel: true });

            this.deactivateNxtPrv();
        }
    },

    /** @private */
    handleKeyUp: function(e) {

        var keyCode = e.originalEvent.keyCode;

        // Only handle keys if we are open
        if (this.findOpen) {

            // Eat enter keys to avoid newlines
			if ( Mojo.Char.isEnterKey(keyCode)) {
                e.stop();
                return true;
            }

            // Queue this up
            this.handleSendEvent();

            // If a search is running then cancel is and show the go button
            if (this.spinnerRunning) {
                this.cancelFind();
            }

            // Hide the field if empty and we previously had something in there
            if (this.findField.value.length === 0) {
                this.handleFindClose();
                this.hideIcon();
            }
            else
            {
                // make sure we don't hide the field if there is now text in it
                this.controller.window.clearTimeout(this.clear);
            }
        }
    },


    /** @private */
    handleKeyDown: function(e) {
        this.controller.window.clearTimeout(this.clear);

        var keyCode = e.originalEvent.keyCode;

        // Eat enter keys to avoid newlines
        if ( Mojo.Char.isEnterKey(keyCode)) 
		{
            e.stop();

            if (this.handleEnter) {
                this.handleGoButton();
            }

            return true;
        }

		if ( keyCode == Mojo.Char.metaKey ||
			keyCode == 129)	// Orange key
		{
			return;
		}
		if ( (keyCode != Mojo.Char.ctrl) && 
			 (keyCode != Mojo.Char.opt) && 
			 (keyCode <= Mojo.Char.deleteKey)) 
		{
			return;
		}

        // display the go icon 
        if (this.icon != 'palm-find-go') 
		{
            this.controller.get(this.result_icon_id).setStyle('display: none');
            this.goIcon();
        }

    },

    /** @private */
    handleDelayedSend:function() {
        //Mojo.Log.info('Mojo.Widget.Find: handleDelayedSend');

        if (this.handleFilter) {
            this.controller.window.clearTimeout(this.findTimer);
            this.findTimer = null;

            // Just forward the event
            Mojo.Event.send(this.controller.element, Mojo.Event.filter,
                    { findString: this.findField.value, cancel: false });

            // Activate the previous and next icons
            if (this.enableNxtPrv) {
                this.activateNxtPrv();
            }
        }
    },

    /** @private */
    handleSendEvent: function(now) {
        //Mojo.Log.info('Mojo.Widget.Find: handleSendEvent');

        if (this.handleFilter) {
            if (now) {
                this.controller.window.clearTimeout(this.findTimer);
                this.findTimer = null;

                // Just forward the event
                Mojo.Event.send(this.controller.element, Mojo.Event.filter,
                        { findString: this.findField.value, cancel: false });
            }
            else {
                if (!this.findTimer) {
                    // Set the timer
                    this.findTimer =
                        this.controller.window.setTimeout(this.handleDelayedSend.bind(this),
                                          this.delay);
                }
            }
        }
    },

    /** @private */
    handleFindClose: function() {
        //Mojo.Log.info('Mojo.Widget.Find: handleFindClose');

		this.findField.value = '';

        // Close widget if we show on key press and we are not handling clear
        if (this.showOnKey && !this.handleClear) {
            this.hideFind();
            this.findOpen = false;

            // Send the event
            this.handleSendEvent(true);
        }
        else {
            this.cancelFind();
        }

        // If we are to send clear events then set a timeout for x seconds
        if (this.handleClear) {
            this.clear = this.controller.window.setTimeout(this.sendClear.bind(this), 2000);
        }
    },

    /** @private */
    handleCommand: function(event) {
        //Mojo.Log.info('Mojo.Widget.Find: handleCommand');

        if (event.type == Mojo.Event.back) {
            if (this.findOpen) {
                this.handleFindClose();
                event.preventDefault();
                event.stopPropagation();
            }
        }
    },

    /** @private */
    handleGoButton: function() {
        //Mojo.Log.info('Mojo.Widget.Find: handleGoButton');

        var isCancel = false;

        // If we are already running then stop the running process
        if (this.spinnerRunning) {
            this.stopSpinner();
            this.goIcon();
            isCancel = true;
        }
        else {
            // Only show the spinner if we were told too
            if (!this.hideSpinner) {
                if (this.findField.value.length > 0) {
                    this.startSpinner();
                }
            }
        }

        //Mojo.Log.info('this.findField.value = '+this.findField.value);
        //Mojo.Log.info('isCancel = '+isCancel);

        Mojo.Event.send(this.controller.element, 'mojo-find-go',
                { findString: this.findField.value, cancel: isCancel });

        // Activate the previous and next icons
        if (this.enableNxtPrv) {
            this.activateNxtPrv();
        }
    },

    /** @private */
    handlePrevButton: function() {
        //Mojo.Log.info('Mojo.Widget.Find: handlePrevButton');

        if (this.buttonsActive) {
            Mojo.Event.send(this.controller.element, 'mojo-find-prev', null);
		}
    },

    /** @private */
    handleNextButton: function() {
        //Mojo.Log.info('Mojo.Widget.Find: handleNextButton');

        if (this.buttonsActive) {
            Mojo.Event.send(this.controller.element, 'mojo-find-next', null);
		}
    },

    /** @private */
    goIcon: function() {
        //Mojo.Log.info('Mojo.Widget.Find: goIcon');

        this.icon = 'palm-find-go';
        this.showIcon();
    },

    /** @private */
    spinnerIcon: function() {
        //Mojo.Log.info('Mojo.Widget.Find: spinnerIcon');

        this.icon = 'palm-find-spinner';
        this.showIcon();
    },

    /** @private */
    resultIcon: function() {
        //Mojo.Log.info('Mojo.Widget.Find: resultIcon');
        //Mojo.Log.info('\n\nthis.resultCount = '+this.resultCount+'\n\n');

        var newWidth = 0;

        if (this.spinnerRunning) {
            this.stopSpinner();
        }

        if (parseInt(this.resultCount, 10) < 2) {
            this.deactivateNxtPrv();
        }
        else {
            this.activateNxtPrv();
        }

        this.icon = 'palm-find-result-icon';

        // Clear the go icon
        this.controller.get('find_go').setStyle('display: none');

        // Wasted too much time trying to get the image to resize just force it
        newWidth = 12 + (this.resultCount.length * 7);

        this.controller.get(this.result_icon_id).setStyle('display: block');
        this.controller.get(this.result_icon_id).setStyle('width: '+newWidth+'px');

        this.controller.get(this.result_text_id).innerHTML = this.resultCount;
    },

    /** @private */
    showIcon: function() {
        this.controller.get('find_go').className = this.icon;
        this.controller.get('find_go').setStyle('display: block');
    },

    /** @private */
    hideIcon: function() {
        this.icon = 'palm-hide-icon';
        this.controller.get('find_go').setStyle('display: none');
        this.deactivateNxtPrv();
    },

    /** @private */
    activateNxtPrv: function() {
        //Mojo.Log.info('Mojo.Widget.Find: activateNxtPrv');

        this.buttonsActive = true;

        this.controller.get('find_prev').addClassName('active');
        this.controller.get('find_next').addClassName('active');
    },

    /** @private */
    deactivateNxtPrv: function() {
        //Mojo.Log.info('Mojo.Widget.Find: deactivateNxtPrv');

        this.buttonsActive = false;

        this.controller.get('find_prev').removeClassName('active');
        this.controller.get('find_next').removeClassName('active');
    },

    /********************************************************
     *                SEMI-PUBLIC FUNCTIONS                 *
     ********************************************************/

    /** @public */
    blur: function() {
        this.findField.blur();
    },

    /** @public */
    focus: function() {
        this.findField.focus();
    },

    /** @public */
    showGo: function() {
        //Mojo.Log.info('Mojo.Widget.Find: showGo');

        this.goIcon();
    },

    /** @public */
    hideFind: function() {
        //Mojo.Log.info('Mojo.Widget.Find: hideFind');

        this.findField.value = '';
        this.cancelFind();

        this.findOpen = false;
        this.find.setStyle('top: -10000px;');
    },

    /** @public */
    showFind: function(e) {
        //Mojo.Log.info('Mojo.Widget.Find: showFind');
		if (!this.findOpen) {
			this.findOpen = true;
			this.find.setStyle('top: '+ this.originalTop);
			this.findField.focus();
		}
    },

    /** @public */
    showResult: function(result) {
        //Mojo.Log.info('Mojo.Widget.Find: showResult - '+result);

        this.resultCount = result;
        this.resultIcon();
    },

    /** @public */
    stopSpinner: function() {
        //Mojo.Log.info('Mojo.Widget.Find: stopSpinner');

        this.spinnerRunning = false;
        this.spinnerModel.p = false;
        this.controller.modelChanged(this.spinnerModel);
    },

    /** @public */
    startSpinner: function() {
        //Mojo.Log.info('Mojo.Widget.Find: startSpinner');

        this.spinnerIcon();
        this.spinnerRunning = true;
        this.spinnerModel.p = true;
        this.controller.get(this.result_icon_id).setStyle('display: none');
        this.controller.modelChanged(this.spinnerModel);
    }
});
