
NeuralMenu = function() {
    this.init.apply(this, arguments);
}

NeuralMenu.prototype = {
    o: {
        data_url: '/index/structure.json',
        doCufon: true //(!$.browser.msie)
    },

    init: function(container, o) {
        var self = this;
        var $container = $(container);
        self.container = $container[0];
        
        // Set container size & position
        var size = self._getSize();
        var w = size.width, h = size.height;

        $container.width(w).height(h).css({
            position: 'absolute',
            top: ($(window).height() - h) / 2,
            left: ($(window).width() - w) / 2
        });
        
        
        //init canvas
        //Create a new canvas instance.
        self.canvas = new Canvas('mycanvas', {
            'injectInto': $container.attr('id'),
            'width': w,
            'height': h
        });

        // Resize event.
        $(window).resize(function(){ self.resize() });

        // afterCompute event
        $(window).bind('ht-afterCompute', function(e) {self.afterComputeListener(e); });
        
        //init Hypertree
        var ht = self.ht = new Hypertree(self.canvas, {
            //Change node and edge styles such as
            //color, width and dimensions.
            Node: {
                dim: 16,
                color: "#e22",
                type: 'circle',
                overridable: true
            },
            
            Edge: {
                type: 'hyperline',
                lineWidth: 1,
                color: "#088"
            },
            
            onBeforeCompute: function(node){},
            
            // This method is only triggered on label creation
            onCreateLabel: function(domElement, node){
                var $$ = $(domElement);
                $$.text(node.name);

                $$.click(function () {
                    self.selectMenuItem(node);
                    self.loadContent(node);
                    return false;
                });
            },
            
            //Change node styles when labels are placed
            //or moved.
            onPlaceLabel: function(domElement, node){
                var $$ = $(domElement);
                var style = domElement.style;
                style.display = '';
                style.cursor = 'pointer';

                // Blinking nodes
                if (node.data.model == 'vrpage') $$.addClass('blink');

                // Leaf Nodes
                if (node._depth == 2 ) {
                    $$.removeClass('rootNode catNode leafNode').addClass('leafNode');
                    var text = $$.text();
                    $$.empty().html(text);
                    
                    var offset = $$.position();
                    var h = $(self.container).height(), w = $(self.container).width();
                    // Fix leaf node vertical position for nodes at top & bottom
                    if (offset.top < h / 4) {
                        style.top = (parseInt(style.top) - 25) + 'px';
                    } 
                    else if (offset.top > h / 3) {
                        style.top = (parseInt(style.top) + 5) + 'px';
                    }
                    // Fix leaf node horizontal position
                    if (offset.left < w / 4) {
                        style.left = (parseInt(style.left) - 10) + 'px';
                    } else {
                        style.left = (parseInt(style.left) + 10) + 'px';
                    }
                
                } 
                else {
                    // Root Node
                    if (node._depth == 0 ) {
                        $$.removeClass('rootNode catNode leafNode').addClass('rootNode');
                        var text = $$.text();
                        $$.empty().html(text);
                        if (self.o.doCufon) Cufon.replace(domElement);
                    }
                    else if (node._depth <= 1) {
                        $$.removeClass('rootNode catNode leafNode').addClass('catNode');
                        var text = $$.text();
                        $$.empty().html(text);
                        if (self.o.doCufon) Cufon.replace(domElement);

                        var offset = $$.position();
                        var h = $(self.container).height(), w = $(self.container).width();
                        if (offset.top < h / 4) {
                            style.top = (parseInt(style.top) - 37) + 'px';
                        } 
                        
                    
                    } else {
                        style.display = 'none';
                    }


                    style.top = (parseInt(style.top) - 15) + 'px';
                }
                

                var left = parseInt(style.left);
                var w = domElement.offsetWidth;
                style.left = (left - w / 2) + 'px';

            },

            //  Called after computing node positions
            onAfterCompute: function(){
                $(window).trigger('ht-afterCompute');                
            }
        });


        // Load Tree Data
        $.getJSON(self.o.data_url, function(data, resp) {
            //load JSON data.
            ht.loadJSON(data);
            
            //compute positions and plot.
            ht.refresh();
            ht.controller.onAfterCompute();

            // Determine page to show based on url hash.
            url = window.location.hash.replace(/^#/,''); 
            if (!url) url = '/';
            var node;
            Graph.Util.eachNode(self.ht.graph, function(thisnode) {
                if (node) return;
                if (thisnode.data.url == url) node = thisnode;
            });
            if (node) {
                self.selectMenuItem(node);
                self.loadContent(node);
            }
        });
    },

    // Determines the menu width/height based on window size
    _getSize: function() {
        // Determine width/height
        var s = Math.min($(window).width() - 200, $(window).height() - 100);
        return {width: s, height: s};
    },
    
    // Resize Menu container & canvas
    resize: function() {
        var self = this;
        
        // Set container size & position
        var size = self._getSize();
        var w = size.width, h = size.height;
        $(self.container).width(w).height(h).css({
            top: ($(window).height() - h) / 2,
            left: ($(window).width() - w) / 2
        });

        // IE seems not to be able to resize the canvas without problems
        if ($.browser.msie) {
            return;
        }

        self.canvas.resize(w, h);
        setTimeout(function() {
            var node = self.ht.graph.getNode($('.node.highlight').attr('id'));
            if (!node) node = Graph.Util.getClosestNodeToOrigin(self.ht.graph, "pos");
            self.ht.refresh(true);
            self.selectMenuItem(node, {duration: 0});
            self.ht.controller.onAfterCompute();
        }, 100);
    },

    // Selects and focuses a menu node with `id`
    selectMenuItem: function(node, o) {
        var self = this;

        o = $.extend({}, o);

        if (self.ht.graph.hasNode(node.id)) {
            $('.node.highlight').removeClass('highlight');
            var id = node.id;
            if (!self._child_count(node)) {
                // Choose parent node id if this is a leaf
                pnode = Graph.Util.getParents(node)[0];
                id = pnode.id;

                // Highligting for leaf node
                $('.node#' + node.id).addClass('highlight');
                                
                // Animation duration of 0 if the node is already the origin.
                var origin = Graph.Util.getClosestNodeToOrigin(self.ht.graph, "pos");
                if (origin == pnode)
                    o['duration'] = 0;
            }
            self.ht.onClick(id, o);
        }
    },

    // Listener for the ht-afterCompute event which is triggered
    // by the hypertree once it has computed all node positions.
    afterComputeListener: function(e) {
        var self = this;
        self.positionDialog(); 
    },

    // Positions the dialog box by looking at the label of the currently
    // selected node.
    positionDialog: function() {
        var self = this;
        var node = Graph.Util.getClosestNodeToOrigin(self.ht.graph, "pos");

        // Position the dialog based on label position and offset to the container
        var $label = $('.node.highlight');
        var container_pos = $('#menu-container').offset();
        var pos = $label.offset();
        pos.top = pos.top - container_pos.top;
        var $dialog = $('#main-content').hide();
        var y = $(window).height() / 2;
        var x = $(window).width() / 2;

        if (pos.top > y) {
            var t = pos.top - $dialog.height() - 44;
            $dialog.find('.arrow').addClass('arrowDown').removeClass('arrowUp');
        } else {
            var t = pos.top + $label.height() + 44;
            $dialog.find('.arrow').addClass('arrowUp').removeClass('arrowDown');
        }
        var l = pos.left - ($dialog.width() /2) + 60;
        $dialog.css({
            top: t,
            left: l
        }).fadeIn('fast');
    },

    // Loads html content associated with the given node
    loadContent: function(node) {
        var self = this;
        var data = node.data
        
        // remove the previous dialog
        var $dialog = $('#main-content').remove(); 

        // Load vr image interface if we have a vrimage model type
        if (data.model == 'vrimage') {
            $(window).trigger('loadVRImage', [data.url]);
            return false;
        }

        // Create the dialog for showing page content (only if data.model is defined)
        if (typeof(data.model) != 'undefined') {
            $dialog = $('<div id="main-content" class="main-dialog dialog"><span class="arrow"></span><a class="close" title="close">x</a><div class="content"></div></div>').appendTo($(self.container).parent()).hide();
            $content = $dialog.find(' > .content');

            // Make dialog draggable
            $dialog.prepend('<div class="dragme"></div>').draggable({
                containment: 'parent',
                handle: '.dragme'
            })
            // Dialog close button
            $dialog.find('a.close').click(function() {
                $dialog.hide();
            });
        }
       
        // Fill the content either from the json structure or by making an 
        // ajax call to the url property for selected node.
        if (typeof(data.text) != 'undefined' && data.text.length) {
            $content.html(data.text);
        }
        else if(typeof(data.model) != 'undefined') {
            $.get(data.url, function(text, status) {
                var $html = $(text).find('#wrap').attr('id', '');
                $content.append($html);
                
                // No dialog if there is no content to display
                if (!$.trim($content.text()) && ! $content.find('img').length) $dialog.remove();

                // Ajaxify links
                $content.find('a').click(function() {
                    var url = this.href.replace(/http:\/\/[^\/]+/, '');
                    
                    // VR Page links
                    if ($(this).is('.image-only-vr')) {
                        $(window).trigger('loadVRImage', [url, true]);
                        return false;
                    }
                    if (/\/static\/files\/vr\//.test(url)) {
                        $(window).trigger('loadVRImage', [url]);
                        return false;
                    }
                    var node = self.ht.graph.getNode(url);
                    if (node) {
                        self.selectMenuItem(node);
                        self.loadContent(node);
                        return false;
                    }
                });

                // Track image loading & adjust width to fit in dialog.
                var $images = $content.find('img');
                var n_images = $images.length;
                $images.load(function() {
                    var w = $(this).width();
                    if (w > 320) {
                        $(this).width(320).height('auto');
                    }
                    --n_images;
                });

                // Dialog content scroller
                var timer = window.setInterval(function(){
                    // Initialize scroller after all images load.
                    var hidden_parents = $content.parents(':hidden').length;
                    if (!hidden_parents && n_images < 1) {
                        $content.Scroller();
                        window.clearInterval(timer);                        
                    }
                }, 150);

            });
        }
        
        // Update url hash 
        var cur_hash = window.location.hash.replace(/^#/, '');
        if (cur_hash != node.id)
            window.location.hash = node.data.url;
    },

    // returns the number of children for the given node
    _child_count: function(node) {
        var c = 0;
        Graph.Util.eachSubnode(node, function() {
            c += 1;
        });
        return c;
    }
}

