/**
 * Nowtilus scrollable menu with 2 levels for categories and subcategories.
 * Uses the Scrollable plugin from jQuery Tools
 * @author Dan Titiu
 * TODO: remove scrolling buttons and resize when only one page
 * TODO: fix Webkit submenu border
 * TODO: fix IE6 nextPage / prevPage buttons hover
 */
(function ($) {

    var PREFIX_CAT = '#ncat_',
        PREFIX_SUBCAT = '#nsubcat_',
        PREFIX_SUBMENU = '#subMenu_';

    $.fn.extend({
        /**
         * Visibility helper for horizontal forward scrolling
         * - checks visibility relative to the offsetParent boundaries and self dimensions
         * @return boolean
         */
        needsScrolling: function () {
            if (this[0]) {
                var $parent = this.parent('.menuItems'),
                    $offParent = $parent.offsetParent();
                if ($offParent.css('overflow') != 'hidden') {
                    return false;
                }
                var selfPos = this.position(),/* note: position is relative to the .menuItems */
                    selfDim = {width: this.outerWidth(), height: this.outerHeight()},
                    parentPos = $parent.position(),/* this one moves around */
                    offParentDim = {width: $offParent.innerWidth(), height: $offParent.innerHeight()};
                /* correct item position relative to offset parent */
                selfPos.left += parentPos.left;
                /* check boundaries */
                return !(selfPos.left >= 0
                        && (selfPos.left + selfDim.width) <= offParentDim.width);
            }
            return false;
        },
        /**
         * Visibility helper for horizontal backward scrolling
         * - checks visibility relative to the offsetParent boundaries and self dimensions
         * @return boolean
         */
        needsUnScrolling: function () {
            if (this[0]) {
                var $parent = this.parent('.menuItems'),
                    $offParent = $parent.offsetParent();
                if ($offParent.css('overflow') != 'hidden') {
                    return false;
                }
                var selfPos = this.position(),/* note: position is relative to the .menuItems */
                    selfDim = {width: this.outerWidth(), height: this.outerHeight()},
                    parentPos = $parent.position(),/* this one moves around */
                    offParentDim = {width: $offParent.innerWidth(), height: $offParent.innerHeight()};
                /* correct item position relative to offset parent */
                selfPos.left += parentPos.left + offParentDim.width;
                /* check boundaries */
                return (selfPos.left >= 0
                        && (selfPos.left + selfDim.width) <= offParentDim.width);
            }
            return false;
        }
    });

    var scrollableTweak = {
        /**
         * Find the first item that is not visible (needs scrolling) from current position forward
         * @return Object with following fields:
         *  - item: jQuery scrollable menu item
         *  - index: int, index of the menu item in scrollable
         */
        findNextSeekTarget: function () {
            for (var x = this.getIndex() + 1; x < this.getSize(); x++) {
                var $item = this.getItems().eq(x);
                if ($item.needsScrolling()) {
                    return {item: $item, index: x};
                }
            }
            return {item: $item, index: (x-1)};// last item as default
        },
        /**
         * Find the last item that that could fit into the previous page from current position backward
         * @return Object with following fields:
         *  - item: jQuery scrollable menu item
         *  - index: int, index of the menu item in scrollable
         */
        findPrevSeekTarget: function () {
            // first item as default
            var result = {item: this.getItems().eq(0), index: 0};
            for (var x = this.getIndex() - 1; x >= 0; x--) {
                var $item = this.getItems().eq(x);
                if (!$item.needsUnScrolling()) {
                    break;
                }
                result = {item: $item, index: x};
            }
            return result;
        },
        /**
         * Find the maximum index to which scrollable can go left
         * @return integer
         */
        findMaxIndex: function () {
            var $last = this.getItems().filter(':last'),
                w = $last.position().left + $last.outerWidth(),
                pw = this.getRoot().innerWidth();
            for (var x = this.getSize() - 2; x >= 0; x--) {
                if (w - this.getItems().eq(x).position().left > pw)
                    return x+1;                
            }
            return this.getSize() - this.getConf().size;
        },
        /**
         * Override default seekTo behaviour in order to scroll to the next invisible element
         * @param e Event
         * @param x integer
         * @return boolean
         */
        tweakSeekTo: function (/*Event*/ e, /*integer*/ x) {

            var target = {item: null, index: x};

            if (!this.getRoot().data('skipPagination')) {
                /* find the element to seekTo (pagination) */
                if ( x > this.getIndex() ) {
                    target = this.findNextSeekTarget();
                    if ( target.index == (this.getIndex() + this.getConf().size) ) {
                        //return true;// no correction needed
                    }
                } else {
                    target = this.findPrevSeekTarget();
                    if ( target.index == (this.getIndex() - this.getConf().size) ) {
                        //return true;// no correction needed
                    }
                }
            }
            
            /* when target is beyond max, reset */
            var maxIndex;
            if (!(maxIndex = this.getRoot().data('maxForwardIndex'))) {
                maxIndex = this.findMaxIndex();
                this.getConf().size = this.getSize() - maxIndex;
                this.getRoot().data('maxForwardIndex', maxIndex);
            }
            if (target.index == maxIndex) {
                return true;// shortcut optimization
            } else
            if (target.index > maxIndex) {
                target.index = maxIndex;
            }

            /* seekTo the first element that needs scrolling */
            this.getRoot().data('seekLock', true);
            this.seekTo(target.index);
            return false;
        },
        fixOnePageOnly: function () {
            /* if all items visible, resize and hide navigation */
            var $last = this.getItems().filter(':last');
            if ( !$last.needsScrolling() ) {
                this.getConf().size = this.getSize();
                this.getNaviButtons().hide();
                this.getRoot().siblings('.right').show();
                var w = $last.position().left + $last.outerWidth();
                this.getRoot().css('width' , '' + w + 'px');
            }
        }
    };

    /* initialize */
    $(document).ready(function () {
        /* categories scrollable */
        var $mm = $('#navigation_bottom .scrollable').scrollable({
            api: true,
            items: '.menuItems',
            item: '.menuItem',
            speed: 200,
            size: 4,
            clickable: false,
            onBeforeSeek: function (/*Event*/ e, /*Index*/ x) {
                if (this.getRoot().data('seekLock')) {
                    return true;
                }
                /* hide submenus */
                $('#subnavigation .subMenu').hide();
                this.getItemWrap().children('.selected').removeClass('selected');
                this.getItemWrap().children('.sepFake').removeClass('sepFake').addClass('separator');

                return this.tweakSeekTo(e, x);
            },
            onSeek: function (/*Event*/ e, /*Index*/ x) {
                this.getRoot().removeData('seekLock');
                this.getRoot().removeData('skipPagination');
                /* show submenus if menu selected */
                this.showSubMenu();
            }
        });

        /* extend Scrollable */
        $.extend($mm, scrollableTweak);
        $.extend($mm, {
            /**
             * Show them to me !
             */
            showSubMenu: function () {
                var catId = this.getRoot().data('selectedCatId');
                if (catId) {
                    var $mItem = this.getItems().filter(PREFIX_CAT + catId);
                    if (!$mItem.length || $mItem.needsScrolling()) {
                        return;// nothing to select
                    }
                    
                    var $subMenu = $(PREFIX_SUBMENU + catId);
                    if (!$subMenu.length) {
                        $mItem.addClass('selNoSub');
                        return;// no submenu to show
                    }

                    /* style the selected menu item */
                    $mItem.addClass('selected');
                    $mItem.prev('.separator').toggleClass('separator').toggleClass('sepFake');
                    $mItem.next('.separator').toggleClass('separator').toggleClass('sepFake');

                    var ncatOff = $mItem.offset(),
                        $subMenuParent = $subMenu.parent(),
                        smParentOff = $subMenuParent.offset(),
                        subMenuPosLeft = ncatOff.left - smParentOff.left,
                        subMenuPosTop = ncatOff.top + $mItem.outerHeight() - smParentOff.top,
                        proposedLeft = $subMenuParent.innerWidth() - $subMenu.outerWidth();

                    /* prevent submenu from reaching the outer limits */
                    if ( subMenuPosLeft > proposedLeft ) {
                        subMenuPosLeft = proposedLeft;
                    }
                    /* position submenu */
                    $subMenu.css('top', (subMenuPosTop - 1) + 'px');// -1 the top border overlay
                    $subMenu.css('left', (subMenuPosLeft) + 'px');

                    /* make scrollable */
                    var $sm = $subMenu.find('.scrollable').scrollable({
                        api: true,
                        items: '.menuItems',
                        item: '.menuItem',
                        speed: 200,
                        size: 4,
                        onBeforeSeek: function (/*Event*/ e, /*Index*/ x) {
                            if (this.getRoot().data('seekLock')) {
                                return true;
                            }
                            return this.tweakSeekTo(e, x);
                        },
                        onSeek: function (/*Event*/ e, /*Index*/ x) {
                            this.getRoot().removeData('seekLock');
                            this.getRoot().removeData('skipPagination');
                        }
                    });
                    /* tweak it */
                    $.extend($sm, scrollableTweak);
                    /* show them the result */
                    $subMenu.show();
                    /* select submenu item */
                    var subCatId = $subMenu.data('selectedSubCatId');
                    if (subCatId) {
                        var $smItem = $sm.getItems().filter(PREFIX_SUBCAT + subCatId);
                        if ($smItem.length > 0) {
                            $smItem.addClass('selected');
                            if ($smItem.needsScrolling()) {
                                var ix = $sm.getItems().index( $smItem.get(0) );
                                $sm.getRoot().data('skipPagination', true);
                                $sm.seekTo(ix);
                            }
                        }
                    }
                    /* fix no-scrolling */
                    $sm.fixOnePageOnly();
                }
            }
        });
        /* fix no-scrolling */
        $mm.fixOnePageOnly();
        
        var CAT_ID = (typeof(currentCategoryID)!="undefined")?currentCategoryID:null,
            SUBCAT_ID = (typeof(currentSubCategoryID)!="undefined")?currentSubCategoryID:null;

        if (CAT_ID) {
            /* find elements */
            var $ncat = $mm.getItems().filter(PREFIX_CAT + CAT_ID);
            if (!$ncat.length) {
                return;// nothing to select
            }
            /* store selected  */
            $mm.getRoot().data('selectedCatId', CAT_ID);
            if (SUBCAT_ID) {
                /* find submenu */
                var $subMenu = $(PREFIX_SUBMENU + CAT_ID);
                if ($subMenu.length > 0) {
                    $subMenu.data('selectedSubCatId', SUBCAT_ID);
                }
            }
            /* check if scrolling is needed */
            if ($ncat.needsScrolling()) {
                var ix = $mm.getItems().index( $ncat.get(0) );
                $mm.getRoot().data('skipPagination', true);
                $mm.seekTo(ix);
                return;// show the submenu only after scrolling the main menu
            }
            /* handle window resize event (reposition absolute elements) */
            $(window).resize(function (/*Event*/ e) {
                $mm.showSubMenu();
            });
            /* ready to go */
            $mm.showSubMenu();
        }
    });
})(jQuery);