/**
* Mind Mapping made with Mootools (HTML, Javascript)
* @license: GNU/GPLv3
* @author: Patrice Ferlet <metal3d@gmail.com>
*/

inmymind = new Class({
    canvas: null,
    currentNode: null,
    title: "noname",
    linetype: "line",
    zoom: 100,
    tree: null,
    theme: {js:null, css:null},
    /**
    * Generate map, initialize events on buttons, clicks, etc...
    */
    initialize: function (options){
        options = $merge({
            'theme' : 'm3d',
             'plugins' : []
        },options);

        $('toolbar').makeResizable({
            handle: 'resize-handler',
            modifiers:{x: 'width', y:false}
        })

        this.theme.css = new Asset.css("./themes/"+options.theme+"/theme.css");
        this.theme.js = new Asset.javascript("./themes/"+options.theme+"/theme.js");
        //$('theme-selector').setSelected(options.theme)
        $('theme-selector').value=options.theme;
        $('theme-selector').addEvent('change', this.setTheme.bind(this));

        if(options.plugins.length>0){
            for (var i=0; i<options.plugins.length; i++){
                new Asset.javascript('./js/plugins/'+options.plugins[i]+'.js');
            }
        }

        //create canvas
        this.canvas = new Element("canvas").set({
            "class" : "m3dcanvasmap",
            "width" : window.getWidth(),
            "height" : window.getHeight(),
        }).injectInside(document.body);

        window.addEvent('domready', function() {
            var _this = this;
            var r = new MooRainbow('bgcolor', {
                'startColor': [58, 142, 246],
                'onChange': function(color) {
                        if (_this.currentNode.hasClass('node'))
                            _this.currentNode.setStyle ('background-color',color.hex);
                }
            });
            var r2 = new MooRainbow('color', {
                id : "myRainbow2",
                'startColor': [58, 142, 246],
                'onChange': function(color) {
                        if (_this.currentNode.hasClass('node'))
                            _this.currentNode.setStyle ('color',color.hex);
                }
            });
        }.bind(this));


        document.body._children = [];

        window.addEvent('resize', function (){
            this.canvas.set({
                "width" : window.getWidth(),
                "height" : window.getHeight(),
            });
            this.redrawCanvas();
        }.bind(this));


        this.canvas.addEvent('dblclick', function (evt){
            //this.currentNode=document.body
            this.createNode(evt);
        }.bind(this));

        this.canvas.addEvent('click', function (evt){
            //this.currentNode=document.body
            $$('.active').removeClass('active');
        }.bind(this));

        window.addEvent('keydown', function(evt){
            if(SqueezeBox.isOpen) return;
            if(evt.code == 8 || evt.code == 46){ // del or suppr
                this.removecurrent();
            }
            else {

            }
        }.bind(this));

        //parent moves with children, so prepare children to be moved
        //if SHIFT key is pressed
        window.addEvent('keydown', function(evt){
            if(evt.code!=16) return;
            var node= this.currentNode;
            this.appendMoveToChild(node);
        }.bind(this));

        //shit key up, so remove chilrend moves
        window.addEvent('keyup', function(evt){
            if(evt.code!=16) return;
            
            var node= this.currentNode;
            this.removeMoveToChild(node);
        }.bind(this));

        $$('.zoom').addEvent('click',function (evt){
            var zoom = this.currentNode.getStyle('font-size').toInt();

            if (evt.target.hasClass('out')) zoom-=1;
            else zoom+=1;

            this.currentNode.setStyle('font-size', zoom);
            this.redrawCanvas();
            //this.zoom = zoom;

        }.bind(this));


        $$('#icon-set img').addEvent('click',function(evt){
            var img = evt.target.clone()
            img.injectTop(this.currentNode.getElements('.icon-set')[0]);
            img.addEvent('click', function(){
                this.destroy();
            })
        }.bind(this));


        $('apply-to-children').addEvent('click',function (evt){
                this.pasteStyles(this.currentNode);
        }.bind(this));



        //display help...
        var help = new Element('div').set({
            'html' : 'Double-click to add new node',
            'id' :   'start-help',
            'styles' : {
                'position': 'absolute',
                'left': window.getWidth()/2 - 100,
                'top':  window.getHeight()/2,
                'z-index': 5,
                'opacity' : 0.5
            }
        }).injectInside(document.body)


        $('help').addEvent('click', function(){
            SqueezeBox.open('help.html', {handler: 'iframe'});
        })

        $('toggleline').addEvent('click', function(){
            if(this.linetype == "line") this.linetype = "bezier";
            else this.linetype = "line";
            this.redrawCanvas();
        }.bind(this));

        $('save').addEvent('click',this.save.bind(this));
        $('open').addEvent('click',this.open.bind(this));

        this.createTree();


        SqueezeBox.initialize();
        this.currentNode = document.body;
        this.currentNode._treemap = this.tree;
        this.currentNode = this.createNode();
        this.currentNode.setStyles({
            'left' : this.canvas.getWidth()/2,
            'top' : this.canvas.getHeight()/2,
        })
        //this.currentNode.getElements('.text-element')[0].set('html','Parent Node')
        this.setNotetext(this.currentNode, 'Parent Node');
        this.currentNode.setStyle('font-size', '16pt')
        this.parentId = this.currentNode.id

        //everything is ok, say it to everybody !
        window.addEvent('load', function (){
            var evt = new MapEvent(this);
            window.fireEvent('mapready',evt);
        }.bind(this));

    },
    
    setTheme: function (){
            var theme = $('theme-selector').value;
            try{
                this.theme.js.destroy();
                this.theme.css.destroy();
            }catch(e){}
           
 
            this.theme.js = new Asset.javascript("./themes/"+theme+"/theme.js", {
                onload: function (){
                    window.fireEvent('themeloaded', this)
                }.bind(this)
            });
            this.theme.css = new Asset.css("./themes/"+theme+"/theme.css");

            

    },

    /**
    * Create the treeview
    */
    createTree: function (){
        this.tree = new MooTreeControl ({
            div: 'treeview',
            mode: 'folders',
            grid: true,
            'theme' : './images/mootree.gif',
            onSelect: function(node, state) {
                this.selectNode(node);
            }.bind(this)
        },{
            text: 'Mind Map Tree',
            open: true
        });//.adopt('tree');
    },

    /**
    * Set the current node
    */
    selectNode : function(node) {
        var id = node.id.replace('map-','');
        $$('.node').removeClass('active');
        $(id).addClass('active');
        this.currentNode = $(id);
    },

    /**
    * Apply styles to children
    */
    pasteStyles: function(node) {
        var i = 0;
        for (i=0; i < node._children.length; i++){
            var child = $(node._children[i]);
            child.setStyles({
                'background-color' : node.getStyle('background-color'),
                'color' : node.getStyle('color'),
                'font-size': node.getStyle('font-size'),
            })
            if (child._children.length > 0) {
                this.pasteStyles(child);
            }
        }
    },

    /**
    * Create node at mouse click or with given id (on import)
    */
    createNode: function (evt){

        try { $('start-help').destroy(); } catch(e) {};
        var x, y = 50;
        var id ='node'+parseInt(Math.random()*999999);

        if(evt && ! evt.client) {
            //id given
           id = evt
        }
        else if(evt) {
            x= evt.client.x
            y= evt.client.y
        }

        var node = new Element ('div').set({
            'class': 'node',
            'id'   : id
        }).setStyles({top: y, left: x})

        var iconscontainer = new Element('div').set({
            'class' : 'icon-set'
        });

        var textcontainer = new Element('div').set({
            'class': 'text-element',
            'html' : 'new node'
        });

        iconscontainer.injectTop(node);
        textcontainer.injectInside(node);

        node._children = []
        node.injectInside(document.body);
        this.initiateEvents(node);
        //this.currentNode = node;
        this.currentNode._children.push(node.id);
        this.currentNode.addClass('active');

        this.redrawCanvas();

        //append element to treeview
        node._treemap = this.currentNode._treemap.insert({text: 'new node', 'id': 'map-'+node.id});
        this.tree.expand();

        return node;
    },

    /**
    * Change node title
    */
    setNotetext: function (node,text) {
        node.getElements('.text-element')[0].set('html',text);
        node._treemap.text= text;
        node._treemap.update();
        this.redrawCanvas();
    },

    /**
    * Initialize events on created node
    */
    initiateEvents: function (node){
        var _this = this;

        node.addEvent('dblclick', function (){
            var t='';
            if(t = prompt("Change text",node.getElements('.text-element')[0].get('html'))){
                this.setNotetext(node,t)
            }
        }.bind(_this));


        node.addEvent('mousedown', function(){
            $$('.node').removeClass('active');
            this.addClass('active');
            _this.currentNode = node

            //unselect every treenode then select current
            $$('.node').each(function(el){
                var treenode = _this.tree.get('map-'+el.id).select(false);
            })

            var treenode = _this.tree.get('map-'+node.id)
            treenode.select(true)
        });


        node.drag = new Drag(node,{
            onDrag: function (element) {
                this.redrawCanvas()
            }.bind(this),
            
        }).attach();

        node.addEvent('mousewheel',function(evt){
            evt.stopPropagation();
            var n = evt.target.getParent();
            var t = evt.target.getParent().getElements('.text-element')[0];
            var s = t.getStyle('font-size').toInt()+evt.wheel
            t.setStyle('font-size',s);
            this.redrawCanvas();
            var myFx = new Fx.Scroll(window, {
                duration: 0
            }).toElement(evt.target).start();
         }.bind(this))

    },

    /**
    * Append node children moves on parent move 
    **/
    appendMoveToChild: function (node, handler) {
        if(!handler) handler=node;
        for(var i=0; i< node._children.length; i++){
            var child = $(node._children[i]);
            child.addClass('active');
            child.parentdrag = new Drag(child,{
                handle: handler
            }).attach();
            if (child._children.length > 0) {
                this.appendMoveToChild(child, handler);
            }
        }
    },

    /**
    * Remove children moves with parend node
    */
    removeMoveToChild: function (node){
        for(var i=0; i< node._children.length; i++){
            var child = $(node._children[i]);
            child.removeClass('active');
            try { child.parentdrag.detach(); } catch (e) {}
            if(child._children.length > 0) {
                this.removeMoveToChild(child);
            }
        }
        
    },

    /**
    * Redraw canvas
    */
    redrawCanvas : function () {
        var c = this.canvas;
        var ctx = c.getContext("2d");
        var i = 0;
        //ctx.save();
        ctx.moveTo(0,0); ctx.clearRect(0,0,window.getWidth(),window.getHeight());

        for (i=0; i< document.body._children.length; i++){
            this.generateLines(document.body._children[i]);
        }
        //ctx.restore()
    },

    /**
    * Get each node and try to join children with lines
    */
    generateLines: function (node1) {
        node1 = $(node1); //get from id
        var i = 0;
        if(node1 && node1._children.length > 0) {
            for (i=0; i<node1._children.length; i++) {
                try {
                    if(node1._children[i] && $type($(node1._children[i])) == "element") {
                        this.drawLine(node1, $(node1._children[i]));
                        this.generateLines(node1._children[i]);
                    }
                }catch(e){alert(e.message)}
            }

        }
    },

    /**
    * Draw line between two nodes
    */
    drawLine: function (node1, node2) {
        var st  = {x: node1.getPosition().x + (node1.getSize().x/2 ) , y: node1.getPosition().y + (node1.getSize().y/2 )};
        var end = {x: node2.getPosition().x + (node2.getSize().x/2 ),  y: node2.getPosition().y + (node2.getSize().y/2 )};

        ctx = this.canvas.getContext('2d');

        ctx.beginPath();
        ctx.moveTo(st.x, st.y)
        if(this.linetype == "line") {
            ctx.lineTo(end.x, end.y);
            //ctx.closePath();
        } else { //bezier
            var offsetx = (6/10) * (end.x - st.x )
            var offsety = 0
            var q1 = {
                x: st.x + offsetx,
                y: st.y + offsety
            };

            var q2 = {
                x: end.x - offsetx,
                y: end.y - offsety
            };
            ctx.bezierCurveTo(q1.x, q1.y, q2.x, q2.y, end.x, end.y);
        }

        ctx.stroke();

    },

    /**
    * Remove current node and children
    */
    removecurrent: function (znode) {
        var s, i ,j = 0
        var node = (znode) ? $(znode) : this.currentNode;

        for (i=0; i<node._children.length; i++) {
            this.removecurrent(node._children[i]);
        }

        //cleanup
        for (i=0; i<node._children.length;i++) {
            if(!$(node._children[i].id)){
                delete node._children[i]
            }
        }

        node.destroy()
        this.redrawCanvas()
    },

   /**
    * Generate node xml (recursive)
    */
    generateXML: function (node) {
        var i = 0;
        var xml='';
        node = (node) ? $(node) : document.body;
        for (i=0; i< node._children.length; i++) {
            var n = $(node._children[i]);
            if (!n) break;
            xml+='<node id="'+n.id+'" x="'+n.getPosition().x+'" y="'+n.getPosition().y+'" fontsize="'+n.getStyle('font-size')+'" bgcolor="'+n.getStyle('background-color')+'" color="'+n.getStyle('color')+'">';
            xml+='<text><![CDATA['+n.getElements('.text-element').get('html')+']]></text>';
            
            //icons
            if(n.getElements('.icon-set img').length > 0){
                for (var img=0; img<n.getElements('.icon-set img').length; img++){
                    var icon = n.getElements('.icon-set img')[img].get('alt');
                    xml+= '<icon name="'+icon+'" />';
                }
            }
            
            if(n._children.length > 0){
                xml+=this.generateXML(n);
            }
            xml+='</node>';
        }
        return xml;
    },

    /**
    * onSave, generate mindmap XML representation
    */
    save: function (){
        var xml = '<?xml version="1.0" encoding="utf-8" ?>'+"\n";
        xml += '<map linetype="'+this.linetype+'">'+"\n";

        xml += this.generateXML();
        //alert(xml);
        xml += "\n"+'</map>'


        var evt = new MapEvent(this);
        evt.xml = xml;
        window.fireEvent('mapsave',evt);
        if (evt.stopped) return;

        var sb = $('save-box').clone().setStyle('display','block');

        var t = sb.getElements('textarea')[0].set('text',xml).injectInside(sb).setStyles({
            'height' : '100%',
            'width'  : '100%'
        });
        SqueezeBox.open(sb, {'handler' : 'adopt'})

        window.fireEvent('saved');
    },

    /**
    * Draw open box to import XML
    */
    open: function (){
        var evt = new MapEvent(this);
        window.fireEvent('mapopen',evt);
        if (evt.stopped) return;

        var box = $('open-box').clone().setStyle('display','block');
        box.getElements('button.import')[0].addEvent('click',this.importxml.bind(this));
        this.currentBox = SqueezeBox.open(box,{'handler': 'adopt'});
    },

    /**
    * onImport, generate mindmap from XML representation
    */
    importxml: function (evt) {
        if(evt.target) var xml = evt.target.getPrevious().value;
        else var xml = evt;

        var parser=new DOMParser();
        var xmlDoc=parser.parseFromString(xml,"text/xml");
        var map = xmlDoc.getElementsByTagName('map')[0];
        this.linetype = map.getAttribute('linetype');
        var root = map.getElementsByTagName('node')[0]

        //this.removecurrent(document.body._children[0]);

        for (var i=0; i<document.body._children.length; i++) {
            if($(document.body._children[0])._children.length){
                for( var j = 0; j < $(document.body._children[0])._children.length; j++){
                    this.removecurrent($(document.body._children[0])._children[j]);
                }
            }
            this.tree.get('map-'+document.body._children[i]).remove();
        }

        $(document.body._children[0]).destroy();
        delete document.body._children;
        document.body._children=[]

        this.currentNode = document.body;
        this.currentNode._treemap = this.tree;
        this.injectXMLnode(root);
        this.redrawCanvas()
        this.currentBox.close();
    },

    /**
    * Create the canvas node from xml representation (recursive)
    */
    injectXMLnode: function (root, currentnode) {
        if(currentnode){
            this.currentNode = currentnode;
        }

        this.currentNode = this.createNode(root.getAttribute('id'));
        currentnode = this.currentNode
        this.setNotetext(this.currentNode, root.getElementsByTagName('text')[0].textContent);
        

        this.currentNode.setStyles ({
            'top'  : root.getAttribute('y')+"px",
            'left' : root.getAttribute('x')+"px",
            'background-color' : root.getAttribute('bgcolor'),
            'color' : root.getAttribute('color'),
            'font-size' : root.getAttribute('fontsize')
        });

        if(root.children.length > 0) {
            var i = 0;
            for (i=0; i < root.children.length ; i++) {
                if(root.children[i].nodeName=='icon'){
                    var alt = root.children[i].getAttribute('name');
                    var img = $('icon-set').getElements('img[alt='+alt+']')[0];
                    img.clone().injectInside(currentnode.getElements('.icon-set')[0]).addEvent('click',function(){
                        this.destroy();
                    });
                }
            
                if(root.children[i].nodeName=="node")
                    this.injectXMLnode(root.children[i],currentnode);
            }
        }
        return currentnode;
    }
});

var MapEvent = new Class({
    stopped: false,
    map: null,
    initialize: function (map){
        this.map = map;
    },
    stop: function (){
        this.stopped = true;
    }
});

