/**
 * @author marco francke
 *
 * With this helper class you can move entries between one list A and an other list B.
 *
 * a sample call looks like this:
 *
 *      Phx.Application.ListManager.init({
 *          leftList : '#listA', // required
 *          rightList : '#listB', // required
 *          moveLeftButton : '#ButtonToMoveEntryToLeftList',
 *          moveRightButton : '#ButtonToMoveEntryToRightList',
 *          deactivated : 'ClassToMarkEntryAsDeactivated',
 *          highlighting : 'ClassToMarkEntryAsSelected',
 *          enabledButtonClass : 'ClassWhichMarkButtonArrowsAsAktiveToUse',
 *          selectAllCheckbox : '#CheckboxToSelectAllEntriesOfLeftList',
 *          idPrefixLeftEntries : 'PrefixForEntriesInLeftList',
 *          idPrefixRightEntries : 'PrefixForEntriesInRightList',
 *          search : {
 *              searchField : '#SearchFieldId',
 *              searchContent : '#ClassOrIdOfElementWithContentYouWantToBrowse',
 *              matchHightlighting: 'ClassWhichHighlightsTheMatchedChars'
 *          }
 *      });
 *
 * to get all ids of entries in right list use this method:
 *
 *      Phx.Application.ListManager.getResult();
 */
Phx.namespace('Phx.Application.ListManager', function()
{
    /**
     * object of left list
     *
     * @var {Object} _leftList
     */
    var _leftList = null;

    /**
     * object of right list
     *
     * @var {Object} _rightList
     */
    var _rightList = null;

    /**
     * object of arrow-button to move entry from right list to leftt list
     *
     * @var {Object} _moveLeft
     */
    var _moveLeft = null;

    /**
     * object of arrow-button to move entry from left list to right list
     *
     * @var {Object} _moveRight
     */
    var _moveRight = null;

    /**
     * object of a checkbox to select all entries of the left list
     *
     * @var {Object} _selectAllCheckbox
     */
    var _selectAllCheckbox = null;

    /**
     * class name to mark an entry as deactivated
     *
     * @var {String} _deactivatedClass
     */
    var _deactivatedClass = null;

    /**
     * class name to mark an entry as selected
     *
     * @var {String} _highlighting
     */
    var _highlighting = null;

    /**
     * to decide entries between the left and right list
     *
     * @var {String} _idPrefixLeftEntries
     */
    var _idPrefixLeftEntries = 'left_';

    /**
     * to decide entries between the left and right list
     *
     * @var {String} _idPrefixRightEntries
     */
    var _idPrefixRightEntries = 'right_';

    /**
     * aktivates the arrows to use them for moving
     *
     * @var {String} _enabledButtonClass
     */
    var _enabledButtonClass = null;

    /**
     * object of search field
     *
     * @var {Object} _searchField
     */
    var _searchField = null;

    /**
     * class name of element which contains the browsable content
     *
     * @var {String} _searchContent
     */
    var _searchContent = null;

    /**
     * class name to hightlighting matched chars of search
     *
     * @var {String} _searchHighlighting
     */
    var _searchHighlighting = null;

    /**
     * makes all bindings
     */
    var _initBindings = function()
    {
        _leftList.unbind('click.listManager').bind('click.listManager', _toggleClickedEntry);
        _rightList.unbind('click.listManager').bind('click.listManager', _toggleClickedEntry);

        _leftList.unbind('dblclick.listManager').bind('dblclick.listManager', _moveEntry);
        _rightList.unbind('dblclick.listManager').bind('dblclick.listManager', _moveEntry);

        _leftList.unbind('mousedown.listManager').bind('mousedown.listManager', function(event) {Phx.Event.stop(event)});
        _rightList.unbind('mousedown.listManager').bind('mousedown.listManager', function(event) {Phx.Event.stop(event)});

        if (_moveLeft) {
            _moveLeft.unbind('click.listManager').bind('click.listManager', function() {_moveMarkedEntries(_rightList);});
        }

        if (_moveRight) {
            _moveRight.unbind('click.listManager').bind('click.listManager', function() {_moveMarkedEntries(_leftList);});
        }

        if (_selectAllCheckbox) {
            _selectAllCheckbox.unbind('click.listManager').bind('click.listManager', _selectUnselectAll);
        }
    };

    /**
     * init search
     *
     * @param {Object} settings - all settings for search
     */
    var _initSearch = function(settings)
    {
        if (!settings.searchField) {
            return;
        }

        if (!settings.searchContent) {
            return;
        }

        var _field = $(settings.searchField);

        if (_field.length === 0) {
            return;
        }

        _searchField = _field;
        _searchContent = settings.searchContent;
        _searchHighlighting = (settings.matchHightlighting) ? settings.matchHightlighting : '';

        _searchField.unbind('keyup.listManager').bind('keyup.listManager', _showMatches);
        _searchField.unbind('focus.listManager').bind('focus.listManager', _clearSearchField);
    };

    /**
     * clears search field from preset and init text
     */
    var _clearSearchField = function()
    {
        var _this = $(this);

        if (_this.attr('class').indexOf('preset') !== -1) {
            _this.removeClass('preset');
            _this.val('');
        }
    };

    /**
     * highlighting matched chars and hide the other entries
     */
    var _showMatches = function()
    {
        var _value = this.value;

        var _textObjs = $(_searchContent, _leftList);

        if (_textObjs.length === 0) {
            return;
        }

        for (var i = 0, len = _textObjs.length; i < len; i++) {
            var _element = $(_textObjs[i]);
            var _li = _element.parent('li');

            // if entry is hidden because its already moved to other list, than continue
            if (_li.css('display') == 'none' && _li.attr('class').indexOf('noMatchFound') === -1) {
                continue;
            }

            var _elementValue = _element.text();

            var _highlightedString = _highlightMatchedChars(_value, _elementValue);

            // hide entries with no matches
            if (false === _highlightedString) {
                _li.addClass('noMatchFound');
                _li.hide();
                continue;
            }

            // show highlighting
            _li.removeClass('noMatchFound');
            _element.html(_highlightedString);
            _li.show();
        }
    };

    /**
     * build highlighting for matched chars
     *
     * @param {String} searchString - chars to search
     * @param {String} matchString  - string where search chars should be in
     */
    var _highlightMatchedChars = function(searchString, matchString)
    {
        var searchStringLength = searchString.length;

        var position = matchString.toLowerCase().indexOf(searchString, 0);

        if (position === -1) {
            return false;
        }

        var matchedValue = matchString.substring(0, position) + '<span class="' + _searchHighlighting + '">' + matchString.substring(position, position + searchStringLength) + '</span>' + matchString.substring(position + searchStringLength);

        return matchedValue;
    };

    /**
     * removes highlighting
     *
     * @param {String} id - id of list-element
     */
    var _removeHighlighting = function(id)
    {
        var _element = $('#' + id);

        if (_element.length === 0) {
            return;
        }

        var _obj = $('.' + _searchHighlighting, _element);

        if (_obj.length === 0) {
            return;
        }

        var _parent = _obj.parent();

        if (_parent.length === 0) {
            return;
        }

        _parent.text(_parent.text());
    };

    /**
     * toggles the class (which developer specifies in variable "_highlighting")
     *
     * @param {Object} event - onClick event
     */
    var _toggleClickedEntry = function(event)
    {
        var _li = $(event.target).closest('li');

        if (_li.length === 0) {
            return;
        }

        // element is deactivated
        if (_deactivatedClass && _li.attr('class').indexOf(_deactivatedClass) !== -1) {
            return;
        }

        _li.toggleClass(_highlighting);
    };

    /**
     * moves the entry (the double clicked entry) to other list
     *
     * @param {Object} event - onClick event
     */
    var _moveEntry = function(event)
    {
        var _li = $(event.target).closest('li');

        if (_li.length === 0) {
            return;
        }

        // element is deactivated
        if (_deactivatedClass && _li.attr('class').indexOf(_deactivatedClass) !== -1) {
            return;
        }

        var _id = _li[0].id;

        if (!_id) {
            return;
        }

        _li.hide();
        _li.removeClass(_highlighting);

        if (this.id == _leftList[0].id) {
            _move(_li, _rightList, _idPrefixLeftEntries, _idPrefixRightEntries);
            return;
        }

        _move(_li, _leftList, _idPrefixRightEntries, _idPrefixLeftEntries);
    };

    /**
     * controlles the move-arrow-buttons to deaktivate/aktivate them if list is empty/not empty
     */
    var _checkLists = function()
    {
        // no buttons -> return;
        if (!_moveLeft || !_moveRight) {
            return;
        }

        var _lisLeft = $('li:not(.empty):visible', _leftList);

        if (_lisLeft.length === 0) {
            _moveRight.removeClass(_enabledButtonClass);
        } else {
            _moveRight.addClass(_enabledButtonClass);
        }

        var _lisRight = $('li:not(.empty):visible', _rightList);

        if (_lisRight.length === 0) {
            _moveLeft.removeClass(_enabledButtonClass);
        } else {
            _moveLeft.addClass(_enabledButtonClass);
        }
    };

    /**
     * moves all selected entries to the other list
     *
     * @param {Object} list - the scope of marked entries
     */
    var _moveMarkedEntries = function(list)
    {
        var _selectedEntries = $('.' + _highlighting, list);
        var _len = _selectedEntries.length;

        if (_len === 0) {
            return;
        }

        _selectedEntries.hide();
        _selectedEntries.removeClass(_highlighting);

        // move to right list
        if (list[0].id == _leftList[0].id) {
            for (var i = 0; i < _len; i++) {
                var _li = $(_selectedEntries[i]);
                _move(_li, _rightList, _idPrefixLeftEntries, _idPrefixRightEntries);
            }

            return;
        }

        for (var j = 0; j < _len; j++) {
            var _li = $(_selectedEntries[j]);
            _move(_li, _leftList, _idPrefixRightEntries, _idPrefixLeftEntries);
        }
    };

    /**
     * shows and hides the moved entries
     *
     * @param {Object} li - the li element to move
     * @param {Object} list - the list where the li element should be displayed
     * @param {String} prefixFrom - prefix of element
     * @param {String} prefixTo - the new prefix for the moved element
     */
    var _move = function(li, list, prefixFrom, prefixTo)
    {
        var _id = li[0].id;

        if (_id.indexOf(prefixFrom) !== -1) {
            _id = _id.substring(prefixFrom.length, _id.length);
        }

        // first try: exist element with prefix and id
        var _element = $('#' + prefixTo + _id);

        if (_element.length > 0) {
            _element.show();
            if (_searchHighlighting) {
                _removeHighlighting(prefixTo + _id);
                // hack -> to update highlighting after moving
                _searchField.keyup();
            }

            _checkLists();

            return;
        }

        // second try: exist element only with id
        _element = $('#' + _id, list);

        if (_element.length > 0) {
            _element.attr('id',  prefixTo + _id);
            _element.show();
        } else {
            list.append('<li id="' + prefixTo + _id + '">' + li.html() + '</li>');
        }

        if (_searchHighlighting) {
            _removeHighlighting(prefixTo + _id);
            // hack -> to update highlighting after moving
            _searchField.keyup();
        }

        // check if something has to change with the arrow-buttons
        _checkLists();
    };

    /**
     * toggles the class (which developer specifies in variable "_highlighting") for all entries of left list
     */
    var _selectUnselectAll = function()
    {
        if (this.checked == true) {
            $('li', _leftList).addClass(_highlighting);
            return;
        }

        $('li', _leftList).removeClass(_highlighting);
    };

    return {
        /**
         * makes init stuff
         *
         * @param {Object} settings - all possible settings to handle lists
         */
        init : function(settings) {
            if (typeof settings != 'object') {
                return;
            }

            if (!settings.leftList || !settings.rightList) {
                return;
            }

            _leftList = $(settings.leftList);
            _rightList = $(settings.rightList);

            if (_leftList.length === 0 || _rightList.length === 0) {
                return;
            }

            if (settings.idPrefixLeftEntries) {
                _idPrefixLeftEntries = settings.idPrefixLeftEntries;
            }

            if (settings.idPrefixRightEntries) {
                _idPrefixRightEntries = settings.idPrefixRightEntries;
            }

            var _button = null;
            if (settings.moveLeftButton) {
                _button = $(settings.moveLeftButton);

                if (_button.length > 0) {
                    _moveLeft = _button;
                }
            }

            if (settings.moveRightButton) {
                _button = $(settings.moveRightButton);

                if (_button.length > 0) {
                    _moveRight = _button;
                }
            }

            _enabledButtonClass = (settings.enabledButtonClass) ? settings.enabledButtonClass : '';

            if (settings.selectAllCheckbox) {
                var _checkbox = $(settings.selectAllCheckbox);

                if (_checkbox.length > 0 && _checkbox.attr('type') == 'checkbox') {
                    _selectAllCheckbox = _checkbox;
                }
            }

            if (settings.search && typeof settings.search == 'object') {
                _initSearch(settings.search);
            }

            _deactivatedClass = (settings.deactivated) ? settings.deactivated : null;
            _highlighting     = (settings.highlighting) ? settings.highlighting : null;

            _initBindings();
            _checkLists();
        },

        /**
         * gets all ids of entries from right list
         *
         * @return {Array} _ids - array of ids
         */
        getResult : function()
        {
            var _lis = $('li', _rightList);

            if (_lis.length === 0) {
                return [];
            }

            var _ids = [];

            for (var i = 0, len = _lis.length; i < len; i++) {
                var _id = _lis[i].id;

                if (_lis[i].style.display == 'none') {
                    continue;
                }

                if (_id.indexOf(_idPrefixRightEntries) !== -1) {
                    _id = _id.substring(_idPrefixRightEntries.length, _id.length);
                }

                _ids.push(_id);
            }

            return _ids;
        }
    };
}());