(function($) {
    
    $.widget('ui.dataList', {
        
        options: {
            
            effectDirection: 'right', // 'left' or 'right'
            buttons: [], // [{title: "Title", callback: Function}]
            expandTo: function(dataList, listItem) {
                
                var listItemContents = listItem.find('div.list-item')
                var heightOffset = (listItemContents.innerHeight() - listItemContents.height());
                var marginTop = listItemContents.css('marginTop');
                var marginBottom = listItemContents.css('marginBottom');
                
                var marginOffset = parseInt(marginTop.substr(0, marginTop.length - 2)) + parseInt(marginBottom.substr(0, marginBottom.length - 2));
                
                return dataList.height() - heightOffset - marginOffset - 1;
                
            },
            formatInner: function(inner, itemValue) {
                
                inner.text(itemValue);
                
            }
            
        },
        
        _internalSettings: {
            
            selfClasses: 'cf-ui data-list ul-corner-all',
            liClasses: 'list-item ui-corner-all',
            innerStub: '<div></div>'
            
        },
        
        _create: function() {
            
            var self = this;
            
            if(self.element.is('ul')) {
                
                self.element.wrap('<div></div>');
                self.element.wrap('<div class="list-container"></div>')
                
                self.dataList = self.element.parent().parent();
                self.dataList.append('<div class="viewport-container"><div class="viewport"></div></div>');
                self.dataList.addClass(self._internalSettings.selfClasses);
                
                self.listContainer = self.dataList.children('div.list-container');
                self.viewportContainer = self.dataList.children('div.viewport-container');
                self.viewportContainer.hide();
                
                self.expandedItemIndex = -1;
                self.lastSelectedIndex = self.selectedIndex = -1;
                self.lastSelectedItem = self.selectedItem = null;
                
                self.lastScrollTop = 0;
                self.itemExpanded = false;
                
                self.ieQuirks = $.msie && parseInt($.browser.version.substr(0, 1)) < 8 && parseInt($.browser.version.substr(0, 1)) > 5;
                
                self._formatListItems(self.element.find('li'));
                
                for(var i = 0; i < self.options.buttons.length; i++) {
                    
                    self._addButtonToItems(self.options.buttons[i].title, self.options.buttons[i].callback, self.element.children('li'));
                    
                }
                
            } else {
                
                self.destroy();
                
            }
            
        },
        
        _formatListItems: function(listItems) {
            
            var self = this;
            
            listItems.each(
                function(index, element) {
                    
                    var listItem = $(element);
                    
                    if(listItem.children().length > 0) {
                        
                        listItem.children().wrapAll('<div class="value"></div>');
                        
                    } else {
                        
                        var listItemContent = listItem.text();
                        listItem.empty();
                        listItem.append('<div class="value">' + listItemContent + '</div>');
                        
                    }
                    
                    listItem.children('div.value').wrap('<div class="list-item ui-corner-all"></div>');
                    listItem.children('div.list-item').append('<a class="hit"></a>')
                    listItem.children('div.list-item').append('<div class="buttons"></div>');
                    listItem.children('div.list-item').append('<div class="inner"></div>');
                    
                    var clickTarget = listItem.children('div.list-item').children('a.hit');
                    var hoverTarget = self.ieQuirks ? listItem.children('div.list-item').children('a.hit') : listItem.children('div.list-item');
                    
                    clickTarget.bind(
                        'click',
                        function() {
                            
                            self._selectItem(listItem);
                            
                        }
                    );
                    
                    hoverTarget.bind(
                        'mouseover',
                        function() {
                            
                            listItem.children().first().addClass('active');
                            
                        }
                    );
                    
                    hoverTarget.bind(
                        'mouseout',
                        function() {
                            
                            listItem.children().first().removeClass('active');
                            
                        }
                    );
                }
            );
            
            
        },
        
        _selectItem: function(listItem) {
            
            var self = this;
            
            self.lastSelectedIndex = self.selectedItem ? self.selectedItem.index() : -1;
            self.lastSelectedItem = self.selectedItem;
            
            self.selectedIndex = listItem.index();
            self.selectedItem = listItem;
            
            self._invalidateExpanded();
            
        },
        
        _invalidateExpanded: function() {
            
            var self = this;
            
            if(self.itemExpanded && self.lastSelectedItem != null) {
                
                self._collapseItem();
                
            } else {
                
                self._expandItem(
                    self.selectedItem, 
                    function(contents) {
                        
                        if(self.options.formatInner) {
                            
                            self.options.formatInner(
                                
                                contents.find('div.inner').first(),
                                contents.find('div.value')
                                
                            )
                            
                        }
                        
                    }
                );
                
            }
            
        },
        
        _expandItem: function(listItem, callback) {
            
            var self = this;
            
            var expandToHeight = self.options.expandTo(self.dataList, listItem);
            var listItemContents = listItem.children().first().clone(true);
            var listItemPosition = listItem.position();            
            var listItemTop = listItemPosition.top - parseInt(self.listContainer.scrollTop()) - 4;
            var listItemViewport = self.viewportContainer.children('div.viewport');
            var clickTarget = listItemContents.find('a.hit').first();
            
            self.expandedItemIndex = listItem.index();
            self.lastScrollTop = self.listContainer.scrollTop();
            self.lastItemTop = listItemTop;
            
            self.viewportContainer.show();
            self.listContainer.fadeOut(150);
            
            listItemViewport.stop(true, true).empty();
            
            listItemViewport.css(
                {
                    left: '0px',
                    top: (listItemTop + 'px'),
                    width: '100%'
                }
            ).append(listItemContents);
            
            listItemViewport.animate(
                {
                    top: '0px'
                },
                400,
                function() {
                    
                    listItemContents.animate(
                        {
                            height: expandToHeight
                        },
                        400,
                        function() {
                            
                            if(callback) {
                                
                                listItemContents.find('div.inner').first().css(
                                    {
                                        // mmm i can has magic number? 24!
                                        height: (listItemContents.height() - 24) + "px"
                                    }
                                );
                                
                                callback(listItemContents);
                                
                            }
                            
                        }
                    );
                    
                }
            );
            
            self.itemExpanded = true;
            
        },
        
        _collapseItem: function(callback) {
            
            var self = this;
            
            var listItemViewport = self.viewportContainer.children('div.viewport').first();
            var listItemContent = listItemViewport.children().first();
            var listItemInner = listItemViewport.children().first().children('div.inner');
            
            
            listItemInner.fadeOut(150, function() {
                
                listItemInner.remove();
                    
                    listItemContent.animate(
                    {
                        height: '16px'
                    },
                    
                    400,
                    
                    function() {}
                    
                );
                
                listItemViewport.animate(
                    {
                        top: self.lastItemTop + 'px'
                    },
                    400,
                    function() {
                        
                        
                        self.listContainer.fadeIn(300, function() {
                            
                            listItemViewport.empty().css(
                                {
                                    
                                    width: '0px'
                                    
                                }
                            );
                            
                            self.viewportContainer.hide();
                            
                            if(callback != null && callback != undefined) {
                                
                                callback();
                                
                            }
                            
                        
                        });
                        
                        self.listContainer.scrollTop(self.lastScrollTop);
                        
                    }
                );
                
            })
            
            
            
            self.itemExpanded = false;
            
            
        },
        
        _destroyItem: function(listItem) {
            
            var self = this;
            
            var listItemContents = listItem.children().first().clone();
            var listItemPosition = listItem.position();            
            var listItemTop = listItemPosition.top - parseInt(self.listContainer.scrollTop()) - 4;
            var listItemViewport = self.viewportContainer.children('div.viewport');
            
            listItemViewport.stop(true, true).empty();
            
            listItemViewport.css(
                {
                    left: '0px',
                    top: (listItemTop + 'px'),
                    width: '100%'
                }
            ).append(listItemContents);
            
            self.viewportContainer.show();
            
            listItem.css(
                
                {
                    visibility: 'hidden'
                }
                
            );
            
            listItem.slideUp(250, function() {
                
                self.viewportContainer.hide();
                listItem.remove();
                
            });
            
            listItemViewport.animate(
                (
                    self.options.effectDirection == 'left' ?
                    {
                        left: '-150%'
                    } :
                    {
                        left: '150%'
                    }
                ),
                250
            );
            
        },
        
        _addButtonToItems: function(title, callback, items) {
            
            var self = this;
            
            items.each(function(index, element) {
                
                var listItem = $(element);
                var elementValue = listItem.find('div.value').first();
                
                var button = $('<a href="javascript:void(0);" class="list-item-button">' + title + '</a>');
                
                button.button();
                button.css({
                    
                    fontSize: '12px'
                    
                });
                
                button.bind(
                    'click',
                    function() {
                        
                        callback(listItem.index(), elementValue);
                        
                    }
                );
                
                listItem.find('div.buttons').eq(0).append(button);
                
            });
            
        },
        
        _addButtonToContents: function(index, contents) {
            
            var self = this;
            
            var buttonContainer = contents.find('div.buttons').first();
            
            buttonContainer.empty();
            
            for(var i = 0; i < self.options.buttons.length; i++) {
                
                var title = self.options.buttons[i].title;
                var callback = self.options.buttons[i].callback;
                
                var button = $('<a href="javascript:void(0);" class="list-item-button">' + title + '</a>');
            
                button.button();
                button.css({
                    
                    fontSize: '12px'
                    
                });
                
                button.bind(
                    'click',
                    function() {
                        
                        callback(index, contents.find('div.value').first());
                        
                    }
                );
                
                buttonContainer.append(button);
                
            }
            
        },
        
        getIndexOfValue: function(value) {
            
            var self = this;
            var result = false;
            
            var items = self.element.children('li');
            
            for(var i = 0; !result && i < items.length; i++) {
                
                result = items.eq(i).find('div.value').first().text() == value;
                
            }
            
            return result ? i - 1 : -1;
            
        },
        
        addItem: function(value) {
            
            var self = this;
            
            var listItem = $('<li>' + value + '</li>');
            
            self.element.append(listItem);
            self._formatListItems(listItem);
            
            for(var i = 0; i < self.options.buttons.length; i++) {
                
                self._addButtonToItems(self.options.buttons[i].title, self.options.buttons[i].callback, listItem);
                
            }
            
            var listItemContents = listItem.children().first().clone();
            var listItemPosition = listItem.position();            
            var listItemTop = listItemPosition.top - parseInt(self.listContainer.scrollTop()) - 4;
            var listItemViewport = self.viewportContainer.children('div.viewport');
            
            listItemViewport.stop(true, true).empty();
            
            listItemViewport.css(
                {
                    left: self.options.effectDirection == 'left' ? '-150%' : '150%',
                    top: (listItemTop + 'px'),
                    width: '100%'
                }
            ).append(listItemContents);
            
            self.viewportContainer.show();
            
            listItem.css(
                
                {
                    visibility: 'hidden'
                }
                
            );
            
            self.listContainer.animate(
                {
                    scrollTop: self.element.height() - self.listContainer.height()
                },
                200,
                function() {
                    
                    listItemViewport.animate(
                        (
                            {
                                left: '0px'
                            }
                        ),
                        250,
                        function() {
                            
                            self.viewportContainer.hide();
                            listItem.css(
                                {
                                    visibility: 'visible'
                                }
                            );
                        }
                    );
                }
            );
            
            self.listContainer.scrollTop();
            
        },
        
        addUniqueItem: function(value) {
            
            var self = this;
            
            if(self.getIndexOfValue(value) == -1) {
                
                self.addItem(value);
                
            }
            
        },
        
        removeItem: function(value) {
            
            var self = this;
            
            var valueIndex = self.getIndexOfValue(value);
            
            if(valueIndex != -1) {
                
                self.removeItemAt(valueIndex);
                
            }
            
            
        },
        
        removeItemAt: function(index) {
            
            
            var self = this;
            var listItem = self.element.children('li').eq(index);
            
            if(self.itemExpanded) {
                
                self._collapseItem(function() {
                    
                    self._destroyItem(listItem);
                    
                });
                
            } else {
                
                self._destroyItem(listItem);
                
            }
            
            
        },
        
        destroy: function() {
            
            
            
        }
    
    });
    
})(jQuery);