package {
	import adobe.utils.CustomActions;
	import com.bit101.components.HBox;
	import com.bit101.components.Label;
	import com.bit101.components.PushButton;
	import com.bit101.components.VBox;
	import com.bit101.components.Window;
	import com.bit101.components.InputSlider;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.display.Stage3D;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.net.FileReference;
	import flash.ui.Keyboard;
	import flash.utils.ByteArray;
	import org.gif.encoder.GIFEncoder;
	import flash.utils.getTimer;
	import com.netgrind.core.*;
	import com.netgrind.*
	
	/**
	 * ...
	 * @author netgrind
	 */
	public class GifCapture extends Sprite {
		private var h:InputSlider;
		private var w:InputSlider;
		private var rec:PushButton;
		private var previewWindow:ModalWindow;
		private var recordFrames:InputSlider;
		private var qualitySlider:InputSlider;
		private var info:Label;
		private var frameTime:int;
		private var rendering:Boolean;
		
		public static var openKey:int = Keyboard.T;
		public static var recordKey:int = Keyboard.R;
		public var frame:Sprite;
		public var size:Rectangle;
		public var lastMouse:Point = new Point();
		public var window:Window;
		public var box:VBox;
		public var preview:Bitmap;
		public var recording:Boolean = false;
		public var record:Array = [];
		public var encoder:GIFEncoder = new GIFEncoder();
		public var frames:int = 0;
		//public var view:View3D;
		public var b3d:BitmapData;
		
		public function GifCapture():void {
			Key.bindFunctionToKeyPress(showHide, openKey);
			Key.bindFunctionToKeyPress(onRecord, recordKey);
			size = new Rectangle(0,0, 500, 500);
			frame = new Sprite();
			addChild(frame);
			window = new Window(this, 0, 0, 'GIF CAPTURE');
			box = new VBox(window, 10, 10);
			w = new InputSlider(box, 0, 0, 'width ', changeWidth);
			w.slider.maximum = 1000;
			h = new InputSlider(box,0,0,'height', changeHeight);
			h.slider.maximum = 1000;
			rec = new PushButton(box, 0, 0, 'RECORD', onRecord)
			recordFrames = new InputSlider(box, 0, 0, 'record for');
			recordFrames.slider.minimum = 0;
			recordFrames.value = 0;
			recordFrames.slider.maximum = 1000;
			preview = new Bitmap(new BitmapData(50,50));
			
			window.width = 250;
			window.height = 130;
			visible = false;
			addEventListener(Event.ADDED_TO_STAGE, init)
		}
		
		public function onRecord(e:*= null):void {
			if (!visible) {
				return;
			}
			recording = !recording;
			if(recording){
				for (var i:int = 0; i < record.length; i++) {
					BitmapData(record[i]).dispose();
					record[i] = null;
				}
				record = null;
				record = [];
				rec.label = 'STOP';
				/*if (view){
					if (b3d) b3d.dispose();
					b3d = new BitmapData(stage.stageWidth,stage.stageHeight,true,0);
					view.renderer.queueSnapshot(b3d);
				}*/
				Scheduler.invokeIn(function():void{Scheduler.invokeEvery(capture);})
			}else {
				Scheduler.cancel(capture);
				rec.label = 'RECORD';
				previewWindow = new ModalWindow(stage, 'PREVIEW');
				preview.bitmapData = record[0];
				var vb:VBox = new VBox(previewWindow, 10, 10);
				vb.addChild(preview);
				var hb:HBox = new HBox(vb, 0, 0)
				new PushButton(hb, 0, 0, 'save', saveGif);
				new PushButton(hb, 0, 0, 'discard', cleanPreviewWindow);
				new PushButton(hb, 0, 0, 'delete even', deleteEvenFrames);
				qualitySlider = new InputSlider(vb, 0,0, 'quality (low is better, high is faster)');
				qualitySlider.value = 10;
				qualitySlider.slider.minimum = 1;
				qualitySlider.slider.maximum = 50;
				
				info = new Label(vb, 0, 0, 'Render time increases exp. with quality and image size');
				
				previewWindow.width = vb.width + 20;
				previewWindow.height = vb.height + 60;
				
				Scheduler.invokeEvery(showPreview);
			}
		}
		
		public function saveGif(e:*= null):void {
			if (rendering) return;
			encoder.setFrameRate(stage.frameRate);
			encoder.setQuality(qualitySlider.value);
			encoder.setRepeat(0);
			encoder.start()
			frameTime = getTimer();
			renderFrame();
			rendering = true;
		}
		
		private function renderFrame(i:int=0):void {
			encoder.addFrame(record[i]);
			var delta:int = getTimer() - frameTime;
			info.text = 'frame '+(i+1)+'/'+record.length+' - last frame = ' +Math.ceil(delta*.01)*.1 + ' - estimated time remaining ' + Math.ceil(delta * (record.length-i)*.01)*.1;
			frameTime = getTimer();
			i++;
			if (i < record.length) {
				Scheduler.invokeIn(renderFrame, 1, i);
			}else {
				finishRender();
			}
		}
		
		public function finishRender():void {
			Scheduler.cancel(renderFrame);
			encoder.finish();
			//var output:File = File.userDirectory.resolvePath('untitled.gif');
			var output:FileReference = new FileReference();
			FileReference(output).save(encoder.stream, 'superawesome.gif');
			
			/*File(output).browseForSave('SAVE THE GIF');
			
			
			output.addEventListener(Event.SELECT,function(e:Event):void{
				var stream:FileStream = new FileStream();
				stream.open(e.target as File, FileMode.WRITE);
				stream.writeBytes(encoder.stream);
				stream.close();
				cleanPreviewWindow();
			});*/
			rendering = false;
			cleanPreviewWindow();
		}
		
		public function cleanPreviewWindow(e:*= null):void {
			if (rendering) {
				finishRender();
			}
			Scheduler.cancel(showPreview);
			Utils.removeChild(previewWindow);
			previewWindow = null;
			showHide();
		}
		
		public function deleteEvenFrames(e:*= null):void {
			for (var i:int = record.length-1; i >-1; i--) {
				if (i % 2 == 1) {
					record[i].dispose();
					record.splice(i, 1);
				}
			}
		}
		
		public function showPreview():void {
			record.push(record.shift());
			preview.bitmapData = record[0];
		}
		
		public function capture():void {
			if (visible) {
				if (recordFrames.value != 0) {
					frames++;
					if (frames > recordFrames.value) {
						onRecord();
						frames = 0;
						return;
					}
				}
				visible = false;
				var b:BitmapData = new BitmapData(size.width, size.height, true, 0);	
				/*if (view) {
					view.renderer.queueSnapshot(b3d);
					b.draw(b3d, new Matrix(1, 0, 0, 1, -size.x, -size.y), null, null);
				}*/
				b.draw(stage, new Matrix(1, 0, 0, 1, -size.x, -size.y), null, null);
				record.push(b);
				visible = true;
			}
		}
		
		public function changeWidth():void {
			size.width = w.value;
			draw();
		}
		
		public function changeHeight():void {
			size.height = h.value;
			draw();
		}
		
		private function init(e:Event):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);			
			stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
		}
		
		public function onMove(e:MouseEvent):void {
			if (visible && e.buttonDown) {
				if(e.ctrlKey&&Utils.isPointInRect(lastMouse, size)) {
					size.x += mouseX - lastMouse.x;
					size.y += mouseY - lastMouse.y;
					if (e.shiftKey) {
						size.x -= size.x % 25;
						size.y -= size.y % 25;
					}
					draw();
				}
			}
			lastMouse.setTo(mouseX, mouseY);
		}
		
		public function showHide():void {
			visible = !visible;
			draw();
		}
		
		public function draw():void {
			frame.graphics.clear();
			frame.graphics.lineStyle(1, 0xff00ff);
			frame.graphics.beginFill(0xffffff, .2);
			frame.graphics.drawRect(size.x, size.y, size.width, size.height);
			frame.graphics.endFill();
			var center:Point = new Point(size.x+size.width * .5, size.y+size.height * .5);
			frame.graphics.moveTo(center.x - 5, center.y);
			frame.graphics.lineTo(center.x + 5, center.y);
			frame.graphics.moveTo(center.x, center.y-5);
			frame.graphics.lineTo(center.x, center.y+5);
			
			frame.visible = true;
		}
		
	}
	
}