(function(){

var DrawApp = function() {
    var drawings = [];
    
    function drawIn( selector ) {
        var el = select( selector );
        if ( !el )
            return false;

        var drawing = new Drawing( this, el );
        drawings.push( drawing );
        return drawing.init();
    }

    return {
        drawIn: drawIn
    };
};

var Drawing = function( app, el ) {
    
    var drawingEl = el,
        width, height, offsetXy,
        topCanvas, bottomCanvas, topContext, bottomContext,
        drag = [], mode = 'freehand',
        shapes = [],
        textboxEl = null,
        colours = [ '000', 'fff' ],
        fill = false,
        size = 1,
        formId = null;
        
    function init() {
        
        width = drawingEl.clientWidth;
        height = drawingEl.clientHeight;
        
        drawingEl.style.position = 'relative';
        
        offsetXy = offset( drawingEl );
        
        bottomCanvas = drawingEl.appendChild( makeCanvas( {
            width: width, height: height,
            style: {
                position: 'absolute', left: 0, top: 0
            }
        } ) );
        bottomContext = bottomCanvas.getContext( '2d' );        

        topCanvas = drawingEl.appendChild( makeCanvas( {
            width: width, height: height,
            style: {
                position: 'absolute', left: 0, top: 0,
                cursor: 'default'
            }
        } ) );
        topCanvas.onselectstart = function() { return false; };
        topContext = topCanvas.getContext( '2d' );
        
        topContext.lineCap = bottomContext.lineCap = 'round';
        topContext.lineJoin = bottomContext.lineJoin = 'round';

        listen( topCanvas, 'mousedown', onmousedown );

        return this;
    }
    
    function onmousedown( ev, point ) {
        var point = getPoint( ev, offsetXy );
        
        topContext.strokeStyle = getStrokeStyle();
        topContext.lineWidth = getLineWidth();

        drag = [ point ];
        
        listen( topCanvas, 'mousemove', onmousemove );
        listen( topCanvas, 'mouseup', onmouseup );
        
        return false;
    }
    
    function onmousemove( ev ) {
        var point = getPoint( ev, offsetXy );

        if ( mode == 'freehand' || mode == 'eraser' ) {
            drag.push( point );
        }
        else {
            drag[ 1 ] = point;
        }
        
        topContext.clearRect( 0, 0, width, height );

        draw[ mode ]( {
            context: topContext,
            x: point[ 0 ], y: point[ 1 ],
            lineWidth: getLineWidth(),
            strokeStyle: getStrokeStyle(),
            fillStyle: getFillStyle()
        } );

        return false;
    }

    function onmouseup( ev ) {
        ignore( topCanvas, 'mousemove', onmousemove );
        ignore( topCanvas, 'mouseup', onmouseup );
        
        topContext.clearRect( 0, 0, width, height );
        
        bottomContext.strokeStyle = getStrokeStyle();
        bottomContext.lineWidth = getLineWidth();
        
        draw[ mode ]( {
            context: bottomContext,
            x: drag[ drag.length - 1 ][ 0 ],
            y: drag[ drag.length - 1 ][ 1 ],
            lineWidth: getLineWidth(),
            strokeStyle: getStrokeStyle(),
            fillStyle: getFillStyle()
        } );

        if ( mode != 'textbox') {
            shapes.push( {
                mode: mode,
                lineWidth: getLineWidth(),
                strokeStyle: getStrokeStyle(),
                fillStyle: getFillStyle(),
                drag: drag
            } );
        }

        return false;
    }
    
    var draw = {

        freehand: function( o ) {
            o.context.beginPath();
            for ( var i = 0; i < drag.length; i++ ) {
                o.context[ i == 0 ? 'moveTo' : 'lineTo' ]
                    ( drag[ i ][ 0 ], drag[ i ][ 1 ] );
            }
            o.context.stroke();
            o.context.closePath();
        },

        line: function( o ) {
            o.context.beginPath();
            o.context.moveTo( drag[ 0 ][ 0 ], drag[ 0 ][ 1 ] );
            o.context.lineTo( o.x, o.y );
            o.context.stroke();
            o.context.closePath();
        },

        box: function( o ) {
            o.context.beginPath();
            if ( o.fillStyle ) {
                o.context.fillStyle = o.fillStyle;
                o.context.fillRect(
                    drag[ 0 ][ 0 ], drag[ 0 ][ 1 ],
                    o.x - drag[ 0 ][ 0 ], o.y - drag[ 0 ][ 1 ]
                );
            }
            o.context.strokeRect(
                drag[ 0 ][ 0 ], drag[ 0 ][ 1 ],
                o.x - drag[ 0 ][ 0 ], o.y - drag[ 0 ][ 1 ]
            );

            o.context.closePath();
        },

        circle: function( o ) {            
            var x1 = drag[ 0 ][ 0 ], y1 = drag[ 0 ][ 1 ],
                x2 = o.x, y2 = o.y,            
                p = 4 * ( ( Math.sqrt( 2 ) -1 ) / 3 ),
                rx = ( x2 - x1 ) / 2, ry = ( y2 - y1 ) / 2,            
                cx = x1 + rx, cy = y1 + ry;
            
            o.context.beginPath();
            o.context.moveTo( cx, cy - ry);
            o.context.bezierCurveTo( cx + (p * rx), cy - ry,  cx + rx, cy - (p * ry), cx + rx, cy );
            o.context.bezierCurveTo( cx + rx, cy + (p * ry), cx + (p * rx), cy + ry, cx, cy + ry );
            o.context.bezierCurveTo( cx - (p * rx), cy + ry, cx - rx, cy + (p * ry), cx - rx, cy );
            o.context.bezierCurveTo( cx - rx, cy - (p * ry), cx - (p * rx), cy - ry, cx, cy - ry );            
            
            if ( o.fillStyle ) {
                o.context.fillStyle = o.fillStyle;
                o.context.fill();
            }
            o.context.stroke();
            o.context.closePath();
        },

        textbox: function( o ) {
            if ( textboxEl ) {
                return;
            }
            var fontSize = parseInt( o.lineWidth ) + 10;
            textboxEl = drawingEl.appendChild( make( {
                tag: 'textarea',
                style: {
                    position: 'absolute', left: o.x + 'px', top: o.y + 'px',
                    width: (width - o.x - 5) + 'px', height: (height - o.y - 5) + 'px',
                    border: 0, padding: 0, margin: 0, overflow: 'hidden',
                    fontFamily: 'Arial', fontSize: fontSize + 'px', color: o.strokeStyle,
                    background: 'transparent'
                }
            } ) );
            textboxEl.focus();
            listen( textboxEl, 'blur', function() {
                if ( textboxEl.value ) {
                    var textboxDiv = drawingEl.insertBefore( make( {
                        tag: 'div',
                        style: {
                            position: 'absolute', left: o.x + 'px', top: o.y + 'px',
                            fontFamily: 'Arial', fontSize: fontSize + 'px', color: o.strokeStyle
                        }
                    } ), topCanvas );
                    textboxDiv.innerHTML = nl2br( textboxEl.value );

                    textboxDiv.serializable = {
                        mode: 'textbox',
                        left: o.x,
                        top: o.y,
                        size: fontSize,
                        color: o.strokeStyle,
                        text: textboxEl.value
                    };
                    
                    shapes.push( textboxDiv );
                }
                drawingEl.removeChild( textboxEl );
                textboxEl = null;
            } );
        },

        eraser: function( o ) {
            var oldStrokeStyle = o.context.strokeStyle,
                oldLineWidth = o.context.lineWidth,
                colour = drawingEl.style.backgroundColor;

            o.context.strokeStyle = ( colour == '' || colour == 'transparent' ? '#fff' : colour );
            o.context.lineWidth = 10;

            draw.freehand( o );

            o.context.strokeStyle = oldStrokeStyle;
            o.context.lineWidth = oldLineWidth;
        }
        
    };

    function getStrokeStyle() {
        return '#' + colours[ 0 ];
    }

    function getFillStyle() {
        if ( fill ) {
            return '#' + colours[ 1 ];
        }
        return null;
    }

    function getLineWidth() {
        return size;
    }

    function undo() {

        bottomContext.clearRect( 0, 0, width, height );

        for ( var i = 0; i < shapes.length - 1; i++ ) {
            var shape = shapes[ i ];

            if ( shape.tagName ) // textbox
                continue;

            bottomContext.lineWidth = shape.lineWidth;
            bottomContext.strokeStyle = shape.strokeStyle;
            
            drag = shape.drag;

            draw[ shape.mode ]( {
                context: bottomContext,
                x: drag[ drag.length - 1 ][ 0 ],
                y: drag[ drag.length - 1 ][ 1 ],
                lineWidth: shape.lineWidth,
                strokeStyle: shape.strokeStyle,
                fillStyle: shape.fillStyle
            } );
        }

        var lastShape = shapes[ shapes.length - 1 ];
        if ( lastShape.tagName ) { // textbox
            lastShape.parentNode.removeChild( lastShape );
        }

        shapes.pop();
    }

    function switchOn(el)
    {
	// 'name' can be 'size' or 'mode'
	// first, switch off all elements in this group (size or mode)
	aButton = document.getElementsByName(el.name);

	for (var i=0; i < aButton.length; i++)
	{
		aButton[i].src = aButton[i].src.replace('_on', '_off');
	}

	el.src = el.src.replace('_off','_on');
	return true;
    }

    function setControls( c ) {

        var sizes = [ 1, 8, 24, 64, 128 ],
            sizesFound = 0;

        each( select( '*', c ), function( el ) {
            if ( el.name == 'mode' ) {
                listen( el, 'click', function() {
		    switchOn( el );
                    this.setMode( el.value );
                }, this );
            }
            else if ( el.name == 'undo' ) {
                listen( el, 'click', undo );
            }
            else if ( el.name == 'colour-1' || el.name == 'colour-2' ) {
                listen( el, 'change', function() {
                    colours[ ( el.name == 'colour-1' ? 0 : 1 ) ] = el.value;
                }, this );
                if ( el.className.indexOf( 'color-with-checkbox' ) > -1 ) {
                    el.toggle = function( toggle ) {
                        fill = toggle;
                    };
                }
            }
            else if ( el.name == 'fill' ) {
                listen( el, 'change', function() {
                    if ( el.type == 'checkbox' )
                        fill = el.checked;
                    else
                        fill = !fill;
                }, this);
            }
            else if ( el.name == 'size' ) {
                if ( el.tagName == 'select' ) {
                    listen( el, 'change', function() {
                        size = el.options[ el.selectedIndex ].value;
                    }, this);
                }
                else {
                    el.sizeValue = sizes[ sizesFound++ ];
                    listen( el, 'click', function() {
		       switchOn(el); 
                       size = this.sizeValue;
                    }, el);
                }
            }
            else if ( el.name == 'save' ) {
                listen( el, 'click', function() {

                    var form = select( 'body' )[ 0 ].appendChild( make( {
                        tag: 'form', method: 'post', action: 'draw.php', 'enc-type': 'multipart/form-data',
                        style: { display: 'none' }
                    } ) );

                    exportTo( form );
                    
                    form.submit();
                    
                }, this);
            }
        }, this );
    }

    function exportTo( form ) {

        form.appendChild( make( {
            tag: 'input', type: 'hidden', name: 'width', value: width
        } ) );

        form.appendChild( make( {
            tag: 'input', type: 'hidden', name: 'height', value: height
        } ) );

        var shapesEl = form.appendChild( make( {
            tag: 'textarea', name: 'shapes', style: { display: 'none' }
        } ) );
        shapesEl.innerHTML = JSON.stringify( shapes, function( key, value ) {
            if ( value && value.serializable ) {
                return value.serializable;
            }
            return value;
        } );
    }

    return {
        init: init,

        undo: undo,
        setMode: function( m ) { mode = m;  },
        setColour1: function( colour ) { colours[ 0 ] = colour; },
        setColour2: function( colour ) { colours[ 1 ] = colour; },
        setFill: function( f ) { fill = f; },
        setSize: function( s ) { size = s; },
        setControls: setControls,
        exportTo: exportTo
    };
};

//

function select( selector, p ) {
    if ( selector.substring( 0, 1 ) == '#' )
        return document.getElementById( selector.substring( 1 ) );
    var s = selector.split( '.' ),
        list = ( p ? p : document ).getElementsByTagName( s[ 0 ] ? s[ 0 ] : '*' ),
        a = [];
    for ( var i = 0; i < list.length; i++ ) {
        if (  !s[ 1 ] || classified( list.item( i ), s[ 1 ] ) )
            a.push( list.item( i ) );
    }
    return a;        
}

function each( o, f, s ) {
    for ( var i in o )
        if ( o.hasOwnProperty( i ) )
            if ( f.call( s, o[ i ], i, o ) === false )
                break;
    return o;        
}

function make( o ) {
    var el = document.createElement( o.tag );
    each( o, function( v, k ) {
        if ( k != 'tag' && k != 'style' && k != 'children' ) {
            k = k == 'cls' ? 'className' : k;
            el[ k ] = v;
        }
    } );
    each( o.style, function( v, k ) {
        el.style[ k ] = v;        
    } );
    each( o.children, function( v, k ) {
        el.appendChild( make( v ) );
    } );
    return el;
}

function makeCanvas( o ) {
    o.tag = 'canvas';
    var canvas = make( o );
    if (! canvas.getContext )
        G_vmlCanvasManager.initElement( canvas );
    return canvas;
}

function listen( el, e, f, s ) {
    var sf = null;
    if (s)
        sf = function() {
            return f.apply( s, arguments );
        };
    if ( el.addEventListener )
        el.addEventListener( e, s ? sf : f, false );
    else
        el.attachEvent( 'on' + e, s ? sf : f );
    return el;
}

function ignore( el, e, f ) {
    if ( el.removeEventListener )
        el.removeEventListener( e, f, false );
    else
        el.detachEvent( 'on' + e, f );
    return el;
}

function offset( el, relativeToEl ) {
    var offset = [ 0, 0 ];
    for ( var node = el; (relativeToEl ? node != relativeToEl && node : node); node = node.offsetParent ) {
        offset[ 0 ] += node.offsetLeft;
        offset[ 1 ] += node.offsetTop;
    }
    return offset;
}

function nl2br( text ) {
    var ch;
    text = escape( text );
    if ( text.indexOf( '%0D%0A' ) > -1 )
        ch = /%0D%0A/g;
    else if( text.indexOf( '%0A' ) > -1 )
        ch = /%0A/g;
    else if( text.indexOf( '%0D' ) > -1 )
        ch = /%0D/g;
    return unescape( text.replace( ch, '<br />' ) );
}

function getPoint( ev, offsets ) {
    return [
        ev.clientX - offsets[ 0 ] + ( document.documentElement.scrollLeft || document.body.scrollLeft ),
        ev.clientY - offsets[ 1 ] + ( document.documentElement.scrollTop || document.body.scrollTop )
    ];
}

//

window.DrawApp = DrawApp;

})();
