<?xml version="1.0" encoding="utf-8"?>
<!-- $Id: GameOfLife.mxml 57 2008-09-16 02:55:33Z danielr $ -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="setCurrentLandscape(createRandomLandscape());" viewSourceURL="srcview/index.html">
    <mx:Script>
        <![CDATA[
            import mx.controls.Image;

            [Embed("GameOfLife.pbj", mimeType="application/octet-stream")]
            private var gameOfLife:Class;

            private static const BORDER:uint = 0xFF0000;
            private static const DEAD:uint = 0xFFFFFF;
            private static const ALIVE:uint = 0x000000;
            private static const BORDER_THICKNESS:int = 1;
            private static const SCALE_FACTOR:Number = 4;
            
            private var landscapeWidth:int = 100;
            private var landscapeHeight:int = 100;
            private var landscapePoints:int = 1000;
            private var current:BitmapData;
            private var old:BitmapData;
            private var generationCount:int;

            [Bindable]
            private var running:Boolean;
            
            private function createEmptyLandscape():BitmapData
            {
                var bitmapData:BitmapData = new BitmapData(landscapeWidth + BORDER_THICKNESS * 2, landscapeHeight + BORDER_THICKNESS * 2, false, BORDER);
                bitmapData.lock();
                bitmapData.fillRect(new Rectangle(BORDER_THICKNESS, BORDER_THICKNESS, landscapeWidth, landscapeHeight), DEAD);
                bitmapData.unlock();
                return bitmapData;
            }
            
            private function createGosperGliderGun():BitmapData
            {
                var bitmapData:BitmapData = createEmptyLandscape();
                bitmapData.lock();
                var points:Array = [[0,4], [0,5],
                                    [1,4], [1,5],
                                    [10,4], [10,5], [10,6],
                                    [11,3], [11,7],
                                    [12,2], [12,8],
                                    [13,2], [13,8],
                                    [14,5],
                                    [15,3], [15,7],
                                    [16,4], [16,5], [16,6],
                                    [17,5],
                                    [20,2], [20,3], [20,4],
                                    [21,2], [21,3], [21,4],
                                    [22,1], [22,5],
                                    [24,0], [24,1], [24,5], [24,6],
                                    [34,2], [34,3],
                                    [35,2], [35,3]];
                for each (var point:Array in points)
                {
                    var childX:int = point[0];
                    var childY:int = point[1];
                    bitmapData.setPixel(childX + BORDER_THICKNESS, childY + BORDER_THICKNESS, ALIVE);
                }
                bitmapData.unlock();
                return bitmapData;
            }
            
            private function createRandomLandscape():BitmapData
            {
                var bitmapData:BitmapData = createEmptyLandscape();
                bitmapData.lock();
                for (var i:int = 0; i < landscapePoints; i++)
                {
                    var childX:int = int(Math.random() * landscapeWidth);
                    var childY:int = int(Math.random() * landscapeHeight);
                    bitmapData.setPixel(childX + BORDER_THICKNESS, childY + BORDER_THICKNESS, ALIVE);
                }
                bitmapData.unlock();
                return bitmapData;
            }
            
            private function setCurrentLandscape(landscape:BitmapData):void
            {
                generationCount = 0;
                
                current = landscape;
                currentDisplay.source = new Bitmap(current);
                currentLabel.text = "Generation " + generationCount;
                old = null 
                oldDisplay.source = null;
                oldLabel.text = "";
            }
            
            private function nextGeneration():void
            {
                if ((running) || (current == null))
                {
                    return;
                }
                running = true;
                var shader:Shader = new Shader(new gameOfLife() as ByteArray);
                shader.data.src.input = current;
                var result:BitmapData = new BitmapData(current.width, current.height);
                var shaderJob:ShaderJob = new ShaderJob(shader, result, current.width, current.height);
                shaderJob.addEventListener(ShaderEvent.COMPLETE, handleShaderDone);
                shaderJob.start();
            }
            
            private function handleShaderDone(shaderEvent:ShaderEvent):void
            {
                old = current;
                oldDisplay.source = new Bitmap(old);
                oldLabel.text = currentLabel.text;
                current = shaderEvent.bitmapData;
                currentDisplay.source = new Bitmap(current);
                generationCount++;
                currentLabel.text = "Generation " + generationCount;
                running = false;
                if (keepRunning.selected)
                {
                    nextGeneration();
                }
            }
            
            private function maybeStart():void
            {
                if (!running && keepRunning.selected)
                {
                    nextGeneration();
                }
            }
        ]]>
    </mx:Script>
    <mx:Style source="flekristal.css"/>
    <mx:HBox>
        <mx:Button label="Create Random" click="setCurrentLandscape(createRandomLandscape());"/>
        <mx:Button label="Gosper Glider Gun" click="setCurrentLandscape(createGosperGliderGun());"/>
        <mx:Button label="Next Generation" click="nextGeneration();" enabled="{!running}"/>
        <mx:CheckBox id="keepRunning" label="Continuous" change="maybeStart();"/>
    </mx:HBox>
    <mx:HBox>
        <mx:VBox>
            <mx:Label id="oldLabel"/>
            <mx:Image id="oldDisplay" scaleX="{SCALE_FACTOR}" scaleY="{SCALE_FACTOR}"/>
        </mx:VBox>
        <mx:VBox>
            <mx:Label id="currentLabel"/>
            <mx:Image id="currentDisplay" scaleX="{SCALE_FACTOR}" scaleY="{SCALE_FACTOR}"/>
        </mx:VBox>
    </mx:HBox>
</mx:Application>