package {
    
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.PixelSnapping;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.filters.BlurFilter;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    
    import mx.core.BitmapAsset;

    [SWF(width=640,height=480,backgroundColor=0x000000,frameRate=60)]

    public class SnowApp extends Sprite {
        
        [Embed(source="saq.jpg")]
        private var imageClass:Class;
        
        private var _canvas:BitmapData;
        private var _glow:BitmapData;
        private var _snow:Array;
        
        private var _forceMap:BitmapData;
        private var _emitTemplate:BitmapData;
        private var _emitTemplateImage:BitmapData;
        
        public function SnowApp() {
            this._canvas = new BitmapData(640, 480, false, 0x0); // カンバスをつくる。ここに 1 pixel ずつ描いていくよ
            this.addChild(new Bitmap(this._canvas)) as  Bitmap;  // stage に配置
            
            this._glow = new BitmapData(640 / 4, 480 / 4, false, 0x0); // キラキラを描く用のん。カンバスの 4 分の 1 のサイズ
            var bm:Bitmap = this.addChild(new Bitmap(this._glow, PixelSnapping.NEVER, true)) as Bitmap; // smoothing を true にして配置
            bm.scaleX = bm.scaleY = 4; // 4 倍にする。
            bm.blendMode = BlendMode.ADD; // 加算モードで合成
            
            // 文字の形から、雪を発生させるための下準備。BitmapData に文字を描いておく。
            var tf:TextField = new TextField();
            tf.defaultTextFormat = new TextFormat('Verdana', 30, 0xffffff, true); 
            tf.autoSize = TextFieldAutoSize.LEFT;
            tf.text = 'TERACO';
            this._emitTemplate = new BitmapData(tf.textWidth, tf.textHeight, false, 0x0);
            this._emitTemplate.draw(tf);
            
            // 画像から雪?を発生させる用のん
            this._emitTemplateImage = BitmapAsset(new imageClass()).bitmapData;
            
            this._snow = []; // 雪パーティクルはここにいれておくよ。
            
            // 雪を積もらせるかたちを BitmapData に描く。
            this._forceMap = new BitmapData(640, 480, false, 0x0);
            this._forceMap.draw(tf, new Matrix(3, 0, 0, 3, 100, 200));
            this._forceMap.applyFilter(this._forceMap, this._forceMap.rect, new Point(0, 0), new BlurFilter(8, 8));
//            this.addChild(new Bitmap(this._forceMap)); // どんなのかみたかったらコメントアウト
            
            // したの 3 行からどれか 1 つコメントアウト
//            this.stage.addEventListener(MouseEvent.CLICK, this.emitFromRect); // ステージをクリックしたら四角から雪発生
//            this.stage.addEventListener(MouseEvent.CLICK, this.emitFromText); // ステージをクリックしたら文字から雪発生
            this.stage.addEventListener(MouseEvent.CLICK, this.emitFromImage); // ステージをクリックしたら画像から雪発生

            this.addEventListener(Event.ENTER_FRAME, this.update); // 毎フレーム update を呼ぶよ
        }
        
        // 四角から雪を発生させる関数
        public function emitFromRect(e:MouseEvent):void {
            for (var ey:Number = this.mouseY - 30; ey < this.mouseY + 30; ey += 3) { // mouthY の上下 30 pixel を 3 pixel ごとに
                for (var ex:Number = this.mouseX - 30; ex < this.mouseX + 30; ex += 3) { // mouthX の左右 30 pixel を 3 pixel ごとに
                    this.emitParticle(ex + Math.random() * 3, ey + Math.random() * 3, Math.random() + 0.5); // 雪発生。randam でちょっとだけ散らす
                }
            }
        }
        
        // 文字から雪を発生させる関数
        public function emitFromText(e:MouseEvent):void {
            var ofx:Number = this.mouseX - this._emitTemplate.width / 2;
            var ofy:Number = this.mouseY - this._emitTemplate.height / 2;
            for (var ey:Number = 0; ey < this._emitTemplate.height; ey += 2) { // _emitTemplate を 2 pixel ごとに縦に
                for (var ex:Number = 0; ex < this._emitTemplate.width; ex += 2) { // _emitTemplate を 2 pixel ごとに横に
                    if (this._emitTemplate.getPixel(ex, ey)) { // getPixel の結果、黒じゃなかったら(文字だったら)
                        this.emitParticle(ex + Math.random() * 2 + ofx, ey + Math.random() * 2 + ofy, Math.random() + 0.5); // 雪発生。randam でちょっとだけ散らす
                    }
                }
            } 
        }
        
        // 画像から雪を発生させる関数
        public function emitFromImage(e:MouseEvent):void {
            // あらかじめ計算できるやつは、ループのそとで計算しておきましょ
            var cx:Number = this._emitTemplateImage.width / 2;
            var cy:Number = this._emitTemplateImage.height / 2;
            var ofx:Number = this.mouseX - cx;
            var ofy:Number = this.mouseY - cy;
            var c:int;
            var a:Number;
            var v:Number;
            for (var ey:Number = 0; ey < this._emitTemplateImage.height; ey += 2) { // _emitTemplateImage を 2 pixel ごとに縦に
                for (var ex:Number = 0; ex < this._emitTemplateImage.width; ex += 2) { // _emitTemplateImage を 2 pixel ごとに横に
                    a = Math.atan2(ey - cy, ex - cy) + Math.random() * Math.PI * 0.1; // 中心から外に向かって飛ばしたいので、その角度を計算
                    v = Math.random() * 2 + 0.5; // 初期速度
                    this.emitParticle( // 雪発生
                        ex + Math.random() * 2 + ofx, ey + Math.random() * 2 + ofy,
                        Math.random() + 0.5,
                        this._emitTemplateImage.getPixel(ex, ey), // 雪?のいろを画像から拾ってくる
                        Math.cos(a) * v, Math.sin(a) * v); // 初期速度
                }
            } 
        }
        
        // 雪を 1 粒発生させる関数
        public function emitParticle(ex:Number, ey:Number, s:Number = 1, c:int = 0xffffff, vx:Number = 0, vy:Number = 0):void {
            var p:SnowParticle = new SnowParticle(); // 作って
            // パラメータ設定して
            p.x = ex;
            p.y = ey;
            p.vx = vx;
            p.vy = vy;
            p.s = s;
            p.c = c;
            this._snow.push(p); // 保存
        }
        
        // 雪を動かすよーー
        public function update(e:Event):void {
            this._canvas.lock(); // いっぱい setPixel するときは必ず lock しよう
            this._canvas.fillRect(this._canvas.rect, 0x0); // カンバスをクリア
            var n:int = this._snow.length;
            var d:Number;
            while (n--) {
                var p:SnowParticle = this._snow[n];
                p.vy += 0.02 * p.s; // まず重力を加える
                p.vx *= 0.99; // 空気抵抗
                p.vy *= 0.99; // y 方向にも
                d = 1 - (this._forceMap.getPixel(p.x, p.y) / 0xffffff) * 0.3; // forceMap にもとづいて抵抗値を計算。黒→速い、白→遅い。
                p.vx *= d; // forceMap から得た抵抗値を適用
                p.vy *= d; // y 方向にも
                p.x += p.vx; // 動かす
                p.y += p.vy;
                this._canvas.setPixel(p.x, p.y, p.c); // 雪 1 粒描く
                if (p.y > this.stage.stageHeight) { // もし画面外にでちゃったら
                    this._snow.splice(n, 1); // とりのぞく
                }
            }
            this._canvas.unlock(); // lock したやつは必ず unlock
            this._glow.draw(this._canvas, new Matrix(0.25, 0, 0, 0.25)); // キラキラを描く
            
            // ↓コメントアウトで常に雪降るよ
//            n = 10; while (n--)    this.emitParticle(Math.random() * this.stage.stageWidth, 0, Math.random() + 0.5);
        }
        
    }
    
}

class SnowParticle {
    
    public var x:Number;
    public var y:Number;
    public var vx:Number;
    public var vy:Number;
    public var s:Number;
    public var c:int;
    
    public function SnowParticle() {
        this.x = 0;
        this.y = 0;
        this.vx = 0;
        this.vy = 0;
        this.s = 1;
        this.c = 0xffffff;
    }
    
}