/**
* Zim Outliner
* @author Anil Bawa <anil@quotesque.net>
*
* Copyright (c) 2007 Anil Bawa-Cavia
* Published under an MIT License. See http://www.zimoutliner.com/license.html
*/


/**
* Globals
*/
KEY_ENTER = 13;
KEY_UP = 38;
KEY_DOWN = 40;
KEY_LEFT = 37;
KEY_RIGHT = 39;
KEY_DELETE = 8;

/**
* Namespaces
*/
var Zim = Class.create();
Zim.Mixin = Class.create();
Zim.Input = Class.create();
Zim.Util = Class.create();
Zim.Menu = Class.create();

/**
* Checkbox input
*/
Zim.Input.Checkbox = function(nodeID){this.nodeID =nodeID;this.id='check_'+nodeID};
Zim.Input.Checkbox.prototype = {
    nodeID:null,
    id:null,
    checked:null,
    type:'checkbox',
    className:'checkbox',
    render:function(){
        var o = document.createElement('input');
        Object.extend(o,this);
        return o;
    },
    
    onclick:function(){
        if(this.checked) {
            $(this.nodeID).checkChildren();
        } else {
            $(this.nodeID).uncheckChildren();
        }
    }
};

/**
* Editable cells in outline
*/
Zim.Input.ListCell = function(id) { this.id = id;}

Zim.Input.ListCell.prototype = {
    id:null,
    type:'text',
    tabindex:null,
    autocomplete:'off',
    size:100,
    value:null,

    render:function(){
        var o = document.createElement('input');
        Object.extend(o,this);
        o.setAttribute('autocomplete',this.autocomplete); //ffox bug workaround for focusing textinput
        o.setAttribute('tabindex',this.tabindex);
		o.observe('keypress',this.blah.bind(this));
        return o;
    },

    blah:function(event){
        event.node = this.getRow();
        //alert(event.charCode || event.keyCode || 0);
        var action = 'Press';
        var action = (event.ctrlKey) ? 'Fold' : action;
        var key = event.keyCode;
        Zim.Outline.Modified = true;
        var action = (event.shiftKey && !event.altKey) ? 'Yank' : action;
        if(key == KEY_ENTER){ Zim.Outline.Modified = true; this.onPressEnter(event); };
        if(key == KEY_UP && action!='Fold'){ Zim.Outline.Modified = true; this.getRow().list['on' + action + 'Up'](event); };
        if(key == KEY_DOWN && action!='Fold'){ Zim.Outline.Modified = true; this.getRow().list['on' + action + 'Down'](event); };
        if(key == KEY_LEFT){ Zim.Outline.Modified = true;this.getRow().list['on' + action + 'Left'](event); };
        if(key == KEY_RIGHT){ Zim.Outline.Modified = true; this.getRow().list['on' + action + 'Right'](event); };
        if(key == KEY_DELETE){ Zim.Outline.Modified = true; this.onPressDelete(event); };
    },

    onblur: function(){
        //alert('blurred');
        this.selected = false;
        this.getRow().removeClassName('highlight');
        this.getRow().removeWrapperHighlight();
    },

    onfocus: function(){
        this.selected = true;
        if(!this.getRow().hasClassName('expanded') && !this.getRow().hasClassName('collapsed')) this.getRow().addClassName('highlight');
        else this.getRow().highlight();
    },

    getRow:function(){
        return $(this.id.substring(5));
    },

    onPressEnter:function(event){
        this.getRow().list.append(this.getRow());
    },

    onPressDelete:function(event){
        if(!$(this.id).value.length) {
            this.getRow().list.onPressDelete(event);
			event.stop();
        }
    }
}

Zim.Input.Title = function(id) {this.id = id;};
Zim.Input.Title.prototype = {
    id:null,
    type:'text',
    tabindex:null,
    autocomplete:'off',
    size:60,
    value:null,
    disabled:false,
    
    render:function(){
        var container = document.createElement('div');
        container.id='titleContainer';
        var inner = document.createElement('div');
        inner.id='inner';
        var o = document.createElement('input');
        Object.extend(o,this);
        o.setAttribute('autocomplete',this.autocomplete); //ffox bug workaround for focusing textinput
        o.setAttribute('tabindex',this.tabindex);
        if(Zim.Outline.Current.isTodo()) { o.disabled = true; }
        inner.appendChild(o);
        container.appendChild(inner);
        /*var bundle = document.createElement('div');
        bundle.id = 'Bundle' + this.id;
        bundle.className = 'bundle';
        container.appendChild(bundle);*/
        return container;
    },
    onfocus:function(){
        this.addClassName('selected');
    },
    getBundle:function() {
        return $('Bundle' + this.id);
    },
    onblur:function(){
        this.removeClassName('selected');
    }
}

/**
* Zim Node component
*/
Zim.Node = function(id){this.id='node' + id;this.name= this.id; };

Zim.Node.prototype = {

    id:null,
    name:null,
    list:null,
    selected:false,
    collapsed:false,
    title:null,
    
    onchange: function(){
        //alert('changed');
    },

    blur:function(){
        this.getListCell().blur();
        this.removeWrapperHighlight();
    },
    
    removeWrapperHighlight:function(){
        $('wrapper_' + this.id).removeClassName('expandedhighlight');
    },

    highlight:function(){
        $('wrapper_' + this.id).addClassName('expandedhighlight');
    },
    
    focus:function(){
        this.getListCell().focus();
    },
    
    getValue:function(){
        return this.getListCell().value;
    },

    setIndex:function(n){
        this.tabindex = n+1;
        this.getListCell().setAttribute('tabIndex',this.tabIndex);
        //if(this.selected) this.list.selectedIndex = this.index;
    },

    render:function() {
        var node = document.createElement('li');
        Object.extend(node,this);
        node.addClassName('zimnode');
        var oCheckbox = new Zim.Input.Checkbox(this.id);
        var div = document.createElement('div');
        div.id = 'wrapper_' + this.id;
        div.className = 'wrapper';
        div.appendChild(oCheckbox.render());
        var oDomInput = new Zim.Input.ListCell('cell_' + this.id);
        var oWrapper = div.appendChild(oDomInput.render());
        var oChild = node.appendChild(div);
        if(this.title) oWrapper.value = this.title;
        return node;
    },

    
    isChecked:function(){
        return $('check_'+this.id).checked;
    },
    
    check:function(){
        $('check_'+this.id).checked = true;
        this.checkChildren();
    },
    
    uncheck:function(){
        $('check_'+this.id).checked = false;
        this.uncheckChildren();
    },
    
    checkChildren:function(){
        if(this.hasChildList()) {
            this.getChildListNodes().each(function(oNode) {
                oNode.check();
            });
        }
    },
    
    uncheckChildren:function(){
        if(this.hasChildList()) {
            this.getChildListNodes().each(function(oNode) {
                oNode.uncheck();
            });
        }
    },
    
    setChecked:function(){
        $('check_'+this.id).checked = 'checked';
    },
    
    isCheckedText:function(){
        return $('check_'+this.id).checked ? 'checked' : '';
    },
    
    expand:function(){
        if(this.hasChildListNodes()) {
            if(!this.hasClassName('expanded')){
                var oDiv = document.createElement('span');
                oDiv.className = 'triangle';
                oDiv.id = 'triangle_' + this.id;
                oDiv.innerHTML = '&#9662;';
                oDiv.onclick = this.collapse.bind(this);
                if(this.collapsed) this.select('.triangle').shift().remove();
                this.collapsed = false;
                this.insertBefore(oDiv,$('wrapper_'+this.id));
                this.className = 'expanded';
            }
            
            this.getChildListNodes().each(function(oChild,n) {
                oChild.show();
                oChild.expand();
            });
        }
    },

    collapse:function(){
        if(this.hasChildListNodes()) {
            if(!this.hasClassName('collapsed')) {
                var oDiv = document.createElement('span');
                oDiv.className = 'triangle collapsed';
                oDiv.innerHTML = '&#9656;';
                oDiv.onclick = this.expand.bind(this);
                this.collapsed = true;
                this.select('.triangle').shift().remove();
                this.insertBefore(oDiv,$('wrapper_'+this.id));
                this.className = 'collapsed';
            }
            
            this.getChildListNodes().each(function(oChild,n) {
                oChild.hide();
                oChild.collapse();
            });
        }
    },

    onmousedown:function(oEvent) {
        new Zim.Util.Drag(this.id);
    },
    
    clear:function(){
        this.remove();
    },

    show:function() {
        this.style.display = 'list-item';
    },

    hide:function() {
        this.style.display = 'none';
    },

    hasChildListNodes:function(){
        return $A(this.childNodes).findAll(function(oNode)
        {
            return oNode.nodeName == 'UL' ;
        }).size() > 0;
    },

    hasChildList:function(){
        return this.hasChildListNodes();
    },
    
    getChildList:function(){
        return $A(this.childNodes).find(function(oNode)
        {
            return oNode.nodeName == 'UL' ;
        });
    },
    
    getChildListNodes:function(){
        return $A(this.getChildList().childNodes);
    },
    
    getListCell:function(){
        //return $A(this.childNodes).find(function(oNode)
        //{
        //    return oNode.type == 'text';
        //});
        return $('cell_' + this.id);
    },
    
    getParentList:function(){
        if(this.hasParentList()){
            return this.parentNode;
        }
    },
    
    
    getParentListNode:function(){
        if(this.hasParentList()){
            return this.parentNode.parentNode;
        }
    },
    
    hasParentList:function(){
        return this.parentNode.nodeName =='UL';
    },
    
    push:function(oChildNode) {
        oChildNode.list = this.list;
        this.appendListChild(oChildNode.render());
        this.expand();
    },
    
    appendListChild:function(oNode){
        if(!this.hasChildListNodes()){
            var oList = document.createElement('ul');
            oList.addClassName('zimlist');
            oList.appendChild(oNode);
            this.appendChild(oList);
        } else {
            this.getChildList().appendChild(oNode);
        }
    },
    
    getListChildren:function(){
         return $A(this.getChildList().childNodes).findAll(function(oNode)
        {
            return oNode.nodeName == 'LI' ;
        });
    },
    
    hasListChildren:function(){
        return $A(this.getChildList().childNodes).findAll(function(oNode)
        {
            return oNode.nodeName == 'LI' ;
        }).size() > 0;
    },
    
    checkExpanded:function(){
        if(!this.hasListChildren()){
            this.removeClassName('expanded');
            $('wrapper_' + this.id).removeClassName('expandedhighlight');
            this.getChildList().remove();
            this.childNodes[0].remove();
        }
    },
    
    toJSON:function(){
        var oChildren = {};
        if(this.hasChildListNodes()){
            this.getListChildren().each(function(oChild,i){
                oChildren[i] = oChild.toJSON();
            });
        }
        return { id:this.id,value:escape(this.getValue()),children:oChildren,_isChecked:this.isCheckedText()};
    }
};

/**
* Outline manager
*/
Zim.Outline = function(id,bundle){
    this.id= 'list' + id;
    this.container = 'zimContainer' + id;
    this.color=bundle.color;
    if(bundle.name.length) this.bundle = bundle.name;
};

Zim.Outline.Save = function(auto,todo) {
    if(Zim.Outline.Modified) {
        Zim.Outline.Modified = false;
        if(!todo) var url = '/list/save/';
        else var url = '/list/savetodo/';
        var msg = (auto) ? 'Autosaving...' : 'Saving...';
        Zim.Menu.setStatus(msg,'info')
    }
        new Ajax.Request(url, {
            method: 'post',
            parameters: 'Outline='+Zim.Outline.Current.toJSON(),
            onSuccess: Zim.Outline.OnSaveSuccess
        });
};


Zim.Outline.OnSaveSuccess = function(response) {
    if (response.responseText.length) {
        if(!$('list'+response.responseText) && parseInt(response.responseText) > 0) {
            if(Zim.Outline.Current.id == 'list0') {
                //Console.Log(response.responseText);
                var newID = 'list' + response.responseText;
                $(Zim.Outline.Current.id).id = newID;
                Zim.Outline.Current.id = newID;
                Zim.Menu.selectedLists.push(parseInt(response.responseText));
                Zim.Menu.getOption('export').enable();                
                Zim.Menu.getOption('delete').enable();                
                $('export').setCommand(new Zim.Command.File.Export(parseInt(response.responseText)));
            }
        }
    }  
    //$('notice').update(response.responseText);
    Zim.Menu.setStatus('Last saved ' + Zim.Time.Print(),'info');
}

Zim.Outline.prototype = {

    id:null,
    container:null,
    bundle:'Miscellanious',
    selectedIndex:null,
    nodecount:0,
    title:'Untitled List',
    todo:false,
    
    isTodo:function() {
        return this.todo;        
    },
    
    push:function(oInput){
        oInput.list = this;
        $(this.id).appendChild(oInput.render());
        $(oInput.id).focus();
        this.nodecount++;
    },

    append:function(oContextNode){
        var oInput = new Zim.Node(this.nodecount++);
        oInput.list = this;

        if(oContextNode) { //middle of the list
            oContextNode.getParentList().insertBefore(oInput.render(),oContextNode.nextSibling);
        }
        
        $(oInput.id).focus();
    },

    getSelectedNode:function(){
        return this.getNode(this.selectedIndex);
    },

    getNode:function(n){
        try {
            return $(this.id).childNodes[n];
        }catch(ex) {
            return false;
        }
    },

    getTitle:function(){
        return $('title').value;
    },
    
    setTitle:function(val){
        $('title').value = val;
        if(Zim.Outline.Current.isTodo()) { $('title').disabled = true; }
        /*$('title').getBundle().innerHTML = this.bundle;
        $('title').getBundle().addClassName(this.color);*/
    },
    
    toJSON:function(){
        var oPack = {};
        $A($(this.id).childNodes).each(function(oNode,i) {
             oPack[i] = oNode.toJSON();
        });
        return $H({id:this.id,title:this.getTitle(),value:this.getTitle(),_isChecked:false,children:oPack}).toJSON();
    },
    
    /**
    * Parse nested JSON structure into a zim outline
    */
    parse:function(oListNode) {
        this.setTitle(oListNode.title);
        var parent = this;
        $A(oListNode.children).each(function(oChild,i) {
            parent._parse(oChild);
        });
        $('node0').focus();
    },
    
    _parse:function(oNode,oContextNode) {
        var oZimNode = new Zim.Node(this.nodecount++);
        var parent = this;
        oZimNode.title = oNode.title;
        if(oContextNode == undefined) this.push(oZimNode);
        else $(oContextNode.id).push(oZimNode);
        if(oNode._status=='checked') {
            oZimNode.setChecked();
        }
        $A(oNode.children).each(function(oChild) {
            parent._parse(oChild,oZimNode);
        });
        return oZimNode;
    },
    
    removeNode:function(oNode){
        oNode.clear();
    },

    render:function(){
        var oElement =document.createElement('ul');
        Object.extend(oElement,this);
        oTitleInput = new Zim.Input.Title('title');
        oTitleInput.value = this.title;
        $(this.container).appendChild(oTitleInput.render());
        $(this.container).appendChild(oElement);
    },

    onPressUp:function(event){
        var oSelectedNode = event.node;
        var oPreviousSibling = oSelectedNode.previousSibling;
        var oContextNode = oPreviousSibling;
        if(this.isValidNode(oContextNode)) {
                while(oContextNode.hasChildListNodes() && !oContextNode.collapsed) {
                    oContextNode = oContextNode.getChildListNodes().pop();
                }
                oSelectedNode.blur();
                oContextNode.focus();
        } else {
            var oPreviousParentNode = oSelectedNode.getParentListNode();
            if(this.isValidNode(oPreviousParentNode)){
                oSelectedNode.blur();
                oPreviousParentNode.focus();
            }
        }
    },

    isValidNode:function(oNode){
      return (oNode && oNode.nodeName == 'LI');
    },
    
    onPressDown:function(event){
        var oSelectedNode = event.node;
        
        if(oSelectedNode.hasChildListNodes() && !oSelectedNode.collapsed) {
            oSelectedNode.blur();
            return oSelectedNode.getChildListNodes()[0].focus();
        }
        
        var oNextSibling = oSelectedNode.nextSibling;
        if(oNextSibling) {
            oSelectedNode.blur();
            return oNextSibling.focus();
        }

        var oContextNode = oSelectedNode;
        try{
            while(oContextNode.hasParentList()) {
                var oNextNode = oContextNode.getParentListNode().nextSibling;
                if(this.isValidNode(oNextNode)) {
                    oSelectedNode.blur();
                    return oNextNode.focus();
                } else {
                    oContextNode = oContextNode.getParentListNode();
                }
            }
        } catch(ex){};
    },

    onPressDelete:function(event){
        var oPreviousSibling = event.node.previousSibling;
        if(oPreviousSibling) {
            oPreviousSibling.focus();
            this.removeNode(event.node);
        } else if(event.node.hasParentList())
        {
            var oParentNode = event.node.getParentListNode();
            try{
            if(oParentNode.hasParentList()){
                this.removeNode(event.node);
                oParentNode.checkExpanded();
                oParentNode.focus();
            }
            } catch(ex){};
        }
    },

    onPressLeft:function(event){
    },

    onPressRight:function(event){
    },

    onYankUp:function(event){
        var oSelected = event.node;
        var oPreviousSibling = oSelected.previousSibling;

        if(oPreviousSibling)
        {
            oSelected.parentNode.insertBefore(oSelected,oPreviousSibling);
            oSelected.focus();
        }
    },

    onYankDown:function(event){
        var oSelected = event.node;
        var oNextSibling = oSelected.nextSibling;

        if(oNextSibling) {
            if(oNextSibling.nextSibling){
                oSelected.getParentList().insertBefore(oSelected,oNextSibling.nextSibling);
            } else {
                oSelected.getParentList().appendChild(oSelected);
            }

            oSelected.focus();
        }
    },

    onYankLeft:function(event){
        var oNode = event.node;
        var oParent = oNode.getParentListNode();
        if(oNode.hasParentList()){
            try{
            oNode.parentNode.parentNode.getParentList().insertBefore(oNode,oNode.parentNode.parentNode.nextSibling);
            oParent.checkExpanded();
            oNode.focus();
            } catch(ex){};
        }
    },

    onYankRight:function(event){
        var oNode = event.node;
        var oPreviousNode = oNode.previousSibling;
        if(oPreviousNode){
            oPreviousNode.appendListChild(oNode);
            oPreviousNode.expand();
            oNode.focus();
        }
    },
    
    onFoldLeft:function(event){
        var oNode = event.node;
        oNode.collapse();
    },
    
    onFoldRight:function(event){
        var oNode = event.node;
        oNode.expand();
    }
};

Zim.Outline.Modified = false;

/**
* Utils
*/

Zim.Util.Drag = function(id) {this.init(id);};

Zim.Util.Drag.prototype = {
    scroll:true,
    target:null,
    timeThreshold:100,
    _active:false,
    
    init:function(id) {
        if(id) {
            this.target = $(id);
            this._onLoad();
            setTimeout(this.startDrag.bind(this),this.timeThreshold);
        }
    },
    
    startDrag:function(x,y) {
        if(this._active) {
            //alert('start drag');
            this.target.style.cursor = 'pointer';
        }
    },
    
    stopDrag:function() {
        Event.stopObserving(window,'mouseup');
        Event.unloadCache();
        this._active = false;
    },
    
    onDrag:function(event){
    },
    
    onDragOver:function(event,id){
        
    },
    
    onDragOut:function(event,id){
    },
    
    _onLoad:function() {
        //Event.observe(window, 'mouseup',this.stopDrag.bind(this));
        this._active=true;
    } 
};

Zim.Bundle = {};
Zim.Bundle.Title = function(id,title,color){ this.bundleID = id;this.value=title;this.id='name'+this.bundleID;this.color=color;};
Zim.Bundle.Title.prototype= {
    value:null,
    type:'text',
    id:null,
    color:null,
    bundleID:null,
    tabindex:null,
    autocomplete:'off',
    size:60,
    value:null,
    onfocus:function(){
        this.onfocusvalue = this.value;
        this.removeClassName('name');
        this.addClassName('selected');
    },
    onblur:function(){
        this.addClassName('name');
        this.removeClassName('selected');
        if(this.onfocusvalue != this.value){
            new Zim.Command.Bundle.Title.Save(this.bundleID,this.value);
        }
    },
    render:function(){
        var div = document.createElement('div');
        var input = document.createElement('input');
        div.className = 'title';
        input.addClassName('name');
        input.setAttribute('autocomplete',this.autocomplete); //ffox bug workaround for focusing textinput
        input.setAttribute('tabindex',this.tabindex);
        Object.extend(input,this);
        div.appendChild(input);
        $('bundle'+this.bundleID).appendChild(div);
    }
}

Zim.Time = function(){};
Zim.Time.Print = function() {
    var Stamp = new Date();
    var Hours = Stamp.getHours();
    var Mins = Stamp.getMinutes();
    var Time = (Hours >= 12)  ? 'PM' : 'AM';
    if (Hours > 12) Hours-=12;
    if (Hours == 0) Hours = 12;
    if (Mins < 10) Mins = "0" + Mins;
    return Hours.toString() + ':' + Mins.toString() + ' '+Time;
};

Event.observe(window, 'mousedown', function() {
    if(!Zim.Menu.active) {
        Zim.Menu.deselect(Zim.Menu.selected,true);
    }
});