this.initialize = function() {
    this.drawing_private = {};
    this.future(0).clientWatch();
};

this.clientWatch = function() {
    
    var clients = this.find( "doc('http://vwf.example.com/clients.vwf')" )[0];

    if ( clients !== undefined ) {
        
        clients.children.added = clients.events.add( function( index, child ) {
            this.clientJoin( this.moniker );
        }, this );

        clients.children.removed = clients.events.add( function( index, child ) {
            this.clientLeave( this.moniker );   
        }, this );

    }

};

// this.isValid = function( obj ) {
//     var objType = ( {} ).toString.call( obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
//     return ( objType != 'null' && objType != 'undefined' );
// }; 

this.clientJoin = function( moniker ) {

    function isValid( obj ) {
        var objType = ( {} ).toString.call( obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
        return ( objType != 'null' && objType != 'undefined' );
    };

    // mirrors the initial state of the toolbar
    if ( !isValid( this.drawing_clients ) ) {
        this.drawing_clients = {};
    }
    if ( this.drawing_clients[ moniker ] === undefined ) {
        this.drawing_clients[ moniker ] = {  
            "drawing_mode": 'none',
            "drawing_visible": 'inherit',
            "drawing_color": 'black',
            "drawing_width": 4,
            "drawing_parentPath": '/',
            "drawing_opacity": 1.0,
            "nameIndex": 1,
            "fontSize": 16,
            "angle": 30
        };
    }
    this.drawing_clients = this.drawing_clients;

};

this.clientLeave = function( moniker ) {
    if ( this.drawing_clients[ moniker ] !== undefined ) {
        delete this.drawing_clients[ moniker ]; 
        this.drawing_clients = this.drawing_clients;
    }
    if ( this.drawing_private[ moniker ] !== undefined ) {
        delete this.drawing_private[ moniker ]; 
    }
};

this.setUpPrivate = function( moniker ) {
    
    if ( this.drawing_private === undefined ) {
        this.drawing_private = {};
    }
    if ( this.drawing_private[ moniker ] === undefined ) {
        this.drawing_private[ moniker ] = {
            "drawingObject": null,
            "initialDownPoint": [ -1, -1 ],
            "previousPoint": [ -1, -1 ],
            "mouseDown": false
        };  
    }

};

this.setClientUIState = function( stateObj ) {

    function isValid( obj ) {
        var objType = ( {} ).toString.call( obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
        return ( objType != 'null' && objType != 'undefined' );
    };

    //console.info( "setClientUIState " + JSON.stringify( stateObj ) );
    if ( stateObj !== undefined ) {
        if ( !isValid( this.drawing_clients ) || 
             !isValid( this.drawing_clients[ this.client ] ) ) {
            this.clientJoin( this.client );
        } 
        var clients = this.drawing_clients;
        var userState = clients[ this.client ];
        for ( var property in stateObj ) {
            userState[ property ] = stateObj[ property ];       
        }
        this.drawing_clients = clients;
    }
};

this.down = function( eventData, nodeData, touch ) {

    function isValid( obj ) {
        var objType = ( {} ).toString.call( obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
        return ( objType != 'null' && objType != 'undefined' );
    };
    if ( !isValid( this.drawing_clients ) || 
         !isValid( this.drawing_clients[ this.client ] ) ) {
        this.clientJoin( this.client );
    } 
    if ( !isValid( this.drawing_private ) || 
         !isValid( this.drawing_private[ this.client ] ) ) {
        this.setUpPrivate( this.client );
    }

    var userState = this.drawing_clients[ this.client ];
    var privateState = this.drawing_private[ this.client ];
    var drawingMode = userState.drawing_mode;

    if ( privateState.drawingObject !== null || drawingMode === 'none' ) {
        return;
    }

    var compExtends = undefined;
    var groupExtends = undefined;
    var section = "/shapes";

    if ( drawingMode === "freeDraw" ) {
        section = "/lines";        
    }

    switch ( drawingMode ) {
        
        case "arc":
        case "circle":
        case "ellipse":
        case "regularPolygon":
        case "rect":
        case "ring":
        case "star":
        case "wedge":
            compExtends = [ "http://vwf.example.com/kinetic/", drawingMode, ".vwf" ].join(''); 
            break;

        case "sprite":            
        case "text":
        case "image":
            groupExtends = "http://vwf.example.com/kinetic/drawingGroup.vwf";
            compExtends = { 
                "border": "http://vwf.example.com/kinetic/line.vwf", 
                "content": [ "http://vwf.example.com/kinetic/", drawingMode, ".vwf" ].join('') 
            };
            break;

        case "arrow":
            groupExtends = "http://vwf.example.com/kinetic/drawingGroup.vwf";
            compExtends = { 
                "line": "http://vwf.example.com/kinetic/line.vwf", 
                "head": "http://vwf.example.com/kinetic/regularPolygon.vwf" 
            };
            break;

        case "thickArrow":
            groupExtends = "http://vwf.example.com/kinetic/drawingGroup.vwf";
            compExtends = { 
                "line": "http://vwf.example.com/kinetic/line.vwf", 
                "head": "http://vwf.example.com/kinetic/regularPolygon.vwf"
            };
            break;

        case "borderRect":
        case "line":
        case "freeDraw":
            compExtends = "http://vwf.example.com/kinetic/line.vwf";
            break;

        case 'none':
        default:
            break;

    }

    var getDefaultProperties = function( drawingMode, groupParent, eventPoint ) {
        var retObj = {
            "visible": 'inherit',
            "listening": 'inherit',
            "opacity": userState.drawing_opacity,
            "z-index": 4
        };

        switch( drawingMode ) {
            case "sprite":
            //case "text":
            case "image":
                retObj.opacity = 1.0;
                retObj.scaleOnLoad = true;
                break;

            default:
                retObj.fill = userState.drawing_color;
                break;
        }

        if ( groupParent ) {
            retObj.x = 0;
            retObj.y = 0;
            retObj.position = [ 0, 0 ];
        } else {
            retObj.x = eventPoint[ 0 ];
            retObj.y = eventPoint[ 1 ];
            retObj.position = eventPoint;
        }

        return retObj; 
    };

    var eventPointDown = eventData.stageRelative;
    if ( groupExtends !== undefined ) {

        privateState.initialDownPoint = eventPointDown;
        var parentPath = userState.drawing_parentPath + section ;
        var parents = this.find( parentPath );

        // find was failing 9/2/14, and the code below 
        // was a backup, going to leave this in until we feel good
        // about the issues we saw are no longer a problem        
        if ( parents === undefined ) {
            parents = [ this.findChild( this, parentPath.split( '/' ) ) ];
        }

        var parent = parents.length > 0 ? parents[ 0 ] : this;
        var groupDef = {
            "extends": groupExtends,
            "properties": {
                "visible": false,
                "listening": false,
                "position": eventPointDown                
            },
            "children": {}
        };

        for ( var def in compExtends ) {
            groupDef.children[ def ] = {
                "extends": compExtends[ def ],
                "properties": getDefaultProperties( drawingMode, true, eventPointDown )
            } 
        }

        var self = this;
        var selfMoniker = this.client;
        var name = drawingMode + this.drawing_index;
        this.drawing_index = this.drawing_index + 1;
        parent.children.create( name, groupDef, function( child ) {
            self.drawing_private[ selfMoniker ].drawingObject = child;
        } ); 

    } else if ( compExtends !== undefined ) {

        privateState.initialDownPoint = eventPointDown;
        var parentPath = userState.drawing_parentPath + section;
        var parents = this.find( parentPath );

        // find was failing 9/2/14, and the code below 
        // was a backup, going to leave this in until we feel good
        // about the issues we saw are no longer a problem
        if ( parents === undefined ) {
            parents = [ this.findChild( this, parentPath.split( '/' ) ) ];
        }
        
        var parent = parents.length > 0 ? parents[ 0 ] : this;
        var shapeDef = {
            "extends": compExtends,
            "properties": getDefaultProperties( drawingMode, false, eventPointDown )
        };

        var self = this;
        var selfMoniker = this.client;
        var name = drawingMode + this.drawing_index;
        this.drawing_index = this.drawing_index + 1;
        parent.children.create( name, shapeDef, function( child ) {
            self.drawing_private[ selfMoniker ].drawingObject = child;
        } );

    }
};

this.move = function( eventData, nodeData, touch ) {

    function isValid( obj ) {
        var objType = ( {} ).toString.call( obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
        return ( objType != 'null' && objType != 'undefined' );
    };

    if ( !isValid( this.drawing_clients ) || 
         !isValid( this.drawing_clients[ this.client ] ) ) {
        this.clientJoin( this.client );
    } 
    if ( this.drawing_private === undefined || 
         this.drawing_private[ this.client ] === undefined ) {
        this.setUpPrivate( this.client );
    }

    var userState = this.drawing_clients[ this.client ];
    if ( userState.drawing_mode === 'none' ) {
        return;
    }

    this.update( eventData, nodeData, false );
};

this.up = function( eventData, nodeData, touch ) {

    if ( this.drawing_private[ this.client ] !== undefined && 
         this.drawing_private[ this.client ].drawingObject !== null  ) {
        var drawingObject = this.drawing_private[ this.client ].drawingObject;
        this.update( eventData, nodeData, true );
        
        this.drawingObjectCreated( drawingObject.id );

        var userState = this.drawing_clients[ this.client ];
        if ( this.moniker === this.client ) {
             
            switch( userState.drawing_mode ) {
                
                case "text":
                    this.textCreated( drawingObject.content.id );
                    break;
                
                case "sprite":
                case "image":
                    this.imageCreated( drawingObject.content.id );
                    break;

            } 
        }

        switch( userState.drawing_mode ) {
            
            case "text":
            case "sprite":
            case "image":
                drawingObject.border.visible = false;
                break;

        } 

        this.drawing_private[ this.client ].drawingObject = null;
    }    
};

this.update = function( eventData, nodeData, upEvent ) {

    function isValid( obj ) {
        var objType = ( {} ).toString.call( obj ).match( /\s([a-zA-Z]+)/ )[ 1 ].toLowerCase();
        return ( objType != 'null' && objType != 'undefined' );
    };
    
    if ( this.drawing_private === undefined || 
         this.drawing_private[ this.client ] === undefined || 
         !isValid( this.drawing_clients ) ) {
        return;
    }

    if ( this.drawing_private[ this.client ].drawingObject !== null ) {
        
        var eventPoint = eventData.stageRelative;
        var userState = this.drawing_clients[ this.client ];        
        var privateState = this.drawing_private[ this.client ];
        var drawingObject = privateState.drawingObject;
        var pointAccepted = true;

        if ( drawingObject.visible !== userState.drawing_visible ) {
            drawingObject.visible = userState.drawing_visible;
        }
        if ( drawingObject.listening !== userState.drawing_listening ) {
            drawingObject.listening = userState.drawing_listening;
        }
        var diffX = eventPoint[ 0 ] - privateState.initialDownPoint[ 0 ];
        var diffY = eventPoint[ 1 ] - privateState.initialDownPoint[ 1 ];
        var pos = [ privateState.initialDownPoint[ 0 ], privateState.initialDownPoint[ 1 ] ];
        var width = diffX;  
        var height = diffY;
        var dist = Math.sqrt( ( diffX * diffX ) + ( diffY * diffY ) );

        //console.info( "== "+userState.drawing_mode +" ==" );
        //console.info( "== pos: " + pos + "   diffX: " + diffX + "   diffY: " + diffY );

        // this keeps the pos as the top left corner for the 
        // rectangular objects
        switch ( userState.drawing_mode ) {

            case "line":
            case "arrow":
            case "thickArrow":
            case "freeDraw":
                break;

            default:
                if ( diffX < 0 ) {
                    pos[ 0 ] += diffX;  
                    width = Math.abs( diffX );
                } 
                if ( diffY < 0 ) {
                    pos[ 1 ] += diffY;  
                    height = Math.abs( diffY );
                } 
                drawingObject.position = pos;
                drawingObject.width = width;
                drawingObject.height = height;  
                break;          
        }

        //console.info( "== pos: " + pos + "   diffX: " + diffX + "   diffY: " + diffY );

        switch ( userState.drawing_mode ) {
            
            case "arc":
                drawingObject.angle = userState.angle ? userState.angle : 30;
                if ( dist > this.drawing_width ) {
                    drawingObject.innerRadius = dist - this.drawing_width;
                    drawingObject.outerRadius = dist;
                }
                break;

            case "ellipse":         
                drawingObject.radius = { "x": width * 0.5, "y": height * 0.5 };
                break;

            case "circle":
                drawingObject.radius = dist;
                break;

            case "line":
                drawingObject.stroke = userState.drawing_color;
                drawingObject.strokeWidth = userState.drawing_width;
                drawingObject.points = [ 0, 0, diffX, diffY ];
                break;

            case "freeDraw":
                drawingObject.stroke = userState.drawing_color;
                drawingObject.strokeWidth = userState.drawing_width;

                var isFirstStrokeOfNewLine = ( drawingObject.points.length === 0 );
                var posX = eventPoint[ 0 ] - drawingObject.x;
                var posY = eventPoint[ 1 ] - drawingObject.y;
                
                if ( isFirstStrokeOfNewLine ) {
                    if ( Math.abs( posX ) + Math.abs( posY ) > 0 ) {
                        drawingObject.points = [ 0, 0, posX, posY ];
                    } else {
                        pointAccepted = false;   
                    }
                } else  {
                    var dragDiff = [ 
                        posX - privateState.previousPoint[ 0 ], 
                        posY - privateState.previousPoint[ 1 ] 
                    ];

                    if ( Math.abs( dragDiff[0] ) + Math.abs( dragDiff[1] ) > 0 ) {
                        drawingObject.points.push( posX );
                        drawingObject.points.push( posY );                        
                    } else {
                        pointAccepted = false;    
                    }
                }
                break;

            case "regularPolygon":
                 // needs defining
                 break;

            case "ring":
                if ( dist > userState.drawing_width ) {
                    drawingObject.innerRadius = dist - userState.drawing_width;
                    drawingObject.outerRadius = dist;
                }
                break;

            case "star":
                drawingObject.points = 5;
                drawingObject.innerRadius = dist * 80;
                drawingObject.outerRadius = dist;
                break;

            case "wedge":
                // needs defining
                drawingObject.angle = userState.angle ? userState.angle : 30;
                drawingObject.radius = dist;
                drawingObject.clockwise = false;
                break;

            case "borderRect":
                drawingObject.stroke = userState.drawing_color;
                drawingObject.strokeWidth = userState.drawing_width;
                drawingObject.points = [ 0, 0, width, 0, width, height, 0, height, 0, 0 ];
                break;

            case "arrow":
                drawingObject.x = drawingObject.position[ 0 ];
                drawingObject.y = drawingObject.position[ 1 ]; 

                drawingObject.line.stroke = userState.drawing_color;
                drawingObject.line.strokeWidth = userState.drawing_width;
                drawingObject.line.position = [ 0, 0 ];
                
                drawingObject.head.sides = 3;
                drawingObject.head.radius = userState.drawing_width * 3;

                var endPoint = goog.vec.Vec2.createFloat32FromValues( 0, 0 );
                var relativeXDiff = eventPoint[ 0 ] - drawingObject.x;
                var relativeYDiff = eventPoint[ 1 ] - drawingObject.y;
                var headOffset = ( userState.drawing_width * 3 ) * Math.sin( Math.PI / 6 );
                var dir = goog.vec.Vec2.createFloat32FromValues( relativeXDiff, relativeYDiff );
                var len = goog.vec.Vec2.distance( goog.vec.Vec2.createFloat32FromValues( 0, 0 ), dir );
                goog.vec.Vec2.normalize( dir, dir );

                drawingObject.head.rotation = Math.atan2( dir[1], dir[0] ) * ( 180 / Math.PI ) - 30;
                goog.vec.Vec2.scale( dir, len - ( userState.drawing_width * 3 ), endPoint );
                drawingObject.head.position = [ endPoint[0], endPoint[1] ];
                goog.vec.Vec2.scale( dir, len - ( ( userState.drawing_width * 3 ) + headOffset ), endPoint );
                drawingObject.line.points = [ 0, 0, endPoint[0], endPoint[1] ];
                break;
            
            case "thickArrow":
                drawingObject.x = drawingObject.position[ 0 ];
                drawingObject.y = drawingObject.position[ 1 ]; 

                drawingObject.line.stroke = userState.drawing_color;
                drawingObject.line.strokeWidth = userState.drawing_width * 8;
                drawingObject.line.position = [ 0, 0 ];
                
                drawingObject.head.sides = 3;
                drawingObject.head.radius = userState.drawing_width * 8;

                var endPoint = goog.vec.Vec2.createFloat32FromValues( 0, 0 );
                var relativeXDiff = eventPoint[ 0 ] - drawingObject.x;
                var relativeYDiff = eventPoint[ 1 ] - drawingObject.y;
                var headOffset = ( userState.drawing_width * 8 ) * Math.sin( Math.PI / 6 );
                var dir = goog.vec.Vec2.createFloat32FromValues( relativeXDiff, relativeYDiff );
                var len = goog.vec.Vec2.distance( goog.vec.Vec2.createFloat32FromValues( 0, 0 ), dir );
                goog.vec.Vec2.normalize( dir, dir );

                drawingObject.head.rotation = Math.atan2( dir[1], dir[0] ) * ( 180 / Math.PI ) - 30;
                goog.vec.Vec2.scale( dir, len - ( userState.drawing_width * 8 ), endPoint );
                drawingObject.head.position = [ endPoint[0], endPoint[1] ];
                goog.vec.Vec2.scale( dir, len - ( ( userState.drawing_width * 8 ) + headOffset ), endPoint );
                drawingObject.line.points = [ 0, 0, endPoint[0], endPoint[1] ];
                break; 

            case "sprite":
            case "image":
                drawingObject.border.stroke = userState.drawing_color;
                drawingObject.border.strokeWidth = 4;
                drawingObject.border.points = [ 0, 0, width, 0, width, height, 0, height, 0, 0 ];
                drawingObject.content.width = width;
                drawingObject.content.height = height;
                break;

            case "text":
                drawingObject.border.stroke = userState.drawing_color;
                drawingObject.border.strokeWidth = 4;
                drawingObject.border.points = [ 0, 0, width, 0, width, height, 0, height, 0, 0 ];
                drawingObject.content.fontSize = userState.fontSize ? userState.fontSize : 16;
                break;

            case "rect":
            default:
                break;

        }

        if ( pointAccepted ) {
            privateState.previousPoint = eventPoint;
        }

    }   
}; 

this.pointerDown = function( eventData, nodeData ) {
    this.down( eventData, nodeData, false );    
};

this.pointerMove = function( eventData, nodeData ) {
    this.move( eventData, nodeData, false );
};

this.pointerUp = function( eventData, nodeData ) {
    this.up( eventData, nodeData, false );
}; 

this.touchStart = function( eventData, nodeData ) {
    this.down( eventData, nodeData, true );    
};

this.touchMove = function( eventData, nodeData ) {
    this.move( eventData, nodeData, true );
};

this.touchEnd = function( eventData, nodeData ) {
    this.up( eventData, nodeData, true );
}; 

this.findChild = function( parent, names ) {
    if ( names.length > 0 ) {
        var childName = names.shift();
        while ( childName === "" ) {
            childName = names.shift();            
        }
        if ( parent.children[ childName ] ) {
            if ( names.length === 0 ) {
                return parent.children[ childName ];
            } else {
                return this.findChild( parent.children[ childName ], names );                
            }
        }
        else {
            return undefined;
        }
    }
    return undefined;
}

//@ sourceURL=kinetic_drawing.js