/*
 * String cell processor for the table control.
 * The string processor allows to edit cell values with a simple
 * input control.
 */
+function ($) { "use strict";

    // NAMESPACE CHECK
    // ============================

    if ($.oc.table === undefined)
        throw new Error("The $.oc.table namespace is not defined. Make sure that the table.js script is loaded.");

    if ($.oc.table.processor === undefined)
        throw new Error("The $.oc.table.processor namespace is not defined. Make sure that the table.processor.base.js script is loaded.");

    // CLASS DEFINITION
    // ============================

    var Base = $.oc.table.processor.base,
        BaseProto = Base.prototype

    var StringProcessor = function(tableObj, columnName, columnConfiguration) {
        //
        // State properties
        //

        this.focusTimeoutHandler = this.onFocusTimeout.bind(this)

        //
        // Parent constructor
        //

        Base.call(this, tableObj, columnName, columnConfiguration)
    }

    StringProcessor.prototype = Object.create(BaseProto)
    StringProcessor.prototype.constructor = StringProcessor

    StringProcessor.prototype.dispose = function() {
        BaseProto.dispose.call(this)
        this.focusTimeoutHandler = null
    }

    /*
     * Renders the cell in the normal (no edit) mode
     */
    StringProcessor.prototype.renderCell = function(value, cellContentContainer) {
        this.createViewContainer(cellContentContainer, value)
    }

    /*
     * This method is called when the cell managed by the processor
     * is focused (clicked or navigated with the keyboard).
     */
    StringProcessor.prototype.onFocus = function(cellElement, isClick) {
        if (this.activeCell === cellElement)
            return

        this.activeCell = cellElement
        this.buildEditor(cellElement, this.getCellContentContainer(cellElement))
    }

    /*
     * Forces the processor to hide the editor when the user navigates
     * away from the cell. Processors can update the sell value in this method.
     * Processors must clear the reference to the active cell in this method.
     */
    StringProcessor.prototype.onUnfocus = function() {
        if (!this.activeCell)
            return

        var editor = this.activeCell.querySelector('.string-input')
        if (editor) {
            // Update the cell value and remove the editor
            this.tableObj.setCellValue(this.activeCell, editor.value)
            this.setViewContainerValue(this.activeCell, editor.value)
            editor.parentNode.removeChild(editor)
        }

        this.showViewContainer(this.activeCell)
        this.activeCell = null
    }
    
    StringProcessor.prototype.buildEditor = function(cellElement, cellContentContainer) {
        // Hide the view container
        this.hideViewContainer(this.activeCell)

        // Create the input control 
        var input = document.createElement('input')
        input.setAttribute('type', 'text')
        input.setAttribute('class', 'string-input')
        input.value = this.tableObj.getCellValue(cellElement)
        cellContentContainer.appendChild(input)

        this.setCaretPosition(input, 0)

        // Focus the element in the next frame. 
        // http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful
        window.setTimeout(this.focusTimeoutHandler, 0)
    }

    /*
     * Determines if the keyboard navigation in the specified direction is allowed
     * by the cell processor. Some processors could reject the navigation, for example
     * the string processor could cancel the left array navigation if the caret 
     * in the text input is not in the beginning of the text.
     */
    StringProcessor.prototype.keyNavigationAllowed = function(ev, direction) {
        if (direction != 'left' && direction != 'right')
            return true

        if (!this.activeCell)
            return true

        var editor = this.activeCell.querySelector('.string-input')
        if (!editor)
            return true

        var caretPosition = this.getCaretPosition(editor)

        if (direction == 'left')
            return caretPosition == 0

        if (direction == 'right')
            return caretPosition == editor.value.length

        return true
    }

    /*
     * This method is called when a cell value in the row changes.
     */
    StringProcessor.prototype.onRowValueChanged = function(columnName, cellElement) {
        if (columnName != this.columnName) {
            return
        }

        var value = this.tableObj.getCellValue(cellElement)

        this.setViewContainerValue(cellElement, value)
    }

    StringProcessor.prototype.onFocusTimeout = function() {
        if (!this.activeCell)
            return

        var editor = this.activeCell.querySelector('.string-input')
        if (!editor)
            return

        editor.focus()
        this.setCaretPosition(editor, 0)
    }

    StringProcessor.prototype.getCaretPosition = function(input) {
        // TODO: use the foundation library

        if (document.selection) { 
           var selection = document.selection.createRange()

           selection.moveStart('character', -input.value.length)
           return selection.text.length
        }

        if (input.selectionStart !== undefined)
           return input.selectionStart

        return 0
    }

    StringProcessor.prototype.setCaretPosition = function(input, position) {
        // TODO: use the foundation library

        if (document.selection) { 
            var range = input.createTextRange()

            setTimeout(function() {
                // Asynchronous layout update, better performance
                range.collapse(true)
                range.moveStart("character", position)
                range.moveEnd("character", 0)
                range.select()
            }, 0)
        }

        if (input.selectionStart !== undefined) {
            setTimeout(function() {
                // Asynchronous layout update
                input.selectionStart = position
                input.selectionEnd = position
            }, 0)
       }

        return 0
    }

    $.oc.table.processor.string = StringProcessor;
}(window.jQuery);