var Wrapper = function()
{
    var _pub = {
        wrap: function(node,classname,override,sticky,add)
        {
            var backup = Wrapper.originalTextNode(node);

            if(classname == 'hilighted' && override)
                alert('fail');
            if(!node.parentNode)
                return;
            if(!add)
                Wrapper.free(classname);
            if(!node.parentNode)
                node = backup;
            if (node.parentNode.hasClassName('wrapper'))
            {
                if(override && !node.parentNode.hasClassName(classname))
                    node.parentNode.className = 'wrapper '+classname;
                return;
            }
            var d = new Date()
            //Debug.set('nuts',d.getTime());
            var left = node.previousSibling;
            var right = node.nextSibling;

            var wrapper = document.createElement('span');
            wrapper.stored_class = classname;
            wrapper.addClassName(classname);
            wrapper.addClassName('wrapper');
            node.parentNode.insertBefore(wrapper,node);
            wrapper.appendChild(node);

            if(!sticky)
                Event.observe(wrapper,'mouseout',function(){Wrapper.free(classname);});

            //this is so if a 'selected' wrapper has broken up a node, we
            //walk over it and hilight the node on the other side(if applicable)
            //so it looks like the entire original node is hilighted
            if(classname == 'hilighted' && Util.match(node,'text'))
            {
                if(Util.match(left,'span.wrapper.selected') && left.firstChild.nodeType == 3)
                {
                    left = left.previousSibling;
                    if(left && left.nodeType == 3)
                        Wrapper.wrap(left,classname,override,sticky,1);
                }
                if(Util.match(right,'span.wrapper.selected') && right.firstChild.nodeType == 3)
                {
                    right = right.nextSibling;
                    if(right && right.nodeType == 3)
                        Wrapper.wrap(right,classname,override,sticky,1);
                }
            }
        },

        free: function(classname,except)
        {
            var editable_divs = new Array();
            var textnodes = new Array();
            var d = new Date();
            if(!except)
                except = new Array();
            else if(!Object.isArray(except))
                except = new Array(except);
            var wrapped;
            if(classname)
                wrapped = $$('span.wrapper.'+classname);
            else
                wrapped = $$('span.wrapper');
            wrapped.each(function(w)
            {
                var editable_div = w.up('div.editable');
                if(editable_divs.indexOf(editable_div) == -1)
                    editable_divs.push(editable_div);
                var contents = w.firstChild;
                if(contents)
                {
                    if(except.indexOf(contents) == -1)
                    {
                        w.parentNode.insertBefore(contents,w);
                        w.remove();
                        TextNode.joinChildren(contents.parentNode)
                        contents.deactivated = d.getTime();
                        if(contents.nodeType == 3)
                            textnodes.push(contents);
                    }
                }
                else
                    w.remove();
            });
            if(classname && classname == 'selected')
            {
                Util.collectAncestors(textnodes,'p,li,'+Format.headings().join(',')).each(Format.isolate);
                editable_divs.each(function(div){
                    Format.ensureIntegrity(div);
                });
            }
        },

        ancestor: function(element)
        {
            var current = element.parentNode;
            while(current.hasClassName('wrapper'))
                current = current.parentNode;

            return current;
        },

        hide: function()
        {
            var wrapped = $$('span.wrapper');
            wrapped.each(function(w)
            {
                w.removeClassName(w.stored_class);
            });
        },

        show: function()
        {
            var wrapped = $$('span.wrapper');
            wrapped.each(function(w)
            {
                w.addClassName(w.stored_class);
            });
        },

        originalTextNode: function(textnode)
        {
            Debug.disableLog();
            Debug.log('given '+Debug.summarise(textnode));
            var current;
            if(textnode.parentNode.hasClassName('wrapper'))
            {
                current = textnode.parentNode;
                Debug.log('moved up to parent wrapper');
            }
            else
                current = textnode;

            while(current.previousSibling && (current.previousSibling.nodeType
                == 3 || (current.previousSibling.hasClassName('wrapper') &&
                current.previousSibling.firstChild.nodeType == 3) ||
                current.previousSibling.hasClassName('dropper')))
            {
                Debug.log('has a prev sibling');
                if(current.previousSibling.nodeType == 3)
                    Debug.log('it is a textnode');
                else if(current.previousSibling.hasClassName('wrapper'))
                    Debug.log('it is a wrapper');
                if(current.previousSibling.nodeType == 3 ||
                    current.previousSibling.hasClassName('wrapper') ||
                    current.previousSibling.hasClassName('dropper'))
                {
                    current = current.previousSibling;
                    Debug.log('moved left to ' + Debug.summarise(current));
                }
            }
            if(current.nodeType != 3 && current.hasClassName('wrapper'))
                current = current.firstChild;

            Debug.log('returning ' + Debug.summarise(current));
            Debug.enableLog();
            return current;
        },

        originalOffset: function(textnode,offset)
        {
            Debug.disableLog();
            Debug.log('');
            Debug.log('given '+ Debug.summarise(textnode));
            var current;
            if(textnode.parentNode.hasClassName('wrapper'))
            {
                current = textnode.parentNode;
                Debug.log('moved up to parent wrapper');
            }
            else
                current = textnode;

            while(current.previousSibling && 
                (current.previousSibling.nodeType == 3 ||
                (current.previousSibling.hasClassName('wrapper') &&
                current.previousSibling.firstChild.nodeType == 3)))
            {
                Debug.log('has a prev sibling: ' +
                Debug.summarise(current.previousSibling));

                current = current.previousSibling;
                if(current.nodeType == 3)
                {
                    Debug.log('moved left to ' + Debug.summarise(current));
                    offset += current.data.length;
                }
                else
                {
                    Debug.log('moved left to wrapper');
                    offset += current.firstChild.data.length;
                }

                Debug.log('next prev sibling: ' +
                Debug.summarise(current.previousSibling));
            }

            Debug.enableLog();
            return offset;
        },

        get: function(classname)
        {
            var wrappers;
            if(classname)
                wrappers = $$('span.wrapper.' + classname);
            else
                wrappers = $$('span.wrapper');

            var ret = new Array();
            wrappers.each(function(w){
                if(w.firstChild)
                    ret.push(w.firstChild);
            });

            return ret;
        }
    };

    return _pub;
}();
