package {
    
    import de.popforge.audio.output.Audio;
    import de.popforge.audio.output.AudioBuffer;
    import de.popforge.audio.output.Sample;
    import de.popforge.gui.Slider;
    import de.popforge.parameter.MappingNumberExponential;
    import de.popforge.parameter.MappingNumberLinear;
    import de.popforge.parameter.Parameter;
    import de.popforge.utils.Formatter;
    
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.PixelSnapping;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.filters.BitmapFilter;
    import flash.filters.BitmapFilterQuality;
    import flash.filters.BlurFilter;
    import flash.filters.ColorMatrixFilter;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.media.SoundMixer;
    import flash.utils.ByteArray;
    
    [SWF(width=256,height=256,frameRate=60, backgroundColor=0)]
    
    public class SpectrumRange extends Sprite {
        
        private static const ZERO_POINT:Point = new Point(0, 0);
        
        private var _paramFreq:Parameter;
        private var _phase:Number;
        private var _frequency:Number;
        private var _slider:Slider;
        private var _spectrum:Bitmap;
        private var _spectrumBitmap:BitmapData;
        private var _data:ByteArray;
        private var _filters:Array;
        
        public function SpectrumRange() {
            
            this.stage.scaleMode = StageScaleMode.NO_SCALE;
            this.stage.align = StageAlign.TOP_LEFT;
            
            this._spectrumBitmap = new BitmapData(256, 256, false, 0);
            this._spectrum = this.addChild(new Bitmap(this._spectrumBitmap, PixelSnapping.NEVER, false)) as Bitmap;
            
            this._filters = [
                new BlurFilter(4, 4, BitmapFilterQuality.LOW),
                new ColorMatrixFilter([
                    0.995, 0, 0, 0, 0,
                    0, 0.75, 0, 0, 0,
                    0, 0, 0.99, 0, 0,
                    0, 0, 0, 0, 0
                ])
            ];
            
            this._data = new ByteArray();
            
            var buffer: AudioBuffer = new AudioBuffer(4, Audio.STEREO, Audio.BIT16, Audio.RATE44100);
            buffer.onInit = onAudioBufferInit;
            buffer.onComplete = onAudioBufferComplete;
            
            //this._paramFreq = new Parameter(new MappingNumberExponential(110, 11050), this._frequency = 440);
            this._paramFreq = new Parameter(new MappingNumberLinear(110, 11008), this._frequency = 440);

            this._slider = this.addChild(new Slider(this._paramFreq, Math.min(500, this.stage.stageWidth * 0.8))) as Slider;

            this._phase = 0;

            this.stage.addEventListener(Event.RESIZE, this._handleResize);
            this._handleResize();
        }
        
        private function _handleResize(e:Event = null):void {
            this._spectrum.width = this.stage.stageWidth;
            this._spectrum.height = this.stage.stageHeight;
            this._slider.x = (stage.stageWidth - this._slider.width) >> 1;
            this._slider.y = (stage.stageHeight - 30);
        }
        
        private function onAudioBufferInit(buffer:AudioBuffer):void {
            buffer.start();
            this.addEventListener(Event.ENTER_FRAME, this.renderSpectrum);
        }
        
        private function onAudioBufferComplete(buffer:AudioBuffer):void {
            var samples:Array = buffer.getSamples();
            var sample:Sample;
            var amplitude:Number;
            
            for (var i:int = 0 ; i < samples.length ; i++) {
                sample = samples[i];
                amplitude = Math.sin(this._phase * Math.PI * 2);
                //sample.left = sample.right = amplitude;  // Sine
                sample.left = sample.right = amplitude < 0 ? -1 : 1;  // Square
                this._frequency += (this._paramFreq.getValue() - this._frequency) * .0001;
                this._phase += this._frequency / Audio.RATE44100;
            }

            buffer.update();
        }
        
        private function renderSpectrum(e:Event = null):void {
            try {
                SoundMixer.computeSpectrum(this._data, true);
                this._spectrumBitmap.lock();
                for each (var fil:BitmapFilter in this._filters) {
                    this._spectrumBitmap.applyFilter(this._spectrumBitmap, this._spectrumBitmap.rect, ZERO_POINT, fil);
                }
                var rect:Rectangle = new Rectangle(0, 0, 1, 0);
                for (var i:Number = 0; i < 256; i++) {
                    rect.x = i;
                    rect.width = 1;
                    rect.height = this._data.readFloat() * 128;
                    rect.y = 256 - rect.height;
                    this._spectrumBitmap.fillRect(rect, 0x80ffffff);
                }
                this._spectrumBitmap.unlock();
                
            } catch (e:Error) {
                trace(e);
            }
        }
        
    }
    
}