    Type.registerNamespace("SilverlightFifteenPuzzle");
    
    SilverlightFifteenPuzzle.FifteenPuzzle = function(element)
    {
        SilverlightFifteenPuzzle.FifteenPuzzle.createProperty("width");
        SilverlightFifteenPuzzle.FifteenPuzzle.createProperty("height");
        SilverlightFifteenPuzzle.FifteenPuzzle.createProperty("xamlUrl");
        SilverlightFifteenPuzzle.FifteenPuzzle.createProperty("imageUrl");
        SilverlightFifteenPuzzle.FifteenPuzzle.createProperty("puzzleRenderMode");
        
        SilverlightFifteenPuzzle.FifteenPuzzle.initializeBase(this, [element]);
        
        this.PuzzleSize = 100;
        this.FontSize = 70;
        
        this.positions = new Array();
        
        for (var nRow=0;nRow<4;nRow++)
        {
            this.positions[nRow] = new Array();
            
            for (var nCol=0;nCol<4;nCol++)
            {
                this.positions[nRow][nCol] = ((nRow*4) + nCol + 1);
            }
        }
    }

    SilverlightFifteenPuzzle.FifteenPuzzle.prototype =
    {
        initialize : function()
        {
            SilverlightFifteenPuzzle.FifteenPuzzle.callBaseMethod(this, "initialize");
            
            this._renderControl();
        },
        
        dispose : function()
        {
            SilverlightFifteenPuzzle.FifteenPuzzle.callBaseMethod(this, "dispose");
        },
	    
	    _getContentElement : function()
	    {
	        return $get(String.format("{0}_Content", this.get_id()));
	    },
	    
	    _onXamlLoaded : function(plugIn, userContext, rootElement)
	    {
	        this._plugIn = plugIn;
	        this._rootElement = rootElement;
	        
	        this._renderNumbers();
	        this._drawShuffleButton();
	    },
	    
	    _getXPosition : function(nCol)
	    {
	        return (15 + (nCol*(this.PuzzleSize+1)))+2;
	    },
	    
	    _getYPosition : function(nRow)
	    {
	        return (65 + (nRow*(this.PuzzleSize+1)))+2;
	    },
	    
	    _drawShuffleButton : function()
	    {
	        var sb = new Sys.StringBuilder();

            sb.append(String.format('<Canvas Name="{0}_btnShuffle" Canvas.Left="140" Canvas.Top="520" Width="160" Height="45" Background="White" Cursor="Hand">', this.get_id()));
            sb.append('<Rectangle Fill="#80000000" Width="158" Height="43" Canvas.Top="2" Canvas.Left="2" RadiusX="15" RadiusY="15">');
            sb.append('</Rectangle>');
            sb.append('<Rectangle Width="160" Height="45" Canvas.Top="0" Canvas.Left="0" RadiusX="15" RadiusY="15">');
            sb.append('<Rectangle.Fill>');
            sb.append('<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">');
            sb.append('<GradientStop Offset="0" Color="Orange"/>');
            sb.append('<GradientStop Offset="0.6" Color="Red"/>');
            sb.append('</LinearGradientBrush>');
            sb.append('</Rectangle.Fill>');
            sb.append('</Rectangle>');
            sb.append('<Rectangle Width="158" Height="43" Canvas.Top="1" Canvas.Left="1" RadiusX="14" RadiusY="14">');
            sb.append('<Rectangle.Fill>');
            sb.append('<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">');
            sb.append('<GradientStop Offset="0" Color="#FFFFFFFF"/>');
            sb.append('<GradientStop Offset="1" Color="#00000000"/>');
            sb.append('</LinearGradientBrush>');
            sb.append('</Rectangle.Fill>');
            sb.append('</Rectangle>');
            sb.append('<TextBlock Canvas.Left="10" Canvas.Top="0" Canvas.ZIndex="1" Text="Shuffle" FontFamily="Verdana" FontSize="35" FontWeight="Bold"></TextBlock>');
            sb.append('</Canvas>');
            
            var buttonElement = this._plugIn.content.createFromXaml(sb.toString(), false);
            this._rootElement.children.add(buttonElement);
            
            buttonElement.AddEventListener("MouseLeftButtonDown", this._onShuffleClick)
	    },
	    
	    _drawNumber : function(nRow, nCol)
	    {
	        var n = ((nRow*4)+nCol+1);
	        
	        var sb = new Sys.StringBuilder();
	        
	        if (this.get_puzzleRenderMode()==SilverlightFifteenPuzzlePuzzleRenderMode.Number)
	        {
                sb.append(String.format('<Canvas Name="{0}_Cell_{1}" Canvas.Left="{2}" Canvas.Top="{3}" Canvas.ZIndex="3" Width="{4}" Height="{4}" Background="White" Cursor="Hand">', this.get_id(), n, this._getXPosition(nCol), this._getYPosition(nRow), this.PuzzleSize));
                sb.append('<Rectangle Fill="#80000000" Width="98" Height="98" Canvas.Top="2" Canvas.Left="2" RadiusX="15" RadiusY="15">');
                sb.append('</Rectangle>');
                sb.append('<Rectangle Width="100" Height="100" Canvas.Top="0" Canvas.Left="0" RadiusX="15" RadiusY="15">');
                sb.append('<Rectangle.Fill>');
                sb.append('<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">');
                sb.append('<GradientStop Offset="0" Color="Green"/>');
                sb.append('<GradientStop Offset="0.6" Color="Lime"/>');
                sb.append('</LinearGradientBrush>');
                sb.append('</Rectangle.Fill>');
                sb.append('</Rectangle>');
                sb.append('<Rectangle Width="98" Height="98" Canvas.Top="1" Canvas.Left="1" RadiusX="14" RadiusY="14">');
                sb.append('<Rectangle.Fill>');
                sb.append('<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">');
                sb.append('<GradientStop Offset="0" Color="#FFFFFFFF"/>');
                sb.append('<GradientStop Offset="1" Color="#00000000"/>');
                sb.append('</LinearGradientBrush>');
                sb.append('</Rectangle.Fill>');
                sb.append('</Rectangle>');
                sb.append(String.format('<TextBlock Name="{0}_TextBlock_{1}_{2}" Canvas.Left="25" Canvas.Top="0" Canvas.ZIndex="1" FontFamily="Verdana" FontSize="{3}" Text="{4}" FontWeight="Bold" Cursor="Hand" />', this.get_id(), nRow, nCol, this.FontSize, n));
                sb.append('</Canvas>');
            }
            else if (this.get_puzzleRenderMode()==SilverlightFifteenPuzzlePuzzleRenderMode.Image)
            {
                sb.append(String.format('<Image Name="{0}_Cell_{1}" Canvas.Left="{2}" Canvas.Top="{3}" Canvas.ZIndex="3" Width="{4}" Height="{4}" Cursor="Hand" Source="{5}">', this.get_id(), n, (17+nCol), (67+nRow), (this.PuzzleSize*4), this.get_imageUrl()));
                sb.append('<Image.Clip>');
                sb.append(String.format('<RectangleGeometry Rect="{0},{1},{2},{2}" RadiusX="15" RadiusY="15"></RectangleGeometry>', (nCol*this.PuzzleSize), (nRow*this.PuzzleSize), this.PuzzleSize));
                sb.append('</Image.Clip>');
                sb.append('<Image.RenderTransform>');
                sb.append(String.format('<TranslateTransform Name="{0}_Cell_{1}_Transform" X="0" Y="0" />', this.get_id(), n));
                sb.append('</Image.RenderTransform>');
                sb.append('</Image>');
            }
            
            Sys.Debug.trace(sb.toString());
            
            var numberElement = this._plugIn.content.createFromXaml(sb.toString(), false);
            this._rootElement.children.add(numberElement);
            
            var elementNumber = this._rootElement.findName(String.format("{0}_Cell_{1}", this.get_id(), n));
            
            if (this.get_puzzleRenderMode()==SilverlightFifteenPuzzlePuzzleRenderMode.Number)
            {
                var textBlockNumber = this._rootElement.findName(String.format("{0}_TextBlock_{1}_{2}", this.get_id(), nRow, nCol));
                
                textBlockNumber["Canvas.Left"] = ((this.PuzzleSize - textBlockNumber.ActualWidth) / 2).toString();
                textBlockNumber["Canvas.Top"] = ((this.PuzzleSize - textBlockNumber.ActualHeight) / 2).toString();
            }
            
            elementNumber.AddEventListener("MouseLeftButtonDown", this._onNumberClick)
	    },
	    
	    _getPuzzleNumber : function(nRow, nCol)
	    {
	        return this.positions[nRow][nCol];
	    },
	    
	    _setPuzzleNumber : function(nRow, nCol, number)
	    {
	        this.positions[nRow][nCol]=number;
	    },
	    
	    _getCellPosition : function(number)
	    {
	        var cellPosition = new Object();
	        
            for (var nRow=0;nRow<4;nRow++)
            {
                for (var nCol=0;nCol<4;nCol++)
                {
	                if (this.positions[nRow][nCol]==number)
	                {
	                    cellPosition.nRow = nRow;
	                    cellPosition.nCol = nCol;
	                    break;
	                }
	            }
	        }
	        
	        return cellPosition;
	    },
	    
	    _getNumberFromElement : function(elementNumber)
	    {
	        var values = elementNumber.Name.split('_');
	        return parseInt(values[values.length-1]);
	    },
	    
	    _getElementFromNumber : function(number)
	    {
	        return this._rootElement.findName(String.format("{0}_Cell_{1}", this.get_id(), number));
	    },
	    
	    _onShuffleClick : function(sender, mouseEventArgs)
	    {
	        var values = sender.Name.split('_');
	        var control = $find(values[0]);
	        
	        for (var t=0;t<300;t++)
	        {
	            control._nextStepShuffle();
	        }
	        
	        control._refreshNumbers();
	    },
	    
	    _refreshNumbers : function()
	    {
            for (var nRow=0;nRow<4;nRow++)
            {
                for (var nCol=0;nCol<4;nCol++)
                {
                    var number = this.positions[nRow][nCol];
                    
                    if (number<16)
                    {
                        var elementNumber = this._getElementFromNumber(number);
                        this._setPuzzlePosition(number, elementNumber, nRow, nCol);
                    }
	            }
	        }
	    },
	    
	    _nextStepShuffle : function()
	    {
            var end = false;
            
            while (!end)
            {
                var randomNumber=(Math.floor(Math.random()*15)+1);
                
                var cellPosition = this._getCellPosition(randomNumber);
                
                end = this._tryToMove(false, cellPosition.nRow, cellPosition.nCol);
            }
	    },
	    
	    _onNumberClick : function(sender, mouseEventArgs)
	    {
	        var values = sender.Name.split('_');
	        var control = $find(values[0]);
	        
	        var number = control._getNumberFromElement(sender);
	        var cellPosition = control._getCellPosition(number);
	        
	        control._tryToMove(true, cellPosition.nRow, cellPosition.nCol);
	    },
	    
	    _tryToMove : function(makeMove, nRow, nCol)
	    {
	        var number = this._getPuzzleNumber(nRow, nCol)
            var numberElement = this._getElementFromNumber(number);
            
	        var nRowMove=-1;
	        var nColMove=-1;
	        
	        if (nRow==3)
	        {
	            var aboveNumber = this._getPuzzleNumber(nRow-1, nCol);
	            if (aboveNumber==16)
	            {
	                nRowMove=nRow-1;
	                nColMove=nCol;
	            }
	        }
	        else if (nRow==0)
	        {
	            var belowNumber = this._getPuzzleNumber(nRow+1, nCol);
	            if (belowNumber==16)
	            {
	                nRowMove=nRow+1;
	                nColMove=nCol;
	            }
	        }
	        else
	        {
	            var aboveNumber = this._getPuzzleNumber(nRow-1, nCol);
	            var belowNumber = this._getPuzzleNumber(nRow+1, nCol);

	            if (aboveNumber==16)
	            {
	                nRowMove=nRow-1;
	                nColMove=nCol;
	            }
	            
	            if (belowNumber==16)
	            {
	                nRowMove=nRow+1;
	                nColMove=nCol;
	            }
	        }
	        
	        if (nCol==3)
	        {
	            var leftNumber = this._getPuzzleNumber(nRow, nCol-1);
	            if (leftNumber==16)
	            {
	                nRowMove=nRow;
	                nColMove=nCol-1;
	            }
	        }
	        else if (nCol==0)
	        {
	            var rightNumber = this._getPuzzleNumber(nRow, nCol+1);
	            if (rightNumber==16)
	            {
	                nRowMove=nRow;
	                nColMove=nCol+1;
	            }
	        }
	        else
	        {
	            var leftNumber = this._getPuzzleNumber(nRow, nCol-1);
	            var rightNumber = this._getPuzzleNumber(nRow, nCol+1);

	            if (leftNumber==16)
	            {
	                nRowMove=nRow;
	                nColMove=nCol-1;
	            }

	            if (rightNumber==16)
	            {
	                nRowMove=nRow;
	                nColMove=nCol+1;
	            }
	        }
	        
	        if (nRowMove!=-1 && nColMove!=-1)
	        {
                this._movePuzzleTo(makeMove, numberElement, nRowMove, nColMove);
	                
	            return true;
	        }
	        else
	        {
	            return false;
	        }
	    },
	    
	    _setPuzzlePosition : function(number, elementNumber, toNRow, toNCol)
	    {
            if (this.get_puzzleRenderMode()==SilverlightFifteenPuzzlePuzzleRenderMode.Number)
            {
                elementNumber["Canvas.Left"] = this._getXPosition(toNCol);
                elementNumber["Canvas.Top"] = this._getYPosition(toNRow);
            }
            else
            {
                var nCol = (number-1)%4;
                var nRow = (number-1-nCol)/4;
                
                var transformElement = this._rootElement.findName(String.format("{0}_Cell_{1}_Transform", this.get_id(), number));
                transformElement["X"] = ((toNCol-nCol)*(this.PuzzleSize+1));
                transformElement["Y"] = ((toNRow-nRow)*(this.PuzzleSize+1));
            }
	    },
	    
	    _movePuzzleTo : function(makeMove, elementNumber, toNRow, toNCol)
	    {
            var number = this._getNumberFromElement(elementNumber);
	        var cellPosition = this._getCellPosition(number);
	        
	        if (makeMove)
	            this._setPuzzlePosition(number, elementNumber, toNRow, toNCol);
	        
            this._setPuzzleNumber(toNRow, toNCol, number);
            this._setPuzzleNumber(cellPosition.nRow, cellPosition.nCol, 16);
            
            if (makeMove)
                this._checkGameCompleted();
	    },
	    
	    _checkGameCompleted : function()
	    {
	        var n = 1;

            for (var nRow=0;nRow<4;nRow++)
            {
                for (var nCol=0;nCol<4;nCol++)
                {
	                if (this.positions[nRow][nCol]!=n)
	                {
	                    return;
	                }
	                
	                n++;
	            }
	        }
	        
            alert("Game completed!");
	    },
	    
	    _renderNumbers : function()
	    {
	        for (var nRow=0;nRow<4;nRow++)
	        {
	            for (var nCol=0;nCol<4;nCol++)
	            {
	                if (((nRow*4)+nCol+1)<=15)
	                {
                        this._drawNumber(nRow, nCol);
                    }
                }
	        }
	    },
	    
        _renderControl : function()
        {
            this.get_element().innerHTML = String.format("<div id='{0}_Content' style='width:{1};height:{2}'></div>", this.get_id(), this.get_width(), this.get_height());
            
            var hostId = String.format("{0}_Host", this.get_id());
            var bounds = Sys.UI.DomElement.getBounds(this._getContentElement());
            
            Silverlight.createObject(this.get_xamlUrl(), this._getContentElement(), hostId, 
            { width:bounds.width.toString(), height:bounds.height.toString(), version:'1.0' },
            { onError:null, onLoad:Function.createDelegate(this, this._onXamlLoaded) }, 
            null);
        }
    };
    
    SilverlightFifteenPuzzle.FifteenPuzzle.registerClass("SilverlightFifteenPuzzle.FifteenPuzzle", Sys.UI.Control);
    
    SilverlightFifteenPuzzlePuzzleRenderMode = function(){};
    SilverlightFifteenPuzzlePuzzleRenderMode.prototype = 
    {
        Number : 0,
        Image : 1
    }
    
    SilverlightFifteenPuzzlePuzzleRenderMode.registerEnum("SilverlightFifteenPuzzlePuzzleRenderMode");    
    
    if (typeof (Sys) != "undefined")
    {    
        Sys.Application.notifyScriptLoaded();    
    }
