package display.states
{
	import display.BaseState;
	import display.DeathOverlay;
	import display.MinedDisplay;
	import display.PauseOverlay;
	import entities.Character;
	import entities.Manager;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.ui.Keyboard;
	import geom.Polygon2D;
	import geom.Utils;
	import light.LightEngine;
	import map.Generator;
	import map.Map;
	import map.MapOptions;
	import map.Renderer;
	import map.Score;
	import sound.BackgroundMusic;
	import starling.display.BlendMode;
	import starling.display.Image;
	import starling.display.Shape;
	import starling.events.Event;
	import starling.events.KeyboardEvent;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import tiles.TileNames;
	
	/**
	 * ...
	 * @author RedMaster
	 */
	public class GameState extends BaseState
	{
		private var _options:MapOptions;
		
		private var _map:Map;
		private var _generator:Generator;
		private var _char:Character;
		private var _renderer:Renderer;
		private var _lightEngine:LightEngine;
		private var _minedDisplay:MinedDisplay;
		private var _charDisplay:Image;
		private var _selector:Image;
		private var _overlay:Image;
		private var _pause:PauseOverlay;
		private var _entities:Manager;
		private var _death:DeathOverlay;
		
		private var _canvas:Shape;
		
		private var _u:Boolean;
		private var _d:Boolean;
		private var _l:Boolean;
		private var _r:Boolean;
		
		private var _halfScreen:Point;
		private var _charOffset:Point;
		private var _cursor:Point;
		private var _pressed:Boolean;
		private var _paused:Boolean;
		
		private var _music:BackgroundMusic;
		
		public static var DEATH:String = "death";
		
		public function setOptions(value:MapOptions):void
		{
			_options = value;
		}
		
		public function get options():MapOptions
		{
			return _options;
		}
		
		override public function init():void
		{
			if (_options == null)
			{
				Log.log(this, "WARNING: No options specified, using defaults");
				_options = new MapOptions();
				_options.makeDefaults();
			}
			
			_music = new BackgroundMusic();
			
			_paused = false;
			_pressed = false;
			
			_u = _d = _l = _r = false;
			_cursor = new Point();
			_halfScreen = new Point(C.starling.nativeStage.stageWidth / 2, C.starling.nativeStage.stageHeight / 2);
			
			_canvas = new Shape();
			_map = new Map(_options.size, 0);
			_generator = new Generator();
			if (_options.createNew)
				_map = _generator.generate(_options.seed, _map.width, _map.height, _options);
			else
				_map.loadFrom(_options.name);
			_char = new Character(_map.spawn.x, _map.spawn.y, _map);
			_renderer = new Renderer(C.assets.getTextureAtlas("tiles"), new Point(32, 32), _map);
			_lightEngine = new LightEngine(_canvas, _halfScreen.clone(), 1000, 800);
			_minedDisplay = new MinedDisplay();
			_minedDisplay.mined = _map.minedCount;
			_minedDisplay.total = _map.goldCount;
			_overlay = new Image(C.assets.getTexture("shadow_overlay"));
			_overlay.blendMode = BlendMode.MULTIPLY;
			
			_map.scaleX = 32;
			_map.scaleY = 32;
			
			_selector = new Image(C.assets.getTexture("selector"));
			
			_charOffset = new Point(_halfScreen.x - _char.width / 2, _halfScreen.y - _char.height / 2);
			
			_map.recalculateGlobalLight();
			
			_charDisplay = new Image(C.assets.getTexture("char"));
			_charDisplay.pivotX = _charDisplay.pivotY = _charDisplay.width / 2;
			_charDisplay.x = _halfScreen.x;
			_charDisplay.y = _halfScreen.y;
			_charDisplay.width = _char.width;
			_charDisplay.height = _char.height;
			
			_pause = new PauseOverlay();
			_pause.addEventListener(PauseOverlay.SHOW, onPauseShow);
			_pause.addEventListener(PauseOverlay.HIDE, onPauseHide);
			_pause.addEventListener(PauseOverlay.MENU, onMenuTrigger);
			_pause.addEventListener(PauseOverlay.SAVE, onSaveTrigger);
			_pause.addEventListener(PauseOverlay.RESUME, onResumeTrigger);
			_pause.hide();
			if (_map.isReadOnly) _pause.saveEnabled = false;
			
			_death = new DeathOverlay();
			_death.visible = false;
			_death.retryEnabled = !_options.createNew;
			_death.addEventListener(DeathOverlay.MENU, onMenuTrigger);
			_death.addEventListener(DeathOverlay.RETRY, onRetryTrigger);
			
			addEventListener(DEATH, onDeath);
			
			_entities = new Manager();
			_entities.addEntities(_map.ents);
			
			_music.start();
			
			addChild(_renderer);
			addChild(_entities);
			addChild(_canvas);
			addChild(_charDisplay);
			addChild(_selector);
			addChild(_renderer.hints);
			addChild(_overlay);
			addChild(_minedDisplay);
			addChild(_pause);
			addChild(_death);
			
			addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
			addEventListener(TouchEvent.TOUCH, onTouch);
			
			C.starling.nativeStage.removeChild(C.preloader);
		}
		
		public function makeLightOverlay():void
		{
			_overlay.texture = C.assets.getTexture("shadow_overlay_balrog");
			_canvas.visible = false;
			removeChild(_canvas);
		}
		
		private function onKeyDown(event:KeyboardEvent):void
		{
			switch (event.keyCode)
			{
				case Keyboard.W: 
					_u = true;
					break;
				case Keyboard.S: 
					_d = true;
					break;
				case Keyboard.A: 
					_l = true;
					break;
				case Keyboard.D: 
					_r = true;
					break;
				case Keyboard.ESCAPE: 
					if (_paused)
						_pause.hide();
					else
						_pause.show();
					break;
			}
		}
		
		private function onKeyUp(event:KeyboardEvent):void
		{
			switch (event.keyCode)
			{
				case Keyboard.W: 
					_u = false;
					break;
				case Keyboard.S: 
					_d = false;
					break;
				case Keyboard.A: 
					_l = false;
					break;
				case Keyboard.D: 
					_r = false;
					break;
			}
		}
		
		private function onTouch(event:TouchEvent):void
		{
			var t:Touch = event.getTouch(this, null);
			if (t)
				_cursor = t.getLocation(this);
			t = event.getTouch(this, TouchPhase.BEGAN);
			if (t)
				_pressed = true;
			t = event.getTouch(this, TouchPhase.ENDED);
			if (t)
			{
				_map.stopSound();
				_pressed = false;
			}
		}
		
		private function rebuildGeometry():void //WARNING: Индусский лагучий код!
		{
			if(!_canvas.visible) return;
			
			_lightEngine.clearPolygons();
			var rect:Rectangle = _renderer.viewportRectangle;
			var _b:uint = 0;
			var _l:uint = 0;
			var _p:Point = new Point();
			var _pol:Polygon2D = new Polygon2D();
			
			for (var i:uint = 0; i < rect.width; i++)
			{
				for (var j:uint = 0; j < rect.height; j++)
				{
					_b = _map.getBlock(rect.x + i, rect.y + j);
					if (!TileNames.isHard(_b))
						continue;
					if (_b == TileNames.getId("bounds"))
						continue;
					_l = _map.getLight(rect.x + i, rect.y + j);
					if (_l == Map.NULL)
						continue;
					_p.x = _renderer.x + i * 32;
					_p.y = _renderer.y + j * 32;
					if (_map.getLight(rect.x + i, rect.y + j - 1) == 0)
					{
						_pol.clearPoints();
						_pol.addPoint(new Point(_p.x, _p.y));
						_pol.addPoint(new Point(_p.x + 32, _p.y));
						_lightEngine.addPolygon(_pol.clone());
					}
					if (_map.getLight(rect.x + i, rect.y + j + 1) == 0)
					{
						_pol.clearPoints();
						_pol.addPoint(new Point(_p.x + 32, _p.y + 32));
						_pol.addPoint(new Point(_p.x, _p.y + 32));
						_lightEngine.addPolygon(_pol.clone());
					}
					if (_map.getLight(rect.x + i - 1, rect.y + j) == 0)
					{
						_pol.clearPoints();
						_pol.addPoint(new Point(_p.x, _p.y + 32));
						_pol.addPoint(new Point(_p.x, _p.y));
						_lightEngine.addPolygon(_pol.clone());
					}
					if (_map.getLight(rect.x + i + 1, rect.y + j) == 0)
					{
						_pol.clearPoints();
						_pol.addPoint(new Point(_p.x + 32, _p.y));
						_pol.addPoint(new Point(_p.x + 32, _p.y + 32));
						_lightEngine.addPolygon(_pol.clone());
					}
				}
			}
		}
		
		private function getBlockPosUnderPoint(position:Point):Point
		{
			var p:Point = position.clone();
			p.x = Math.floor((position.x - _renderer.x) / 32);
			p.y = Math.floor((position.y - _renderer.y) / 32);
			return p;
		}
		
		private function updateBlockCursor():void
		{
			var _cursorBlock:Point = getBlockPosUnderPoint(_cursor);
			_selector.x = _renderer.x + _cursorBlock.x * 32;
			_selector.y = _renderer.y + _cursorBlock.y * 32;
			if (Utils.distance(new Point(_halfScreen.x, _halfScreen.y), _cursor) > 100)
				_selector.alpha = 0.3;
			else
				_selector.alpha = 1;
			
			if (!_pressed)
				return;
			if (_selector.alpha < 1)
			{
				_map.stopSound();
				return;
			}
			var p:Point = _renderer.getGlobalBlockPos(_cursorBlock);
			if (TileNames.getName(_map.damageBlock(p.x, p.y, 1)) == "gold")
			{
				_minedDisplay.mined = _map.minedCount;
				C.assets.playSound("yeah", 0, 0, C.transform);
				if (_map.goldCount <= 0)
				{
					var score:Score = new Score();
					score.mined = _map.minedCount;
					score.deathCount = 0;
					score.date = new Date();
					score.name = _options.name;
					score.worldSize = _map.width;
					Game.GameEnded.score = score;
					Game.GameEnded.readOnly = _map.isReadOnly;
					C.game.showState(Game.GameEnded);
				}
			}
		}
		
		private function updateCharacterSteps():void
		{
			if (_paused)
				return;
			if (_u)
			{
				_charDisplay.rotation = Utils.radians(0);
				_char.walk(Character.UP);
			}
			if (_d)
			{
				_charDisplay.rotation = Utils.radians(180);
				_char.walk(Character.DOWN);
			}
			if (_l)
			{
				_charDisplay.rotation = Utils.radians(-90);
				_char.walk(Character.LEFT);
			}
			if (_r)
			{
				_charDisplay.rotation = Utils.radians(90);
				_char.walk(Character.RIGHT);
			}
			_char.update();
		}
		
		override public function update(delta:Number):void
		{
			if (_paused)
				return;
			updateCharacterSteps();
			
			_renderer.setPosition(_char.x - _charOffset.x, _char.y - _charOffset.y);
			_renderer.update();
			_canvas.graphics.clear();
			rebuildGeometry();
			_lightEngine.update();
			updateBlockCursor();
			if (_entities != null)
			{
				_entities.char = _char.aabb;
				_entities.setPosition(_renderer.position.x, _renderer.position.y);
				_entities.update();
			}
		}
		
		public function get isPaused():Boolean
		{
			return _paused;
		}
		
		private function onDeath(e:Event):void
		{
			Log.log(this, "Player died");
			_paused = true;
			_death.mined = _minedDisplay.mined;
			_death.show(e.data != null);
			_entities.destroy();
		}
		
		private function onRetryTrigger(e:Event):void
		{
			C.game.showState(Game.Playing);
		}
		
		private function onPauseShow(e:Event):void
		{
			_paused = true;
		}
		
		private function onPauseHide(e:Event):void
		{
			_paused = false;
		}
		
		private function onMenuTrigger(e:Event):void
		{
			C.game.showState(Game.MainMenu);
		}
		
		private function onSaveTrigger(e:Event):void
		{
			_map.saveTo(_options.name);
			_death.retryEnabled = true;
			_options.createNew = false;
		}
		
		private function onResumeTrigger(e:Event):void
		{
			_pause.hide();
		}
		
		override public function destroy():void
		{
			_music.stop();
			removeChildren(0, -1, true);
			removeEventListeners();
			_pause.removeEventListeners();
			_death.removeEventListeners();
			_map = null;
			_entities.destroy();
		}
	}
}