package com.alternativagame.engine3d.object.light {
	import com.alternativagame.engine3d.material.HelperMaterial;
	import com.alternativagame.engine3d.material.Material;
	import com.alternativagame.engine3d.skin.OmniSkin;
	import com.alternativagame.type.RGB;
	import com.alternativagame.type.Vector;
	import com.alternativagame.engine3d.skin.Skin;
	import com.alternativagame.engine3d.object.Object3D;
	import com.alternativagame.engine3d.Math3D;
	import com.alternativagame.engine3d.engine3d;
	
	use namespace engine3d;

	public class Omni3D extends Light3D {

		use namespace engine3d;

		// Расстояния влияния источника света
		private var _near:Number;
		private var _far:Number;

		// Квадраты расстояний влияния
		engine3d var nearSqr:Number;
		engine3d var farSqr:Number;
		
		// Предпросчитанная обратная разница расстояний влияния
		engine3d var farNearSqr:Number;
		
		// Параметры в системе камеры
		engine3d var canvasNear:Number;
		engine3d var canvasFar:Number;
		engine3d var canvasNearSqr:Number;
		engine3d var canvasFarSqr:Number;
		engine3d var canvasFarNearSqr:Number;
		
		// Часто изменяемый источник
		private var _mobile:Boolean = false;

		public function Omni3D(color:RGB = null, near:Number = 0, far:Number = 100, material:HelperMaterial = null) {
			super(color, material);
			_near = near;
			_far = far;
			calculateParams();
		}
		
		override protected function updateTransform():void {
			super.updateTransform();
			calculateCanvasParams();			 
		}
		
		override protected function createSkin():Skin {
			return new OmniSkin(this);
		}
		
		// Освещение в заданной точке (null, если нет)
		override engine3d function getLightColor(coords:Vector, normal:Vector):RGB {
			// Находим вектор до точки из источника
			var vector:Vector = new Vector(coords.x - transform.d, coords.y - transform.h, coords.z - transform.l);
			// Находим квадрат расстояния
			var length:Number = Math3D.vectorLengthSquare(vector);
			
			// Если за пределами влияния
			if (length > canvasFarSqr) {
				return null;
			} else {
				Math3D.normalize(vector);
				var res:RGB = calculateLightColor(normal, vector);
				// Если в промежутке near и far  
				if (res != null && length > canvasNearSqr) {
					res.multiply(canvasFarNearSqr*(canvasFarSqr - length));
				}
				return res; 
			}
		}
		
		// Обновление предпросчитанных параметров
		private function calculateParams():void {
			nearSqr = _near * _near;
			farSqr = _far * _far;
			farNearSqr = 1/(farSqr - nearSqr);
		}
		
		// Обновление предпросчитанных параметров с учётом масштаба камеры
		private function calculateCanvasParams():void {
			canvasNear = _near * view.zoom;
			canvasFar = _far * view.zoom;
			canvasNearSqr = canvasNear * canvasNear;
			canvasFarSqr = canvasFar * canvasFar;
			canvasFarNearSqr = 1/(canvasFarSqr - canvasNearSqr);
		}
		
		public function set near(value:Number):void {
			_near = value;
			calculateParams();
			updateSkin();
			applyToSolidObjects();
		}
		
		public function get near():Number {
			return _near;
		}

		public function set far(value:Number):void {
			_far = value;
			calculateParams();
			updateSkin();
			applyToSolidObjects();
		}

		public function get far():Number {
			return _far;
		}
		
		public function get mobile():Boolean {
			return _mobile;
		}
		
		public function set mobile(value:Boolean):void {
			if (_mobile != value) {
				_mobile = value;
				applyToSolidObjects();
			}			
		}
		
		// Клон
		override public function clone():Object3D {
			var res:Omni3D = new Omni3D();
			cloneParams(res);
			return res;
		}
		
		// Клонировать параметры
		override protected function cloneParams(object:*):void {
			var obj:Omni3D = Omni3D(object);
			super.cloneParams(obj);
			obj.far = far;
			obj.near = near;
			obj.mobile = mobile;
		}
		
	}
}