/* ---------------
// Image Extruder
// ---------------
//
// AUTHOR:  Sammy Joe Osborne
// DATE:    01/08/2010
//
// DESCRIPTION: The image extruder evaluates the brightness of pixels in an image. Based on a pixels brightness, a plane is drawn in
// 3D space at varying size and z-depth. This is similar in functionality of a bump-map in most 3D software. Color can be used,
// amplitude can be adjusted, and the 3D view can be rotated all in real-time.

// Thanks to Pickle from www.allflashwebsite.com for the autoOrbit classes.
*/


package
{
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	
	import org.papervision3d.core.math.Matrix3D;
	import org.papervision3d.core.math.Number3D;
	import org.papervision3d.view.BasicView;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.primitives.Plane;
	import org.papervision3d.materials.ColorMaterial;
  	import org.papervision3d.materials.special.LineMaterial;
	import org.papervision3d.core.geom.Lines3D;
  	import org.papervision3d.core.geom.renderables.Line3D;
  	import org.papervision3d.core.geom.renderables.Vertex3D;
	import org.papervision3d.Papervision3D;
	import org.papervision3d.objects.primitives.Sphere;
	//import com.afw.papervision3d.view.AutoOrbitDrag;

 
	//[SWF(width="640", height="480", backgroundColor="#ffffff", frameRate="60")]
	public class StuffYerFace extends BasicView
	{
		private static const FORWARD:Number3D = new Number3D(0, 0, 1); //for rotation
		private var previousMousePoint:Point = new Point(); //for rotation
		private var isMouseDown:Boolean = false; //for rotation
		/*[Embed(source="images/test2.jpg")]
		private var test2:Class;*/
		
		//Set up variables
		const NUM_PLANES = 3000; //number of planes to compose the image of
		private var bmp:Bitmap; //the image that will be used
		private var maxSize:Number = 17; //the maximum size of each plane
		private var minSize:Number = 12; //the minimum size of each plane
		private var spacing:Number = 1; //spacing between the planes
		private var scale:int = 15; //how much larger than the actual image the plane construct should be
		private var _amplitude:Number = .5; //how severely brightness effects zDepth
		private var _colorize:Boolean = false;; //whether to use color or not
		private var numPlanesWide:int; //number of planes wide to construct it
		private var numPlanesHigh:int; //number of planes high to construct it
		private var containerWidth:Number; //how wide the whole plane container is
		private var containerHeight:Number; //how tall the whole plane container is

		private var camTarget:DisplayObject3D = new DisplayObject3D(null,null); //the target for the camera
		private var planeContainer:DisplayObject3D = new DisplayObject3D(); //groups all the planes together into one display object
		private var pcContainer:DisplayObject3D = new DisplayObject3D(); //another wrapper around the planeContainer (thus pcContainer)
		//private var orbitor:AutoOrbitDrag;
		private var sphere:Sphere;
		//var orbitInitialized:Boolean = false;
		private var planeArray:Array = new Array();
		private var brightnessArray:Array = new Array();
		private var pixelArray:Array = new Array();
		var planeContainerAdded:Boolean = false;
		
		public function StuffYerFace()
		{
			Papervision3D.PAPERLOGGER.unregisterLogger(Papervision3D.PAPERLOGGER.traceLogger); //turns off debug trace statements
			//importBitmap(); //imports the bitmap and sets all size related variables based on the bitmap's dimensions

			trace(camera.x + ", " + camera.y + ", " + camera.z);
			addEventListener(MouseEvent.MOUSE_DOWN, stage_mouseDownHandler); //TODO change from stage to the view holder
			addEventListener(MouseEvent.MOUSE_UP, stage_mouseUpHandler); //TODO see above
			
			addAxis();
			//createPlanes(NUM_PLANES);
			
			sphere = new Sphere();
			sphere.visible = true;
			//scene.addChild(sphere);			
			startRendering();
		}
		
		
		/*public function initOrbit():void {
			orbitor = new AutoOrbitDrag(this, sphere);
			orbitInitialized = true;
		}*/
		
		public function importBitmap(bmpImport:Bitmap):void {
			bmp = bmpImport;
			var ratio:Number;//width to height ratio for the image so we can size the planes array accordingly
			//bmp = new Bitmap(new test2(1,1)); //imports the image to use
			ratio = bmp.height / bmp.width; //width to height ratio for the image so we can size the planes array accordingly
			numPlanesWide = Math.ceil(Math.sqrt(NUM_PLANES/ratio)); 
			numPlanesHigh = Math.ceil(numPlanesWide*ratio);
			//x*scale + maxSize + spacing;
			containerWidth = (numPlanesWide*scale + maxSize + maxSize)* spacing; //fuck. there is no width or height property so 
			containerHeight = (numPlanesHigh*scale + maxSize + maxSize)* spacing; //we gotta figure it out...how shitty is that. wtf.
			trace("containerWidth: " + containerWidth);
			trace("containerHeight: " + containerHeight);
			planeContainer.x = -containerWidth/2;
			planeContainer.y = containerHeight/2;
			if(!planeContainerAdded) {
				planeContainer.scaleX = -1;
				pcContainer.addChild(planeContainer);
				scene.addChild(pcContainer);
				planeContainerAdded = true;
			}
			//remove anything in the container if anything exists
			if(planeContainer.numChildren > 0) {
				removeChildren(planeContainer);
			}
			createPlanes(NUM_PLANES);
		}
		
		public function createPlanes(numPlanes:uint) {
			trace("w: " + numPlanesWide + "h: " + numPlanesHigh);
			var colorMaterial:ColorMaterial = new ColorMaterial(0x000000); //just a plane black material for now
			colorMaterial.doubleSided = true;
			var sizeRatio:Number = (bmp.width*bmp.height)/NUM_PLANES;
			var zDepth:Number;
			var pixelX, pixelY:int;
			var creationSize:Number;
			var newColorMaterial:ColorMaterial;
			for(var x:uint = 0; x<=numPlanesWide; x++) {
				planeArray[x] = new Array();
				brightnessArray[x] = new Array();
				pixelArray[x] = new Array();
				for (var y:uint = 0; y<numPlanesHigh; y++) {
					pixelX = int(x*(bmp.width/numPlanesWide));
					pixelY = int(y*(bmp.height/numPlanesHigh));
					//trace("plane ("+x+","+" "+y+") is based on pixel " + "("+pixelX+", "+pixelY+")");
					//pixel = bmp.bitmapData.getPixel(pixelX, pixelY);
					pixelArray[x][y] = bmp.bitmapData.getPixel(pixelX, pixelY);
					brightnessArray[x][y] = (calcBrightness(pixelArray[x][y]));
					creationSize = maxSize-(((brightnessArray[x][y]/255)*(maxSize-1))+minSize);
					trace(creationSize);
					if(creationSize <= 0) creationSize += 1;
					planeArray[x][y] = new Plane(colorMaterial, creationSize, creationSize);//plane.clone();
					planeArray[x][y].x =  -x*scale + maxSize + spacing;//*10 + maxSize + spacing;//planeVector[y-1].x + maxSize + spacing;
					planeArray[x][y].y = -y*scale + maxSize + spacing;//*10) - maxSize - spacing;//planeVector[y-1].y + maxSize + spacing;
					trace("xy: " + planeArray[x][y].x + ", " + planeArray[x][y].y);
					zDepth = _amplitude*brightnessArray[x][y]; //gives a Z-depth in relation to the brightness of the associated pixel, times the _amplitude
					planeArray[x][y].z = -zDepth;
					
					planeContainer.addChild(planeArray[x][y]);
				}
			}
			//pcContainer.addChild(sphere);
			//planeContainer.addChild(sphere);
		}
		
		/*
		removes all children in an object
		@param: object - the object to remove the children from
		@returns: false if no children are present, true if all children were removed
		*/		
		private function removeChildren(object:Object):Boolean {
				if(object.numChildren == 0) {
					return false;
				}
				for(var o:Object in object.children)
				{
				  object.removeChild(object.children[o]);
				}
				return true;
		}
		
		/*I didn't write this part but I only used it for testing purposes...it's fairly simple anyway*/
		private function addAxis():void {
			// Create a default line material and a Lines3D object (container for Line3D objects)
			var defaultMaterial:LineMaterial = new LineMaterial(0x000000);
			var axes:Lines3D = new Lines3D(defaultMaterial);
			
			// Create a different colour line material for each axis
			var xAxisMaterial:LineMaterial = new LineMaterial(0xFF0000);
			var yAxisMaterial:LineMaterial = new LineMaterial(0x00FF00);
			var zAxisMaterial:LineMaterial = new LineMaterial(0x0000FF);
			
			// Create a origin vertex
			var origin:Vertex3D = new Vertex3D(0, 0, 0);
			
			// Create a new line (length 100) for each axis using the different materials and a width of 2.
			var xAxis:Line3D = new Line3D(axes, xAxisMaterial, 1, origin, new Vertex3D(100, 0, 0));
			var yAxis:Line3D = new Line3D(axes, yAxisMaterial, 1, origin, new Vertex3D(0, 100, 0));
			var zAxis:Line3D = new Line3D(axes, zAxisMaterial, 1, origin, new Vertex3D(0, 0, 100));
			
			// Add lines to the Lines3D container
			axes.addLine(xAxis);
			axes.addLine(yAxis);
			axes.addLine(zAxis);
			scene.addChild(axes);
		}
		
		public function set amplitude(value:Number) {
			_amplitude = value;
			var zDepth:Number;
			for(var x:uint = 0; x<=numPlanesWide; x++) {
				for (var y:uint = 0; y<numPlanesHigh; y++) {
					zDepth = _amplitude*brightnessArray[x][y];
					planeArray[x][y].z = -zDepth;
				}
			}
		}
		public function get amplitude():Number {
			return _amplitude;
		}
		
		public function set colorize(value:Boolean):void {
			if(_colorize!=value) {
				var x:uint;
				var y:uint;
				var newColorMaterial:ColorMaterial;
				if(_colorize) {
					for(x = 0; x<=numPlanesWide; x++) {
						for (var y:uint = 0; y<numPlanesHigh; y++) {
							newColorMaterial = new ColorMaterial(pixelArray[x][y]);
							newColorMaterial.doubleSided = true;
							planeArray[x][y].material = newColorMaterial;
						}
					}
				}
				else {
					newColorMaterial = new ColorMaterial(0x000000);
					newColorMaterial.doubleSided = true;
					for(x = 0; x<=numPlanesWide; x++) {
						for (y = 0; y<numPlanesHigh; y++) {							
							planeArray[x][y].material = newColorMaterial;//new ColorMaterial(Math.random()*0xffffff);
						}
					}
				}
				_colorize = value;

			}
		}
		public function get colorize():Boolean {
			return _colorize;
		}
		/*
		Takes a pixel and evaluates the brightness
		*/
		private function calcBrightness(p:uint):uint {
			var r:uint = (p&0xFF0000)>>16;
			var g:uint = (p&0xFF00)>>8;
			var b:uint = (p&0xFF);
			//trace((r<<16 | g<<8 | b).toString(2));
			//return (Math.sqrt((.241*((p&0xFF0000)*(p&0xFF0000))) + (.691*((p&0xFF00)*(p&0xFF00))) + (.068*((p&0xFF)*(p&0xFF))) ));
			return (Math.sqrt((.241*(r*r)) + (.691*(g*g)) + (.068*(b*b))));
		}
		
		/*for rotation*/
		private function stage_mouseDownHandler(event:MouseEvent):void
		{
			isMouseDown = true;
		}
 		/*for rotation*/
		private function stage_mouseUpHandler(event:MouseEvent):void
		{
			isMouseDown = false;	
		}
		
		
		override protected function onRenderTick(e:Event=null):void
		{
			/*if(orbitInitialized) {
				orbitor.update();
			}*/
			var currentMousePoint:Point = new Point(viewport.containerSprite.mouseX, viewport.containerSprite.mouseY);
			if(isMouseDown)
			{
				var difference:Point = currentMousePoint.subtract(previousMousePoint);
	 			var vector:Number3D = new Number3D(difference.x, difference.y, 0);
 
				var rotationAxis:Number3D = Number3D.cross(vector, FORWARD);
				rotationAxis.normalize();
 
				var distance:Number = Point.distance(currentMousePoint, previousMousePoint);
				var rotationMatrix:Matrix3D = Matrix3D.rotationMatrix(rotationAxis.x, -rotationAxis.y, rotationAxis.z, distance/250);
 
				pcContainer.transform.calculateMultiply3x3(rotationMatrix, pcContainer.transform);
			}
 
			previousMousePoint = currentMousePoint;
			
			super.onRenderTick(e);
		}
 
	}
}