package
{
    import away3dlite.cameras.HoverCamera3D;
    import away3dlite.containers.Scene3D;
    import away3dlite.containers.View3D;
    import away3dlite.core.utils.Cast;
    import away3dlite.debug.AwayStats;
    import away3dlite.materials.BitmapMaterial;
    import away3dlite.materials.ColorMaterial;
    import away3dlite.materials.MovieMaterial;
    import away3dlite.materials.WireColorMaterial;
    import away3dlite.primitives.Cube6;
    
    import be.boulevart.google.GoogleApi;
    import be.boulevart.google.ajaxapi.search.GoogleSearchResult;
    import be.boulevart.google.ajaxapi.search.images.GoogleImageSearch;
    import be.boulevart.google.ajaxapi.search.images.data.GoogleImage;
    import be.boulevart.google.ajaxapi.search.images.data.types.*;
    import be.boulevart.google.ajaxapi.search.web.data.GoogleWebItem;
    import be.boulevart.google.apicore.GoogleApiKeyStore;
    import be.boulevart.google.events.GoogleApiEvent;
    
    import com.adobe.viewsource.ViewSource;
    
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import flash.display.StageQuality;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.MouseEvent;
    import flash.events.ProgressEvent;
    import flash.filters.BevelFilter;
    import flash.filters.DropShadowFilter;
    import flash.filters.GlowFilter;
    import flash.geom.Matrix;
    import flash.geom.Vector3D;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.media.SoundMixer;
    import flash.net.URLRequest;
    import flash.net.URLVariables;
    import flash.net.URLRequestMethod;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.utils.ByteArray;
    
    [SWF("30", width="800", height="600", backgroundColor="0x000000")]
    public class cash extends Sprite
    {    
        private const HEIGHT_MULTIPLIER:Number = 60000;
        private const GRID_ROWS_AND_COLS:uint = 7;
        private const STATE_MAN_IN_BLACK:uint = 0;
        private const STATE_TO_THE_LIGHT:uint = 1;
        private const STATE_RING_OF_FIRE:uint = 2;
        private const STATE_NO_BLEND:uint = 3;
        private var _state:uint = STATE_MAN_IN_BLACK;
        
        [Embed("black2.jpg")] private var blackImage:Class;
        
        // away3dlite vars
        private var _view:View3D;
        private var _camera:HoverCamera3D;
        private var _cubes:Vector.<Cube6> = new Vector.<Cube6>();
        private var _fireMaterial:MovieMaterial;
        private var _blackMaterial:BitmapMaterial;
        private var _guitar:GuitarModel;
        
        // 2d elements
        private var _mibText:TextField;
        private var _pctText:TextField;
        private var _pics:Vector.<Bitmap> = new Vector.<Bitmap>();
        private var _picCtr:uint = 0;
        
        // sound handling vars
        private var _mp3:Sound;
        private var _channel:SoundChannel;
        private var _soundBytes:ByteArray = new ByteArray();
        private var _totals:Vector.<Number> = new Vector.<Number>(GRID_ROWS_AND_COLS*GRID_ROWS_AND_COLS, true);
        private var _lastValue:Number = 0;
        
        public function cash()
        {
            ViewSource.addMenuItem(this, "srcview/index.html");
            if (stage) {
                _init();
            } else {
                this.addEventListener(Event.ADDED_TO_STAGE, function(e:Event):void { _init(); } );
            }    
        }
        
        private function _init():void {
            var bg:Sprite = new Sprite();
            var m:Matrix = new Matrix();
            var i:uint = 0;
            var dropShadow:DropShadowFilter = new DropShadowFilter(3,45,0x888888);
            var plainTextFormat:TextFormat = new TextFormat("arial", "14", 0xffffff, true);
            
            // draw background gradient
            stage.quality = StageQuality.LOW;
            m.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI/2);
            bg.graphics.beginGradientFill("linear", [0x000000,0x888888,0x000000],[1,1,1],[55,127,200],m);
            bg.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);
            bg.graphics.endFill();
            this.addChild(bg);
            
            // create materials
            _fireMaterial = new MovieMaterial(_createFireSprite());
            _blackMaterial = new BitmapMaterial(Cast.bitmap(blackImage));
            
            // create away3dlite view
            _camera = new HoverCamera3D();
            _camera.tiltAngle = 15;
            _camera.distance = 1500;
            
            _view = new View3D(new Scene3D(), _camera);
            _view.x = stage.stageWidth/2;
            _view.y = stage.stageHeight/2;
            this.addChild(_view);
            
            // setup guitar model
            _guitar = new GuitarModel(5);
            _guitar.x = -50;
            _guitar.y = -80;
            _guitar.z = -65;
            _guitar.rotationX = 90;
            _guitar.rotationY = 90;
            _guitar.visible = false;
            _view.scene.addChild(_guitar);
            
            // draw the initial cube setup
            _createCubes();
            
            // add stats
            this.addChild(new AwayStats());
            
            // add artist, song, album
            var text:TextField = new TextField();
            text.text = "Johnny Cash\r\"God's Gonna Cut You Down\"\rAmerican V: A Hundred Highways";
            text.setTextFormat(plainTextFormat);
            text.width = text.textWidth*1.3;
            text.x = 10;
            text.y = stage.stageHeight-80;
            text.filters = [dropShadow];
            this.addChild(text);
            
            // add theme text
            _mibText = new TextField();
            _mibText.text = "Theme: \"Man in Black\"";
            _mibText.setTextFormat(plainTextFormat);
            _mibText.width = 300;
            _mibText.x = stage.stageWidth-300;
            _mibText.y = stage.stageHeight-80;
            _mibText.filters = [dropShadow];
            this.addChild(_mibText);
            
            // add click instructions
            var clickText:TextField = new TextField();
            clickText.text = "(click on the stage to change)";
            clickText.setTextFormat(plainTextFormat);
            clickText.width = 300;
            clickText.x = stage.stageWidth-300;
            clickText.y = stage.stageHeight-60;
            this.addChild(clickText);
            
            // add click instructions
            _pctText = new TextField();
            _updatePctText(0);
            this.addChild(_pctText);
            
            // start loading mp3
            _mp3 = new Sound();
            _mp3.load(new URLRequest("mp3/cash.mp3"));
            _mp3.addEventListener(ProgressEvent.PROGRESS, _onSoundProgress);
            _mp3.addEventListener(Event.COMPLETE, _onSoundComplete);
            
            // search for and retrieve images from Google Image
            GoogleApiKeyStore.setAPIKey("ABQIAAAA-zGCprH_thF_6IgLmv1kqRTJ53Sd-_5vdPT4XSu8ToAtvm3yoRSriaWgYbD2d3O7rIA0dLJZ-DqQ9w");
            var gis:GoogleImageSearch = new GoogleImageSearch();
            gis.addEventListener(GoogleApiEvent.IMAGE_SEARCH_RESULT, _onResult);
            for (i = 0; i < 7; i++) {
                gis.search("johnny cash", i*8+1, GoogleImageSafeMode.MODERATE, GoogleImageSize.MEDIUM);
            }
            
            this.addEventListener(Event.ENTER_FRAME, _onEnterFrame);
            stage.addEventListener(MouseEvent.CLICK, _onMouseClick);
            this.useHandCursor = true;
            this.buttonMode = true;
        }
        
        private function _updatePctText(pct:uint):void {
            _pctText.text = "Loading: " + String(pct) + "%";
            _pctText.setTextFormat(new TextFormat("arial", "48", 0xffffff, true));
            _pctText.width = stage.stageWidth;
            _pctText.x = stage.stageWidth/2 - _pctText.textWidth/2;
            _pctText.y = stage.stageHeight/2 - _pctText.textHeight/2;
        }
        
        private function _onSoundProgress(e:ProgressEvent):void {
            var pct:Number = Math.floor((Number(e.bytesLoaded) / Number(e.bytesTotal)) * 100);
            _updatePctText(pct);
        }
        
        private function _onSoundComplete(e:Event):void {
            _pctText.visible = false;
            _channel = new SoundChannel();
            _channel = _mp3.play();    
        }
        
        private function _onResult(e:GoogleApiEvent):void {
            var res:GoogleSearchResult = e.data as GoogleSearchResult;
            
            for (var i:uint = 0; i < res.results.length; i++) {
                var k:GoogleImage = res.results[i] as GoogleImage;
                var loader:Loader = new Loader();
                var req:URLRequest = new URLRequest("http://savagelook.com/imageproxy.php");
                var vars:URLVariables = new URLVariables();
                
                vars.url = k.thumbUrl;
                req.data = vars;
                req.method = URLRequestMethod.POST;
                loader.load(req);
                loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onComplete);
                loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, _onError);
            }
        }
        
        private function _onComplete(e:Event):void {
            var bm:Bitmap = new Bitmap(((e.target as LoaderInfo).content as Bitmap).bitmapData);
            bm.visible = false;
            bm.filters = [new BevelFilter()];
            this.addChild(bm);
            _pics.push(bm);
        }
        
        private function _onError(e:IOErrorEvent):void {
            trace(e);
        }
        
        private function _createFireSprite():Sprite {
            var sprite:Sprite = new Sprite();
            var matrix:Matrix = new Matrix();
            
            matrix.createGradientBox(256,256, Math.PI/2);
            sprite.graphics.beginGradientFill("linear", [0xffff00,0xff6600], [1,1], [0,255], matrix);
            sprite.graphics.drawRect(0,0,256,256);
            sprite.graphics.endFill();
            
            return sprite;
        }
        
        private function _createCubes():void {
            var cube:Cube6;
            var i:uint = 0;
            
            // remove all cubes and layers
            for (i = 0; i < _cubes.length; i++) { 
                cube = _cubes[i];
                if (cube.layer != null) {
                    _view.removeChild(cube.layer);
                }
                _view.scene.removeChild(cube); 
            }
            _cubes.length = 0;
            
            // show guitar model 
            if (_state == STATE_RING_OF_FIRE) {
                _guitar.visible = true;
            } else {
                _guitar.visible = false;
            }
            
            // create our grid of cubes
            for (i = 0; i < GRID_ROWS_AND_COLS; i++) {
                for (var j:uint = 0; j < GRID_ROWS_AND_COLS; j++) {
                    cube = new Cube6(new WireColorMaterial(0x333333,1,0xffffff), 100, 100, 100);
                    cube.x = -(GRID_ROWS_AND_COLS*120/2) + (i*120);
                    cube.y = 0;
                    cube.z = -(GRID_ROWS_AND_COLS*120/2) + (j*120);
                    cube.mouseEnabled = false;
                    cube.visible = true;
                    
                    switch (_state) {
                        case STATE_NO_BLEND:
                            // do nothing
                            break;
                        case STATE_MAN_IN_BLACK:
                            cube.material = _blackMaterial;
                            break;
                        case STATE_TO_THE_LIGHT:
                            cube.material = new ColorMaterial(0x333333);
                            cube.layer = new Sprite();
                            cube.layer.blendMode = BlendMode.ADD;
                            cube.layer.filters = [new GlowFilter(0xffffff,0.2)];
                            _view.addChild(cube.layer);
                            
                            if ((i+j)%2 == 1) {
                                cube.visible = false;
                                cube.layer.visible = false;
                            }
                            break;
                        case STATE_RING_OF_FIRE:
                            cube.material = _fireMaterial;
                            if (i == 0 || i == GRID_ROWS_AND_COLS-1 || j == 0 || j == GRID_ROWS_AND_COLS-1) {
                                cube.material = _fireMaterial;
                            } else if (i == 1 || i == GRID_ROWS_AND_COLS-2 || j == 1 || j == GRID_ROWS_AND_COLS-2) {
                                cube.visible = false;
                            } else {
                                cube.material = _blackMaterial;
                            }
                            break;
                    }
                    
                    _view.scene.addChild(cube);
                    _cubes.push(cube);
                }
            }
        }
        
        private function _onMouseClick(e:MouseEvent):void {
            _state = _state == 3 ? 0 : _state + 1;
            _mibText.text = "Theme: ";
            switch(_state) {
                case STATE_NO_BLEND:
                    _mibText.appendText("\"Themeless\"");
                    break;
                case STATE_MAN_IN_BLACK:
                    _mibText.appendText("\"Man in Black\"");
                    break;
                case STATE_TO_THE_LIGHT:
                    _mibText.appendText("\"Brought to the Light\"");
                    break;
                case STATE_RING_OF_FIRE:
                    _mibText.appendText("\"Ring of Fire\"");
                    break;
            }
            _mibText.setTextFormat(new TextFormat("arial", "14", 0xffffff, true));
            _createCubes();
        }
        
        private function _onEnterFrame(e:Event):void {
            var bm:Bitmap;
            var i:uint = 0, j:uint = 0;
            var ctr:uint = 0;
            var cube:Cube6;
            var newHeight:Number;
            var total:Number = 0;
            var avg:Number = 0;
            
            // move camera
            _camera.hover();
            _camera.panAngle += 0.3;
            
            // create total values from SoundMixer
            SoundMixer.computeSpectrum(_soundBytes, true);
            for (i = 0; i < _totals.length; i++) { _totals[i] = 0; }
            for (i = 0; i < Math.floor(_soundBytes.length/4); i = i + 4) {
                _totals[ctr] += _soundBytes.readFloat();
                if (ctr == _cubes.length-1) {
                    ctr = 0;
                } else {
                    ctr++;
                }
            }
            
            
            if (_state == STATE_RING_OF_FIRE) {
                _totals = _totals.sort(function compare(x:Number, y:Number):Number { 
                    if (x>y) { 
                        return -1; 
                    } else if (x<y) {
                        return 1;
                    } else {
                        return 0;
                    }
                });
                
                ctr = 0;
                for (i = 0; i < GRID_ROWS_AND_COLS; i++) {
                    for (j = 0; j < GRID_ROWS_AND_COLS; j++) {
                        cube = _cubes[i*GRID_ROWS_AND_COLS+j];
                        if (i == 0 || i == GRID_ROWS_AND_COLS-1 || j == 0 || j == GRID_ROWS_AND_COLS-1) {
                            newHeight = (_totals[ctr++]/4/_cubes.length) * HEIGHT_MULTIPLIER;
                            if (newHeight > cube.height) {
                                cube.height = newHeight;
                            } else if (cube.height <= 0) {
                                cube.height = 0;
                            } else {
                                cube.height -= 12;
                            }
                            cube.y = -cube.height/2;
                        } 
                    }
                }
            } else {
                for (i = 0; i < _cubes.length; i++) {
                    cube = _cubes[i];
                    newHeight = (_totals[i]/4/_cubes.length) * HEIGHT_MULTIPLIER;
                    if (newHeight > cube.height) {
                        cube.height = newHeight;
                    } else if (cube.height <= 0) {
                        cube.height = 0;
                    } else {
                        cube.height -= 12;
                    }
                    cube.y = -cube.height/2;
                }
            }
            
            // get total and average of SoundMixer values
            for (i = 0; i < _totals.length; i++) {
                total += _totals[i];
            }
            avg = total/_totals.length;
            
            // show image if sound threshold and criteria are met
            if (avg > 0.5 && _lastValue < avg && _pics.length > 0) {
                _picCtr++;
                if (_picCtr >= _pics.length) {
                    _picCtr = 0;
                }
                
                bm = _pics[_picCtr] as Bitmap;
                bm.visible = true;
                bm.alpha = 1;
                bm.scaleX = 1;
                bm.scaleY = 1;
                bm.x = Math.random() * (stage.stageWidth-bm.width);
                bm.y = Math.random() * (stage.stageHeight-bm.height-100);
            }
            _lastValue = avg;
            
            // fade, scale, and possibly remove visibility of images
            for (i = 0; i < _pics.length; i++) {
                bm = _pics[i] as Bitmap;
                bm.alpha -= 0.005;
                bm.scaleX = bm.scaleY = bm.alpha;
                if (bm.alpha <= 0) {
                    bm.alpha = 0;
                    bm.visible = false;
                }
            }
            
            // render scene
            _view.render();
        }
    }
}