dojo.provide('tb.Menu')
dojo.require('dijit._Widget')
dojo.require('dijit._Templated')

dojo.declare('tb.MenuBase', null, {
    baseClass : 'tbMenu',
    menus : null,
    struc  :null,
    _started : false,

    constructor : function(struc, params){
        this.struc = dojo.byId(struc)
        this.menus = {} 

        dojo.mixin(this, params)

        dojo.style(struc, 'display', 'none')
        this.domNode = dojo.create('div', {'class': this.baseClass, position:'relative', zIndex:1})
        if(this.id){
            this.domNode.id = this.id
        }
    },

    addMenu : function(Menu){
        Menu.base = this
        this.menus[Menu.id]=Menu
        Menu.hide(true)
        dojo.style(Menu.domNode,'position','absolute')
        //console.log('adding', Menu.domNode.parentNode)
        if(Menu.domNode.parentNode!==dojo.doc){
            Menu.placeAt(this.domNode, this.struc.id==Menu.id?'first':null)
        }
        return Menu 
    },

    getMenuById : function(id){
        return this.menus[id] || null
    },

    startup : function(){
        if(this._started){return} this._started=true;

        var s, id
        for(id in this.menus){

            s = dojo.byId(id)

            if(s===this.struc){
                this.menus[id].isRoot=true
                dojo.style(this.menus[id].domNode,'position','')
                dojo.addClass(this.menus[id].domNode,'tbRootMenu')
                continue
            }

            s = s.parentNode
            while(s!==this.struc && (!this.getMenuById(s.id) )){
                s = s.parentNode
            }
            
            this.menus[id].registerParent(this.getMenuById(s.id))
        }

        for(id in this.menus){
            this.menus[id].startup()
        }
    },

    placeAt :function(node, pos){
        dojo.place(this.domNode,node, pos)
    }
})

dojo.declare('tb.Menu', null, {
    baseClass : 'tbMenuNode',
    childIndicatorClass : 'childIndicator',
    srcNode:null,
    statics : {idcounter:0},
    borderCollapse : true,
    items: null,
    isRoot:false,
    childeren : null,
    parentMenu:null, //reference to the parentMenu
    parentItem:null, //reference to the item of the parent menu that is linked to this menu

    _offset : {x:0,y:0},
    _started : false,
    _keepOpen: false,

    constructor  :function(params, src){
        this.childeren = []
        this.items= []
        this.srcNode = dojo.byId(src)    

        //generate an id if the source node does not have one
        if(!this.srcNode.id){ this.srcNode.id = 'tbMenu_'+(this.statics.idcounter++) }
        this.id = this.srcNode.id

        //register all params
        dojo.mixin(this, params)
        this.offset = dojo.mixin({},this._offset, params.offset||{})

        this.initItems()
        this.buildRendering()

        if(this['class']) {
            dojo.addClass(this.domNode, this['class'])
        }
        this.postCreate()
        this.initEvents()

        if(this.style){ dojo.style(this.domNode, this.style) }
    },

    isVisible : function(){
        return dojo.style(this.domNode, 'visibility') !='hidden' && dojo.style(this.domNode, 'display') != 'none'
    },

    initItems: function(){
        this.items = this.parseDom(this.srcNode)
    },

    parseDom : function(domNode){
        var items= [] 
        dojo.query('>li>a', domNode).forEach(function(node){
            var params = {
                srcNode : node,
                href : node.href,
                content: node.innerHTML
            }
            items.push(params)
        }, this)
        return items 
    },

    onMenuEnter : function(node, e){
        this._hoverItem=null
        this.show()
    },

    onMenuLeave: function(node, e){
        this._hoverItem=null
        this._setHover()
        this.hide()
    },

    onItemEnter : function(item, indx, e){
        if(this._hoverItem===item){return}
        var prevItem = this._hoverItem
        this._hoverItem = item
        if(prevItem){ this.onItemLeave(prevItem, indx, e) }
        
        this._setHover(item)

        if(item.childMenu){
            item.childMenu.show()
        }
    },

    onItemLeave: function(item, indx, e){
        if(this._hoverItem && this._hoverItem===item){return;}

        dojo.removeClass(item.node,'hover')    
        if(item.childMenu){
            item.childMenu.hide(false, true)
        }
    },

    _setHover : function(item){
        dojo.forEach(this.items, function(item){dojo.removeClass(item.node,'hover')})
        item && dojo.addClass(item.node,'hover')    
    },

    setShowStyle : function(){
        this.domNode.style.visibility= 'visible'
    },

    resetShowStyle : function(){
        this.domNode.style.visibility= ''
    },

    resetPosition : function(){
        dojo.style(this.domNode, {top:'0px', left:'0px'})
    },

    setPosition  :function(offset){
        //use a mixin from tb.Menu.relPos 
    },

    keepOpen : function(open, doItNow){
        this._keepOpen = open;
        this.parentMenu && this.parentMenu.keepOpen(open)
        if(doItNow){
            this[open?'show':'hide']()
        }
    },

    show: function(){
        if(this.parentMenu){ this.parentMenu.show() }
        if(this.parentItem){ dojo.addClass(this.parentItem.node, 'active') }

        if(this._hto){ 
            clearTimeout(this._hto); this._hto=null
        }
        if(this.isVisible()){return}
        dojo.style(this.domNode, 'display', '')

        //only position absolute positioned elements
        if(dojo.style(this.domNode, 'position')=='absolute'){
            this.resetPosition()
            this.setPosition(this.offset)
            this.setShowStyle()
        }
    },

    _hide : function(){
        dojo.style(this.domNode, 'display', 'none')
        for(var i=0;i<this.items.length;i++){
            //hide all child menus and only buble down
            this.items[i].childMenu && this.items[i].childMenu.hide(true,true)
        }
        if(this.parentItem){ dojo.removeClass(this.parentItem.node, 'active') }
        this.resetShowStyle()
    },

    hide: function(force, noBubbeling){
        if(this.isRoot || this._keepOpen || !this.isVisible()){return}

        if(force){
            if(this._hto){ clearTimeout(this._hto); this._hto=null }
            this._hide()
            return;
        }
        if(this._hto){return;}
        !noBubbeling && this.parentMenu.hide()
        this._hto = setTimeout( dojo.hitch(this, this._hide),200)
    },

    buildRendering : function(){
        //this.domNode = this.containerNode = dojo.create('div', {id:'tbMenu'+(this.id.charAt(0).toUpperCase()+this.id.slice(1)),'class': this.baseClass})    
        this.domNode = this.containerNode = dojo.create('div', {'class': this.baseClass})    
        for(var i=0;i<this.items.length;i++){
            this.createItem(this.items[i], this.domNode)
        }
    },

    createChildMarker : function(node){
        dojo.addClass(node,'hasChild')
        this.childIndicator = dojo.create('div', {'class' : this.childIndicatorClass}, node,'first')
    },

    initEvents : function(){
        this.connect(this.domNode, 'onmouseenter', 'onMenuEnter', true)
        this.connect(this.domNode, 'onmouseleave', 'onMenuLeave', true)
        for(var i=0;i<this.items.length;i++){
            this.connect(this.items[i].node, 'onmouseenter', dojo.hitch(this, this.onItemEnter, this.items[i],i),true)
            this.connect(this.items[i].node, 'onmouseleave', dojo.hitch(this, this.onItemLeave, this.items[i],i),true)
        }
    },

    postCreate : function(){},

    createItem : function(params, src, pos){
        params.node = dojo.create('a', {href : params.href, innerHTML: params.content}, src, pos)
        return params.node
    },

    connect : function(obj, func, method){
        return dojo.connect(obj, func, this, method)
    },

    registerParent : function(Menu){
        if(this.parentMenu){
            this.parentMenu.unRegisterChild(this)
        }
        this.parentMenu = Menu
        this.parentMenu.registerChild(this)
    },

    unRegisterChild : function(Menu){
        for(var i=0;i<this.items.length;i++){
            if(this.items[i].childMenu && this.items[i].childMenu===Menu){
                this.items[i].childMenu.parentItem=null
                this.items[i].childMenu=null
                return
            }
        }
    },

    registerChild : function(Menu){
        for(var i=0;i<this.items.length;i++){
            if(this.items[i].childMenu===Menu){return}
        }


        for(var i=0;i<this.items.length;i++){
            if(this.items[i].srcNode.parentNode===Menu.srcNode.parentNode){
                this.items[i].childMenu = Menu
                Menu.parentItem = this.items[i]
                //this.createChildMarker(this.items[i].node)
                return;
            }
        }
    },

    startup : function(){
        if(this._started){return;} this._started = true;
    },

    disconnect : dojo.disconnect,

    placeAt : function(node, pos){
        //console.log('placing node', node, pos)
       dojo.place(this.domNode, node, pos) 
    } 
})

dojo.declare('tb.HorizontalMenu', tb.Menu,{
    postCreate : function(){
       dojo.addClass(this.domNode,'tbHorizontalMenu tbIB') 

       var nl = dojo.query('>', this.domNode).addClass('tbIB')

       //apply border collapse
       //we collapse the right border, so that the submenus who's position is calculated relative to the left corner do not get affected
       this.borderCollapse && nl.slice(0,-1).style('borderRight', 'none')
    }
})

tb.Menu.fx = {
    fade : {
        setShowStyle: function(){
            var node = this.domNode;
            this.domNode.style.visibility= 'visible'
            dojo.animateProperty({
                node : this.domNode,
                properties : {
                    opacity : {start : 0, end : 1}
                },
                duration:800
            }).play()
        },

        resetShowStyle : function(){
            this.domNode.style.visibility= ''
        }
    },
    wipe : {
        setShowStyle : function(){
            var node = this.domNode;
            dojo.style(node, 'overflow', 'hidden')
            this.domNode.style.visibility= 'visible'
            dojo.animateProperty({
                node : this.domNode,
                properties : {
                    height : {start : 0, end : parseInt(dojo.style(node, 'height'),10)},
                    width : {start : 0, end : parseInt(dojo.style(node, 'width'),10)}
                },
                onEnd : function(){
                    dojo.style(node, 'overflow','')
                },
                duration:200
            }).play()
        },

        resetShowStyle : function(){
            this.domNode.style.visibility= ''
        }
    }
}

dojo.declare('tb.VerticalMenu', tb.Menu,{
    postCreate : function(){
        dojo.addClass(this.domNode,'tbVerticalMenu tbIB') 

        var nl = dojo.query('>', this.domNode).style({'display': 'block'})

        //apply border collapse
        this.borderCollapse && nl.slice(0,-1).style('borderBottom', 'none')
    }

})

tb.Menu.relPos = new (dojo.declare(null, {
    horizontal : function(offset){
        offset = dojo.mixin({x:0,y:0}, offset||{})
        var pos = dojo.position(this.domNode),
            parentPos = dojo.position(this.parentItem.node)
        dojo.style(this.domNode, {
            top:offset.y+parentPos.y-pos.y+'px',
            left:offset.x+parentPos.x+parentPos.w-pos.x+'px'
        })
    },

    vertical : function(offset){
        offset = dojo.mixin({x:0,y:0}, offset||{})
        var pos = dojo.position(this.domNode),
            parentPos = dojo.position(this.parentItem.node)
            //parentBw= parseInt(dojo.style(this.parentItem.node, 'borderLeftWidth'),10)
        dojo.style(this.domNode, {
            top:offset.y+parentPos.y+parentPos.h-pos.y+'px',
            left:offset.x+parentPos.x-pos.x+'px'
        })
    }
}))()


