package worlds;

import flash.Lib;
import flash.geom.Point;
import flash.events.Event;
import flash.events.MouseEvent;
import com.haxepunk.World;
import com.haxepunk.Entity;
import com.haxepunk.HXP;
import com.haxepunk.graphics.Image;
import com.haxepunk.masks.Hitbox;
import com.haxepunk.utils.Key;
import com.haxepunk.utils.Input;
import com.haxepunk.Sfx;
import worlds.Win;
import worlds.Lose;
import entities.Needle;
import entities.Grave;
import entities.Keeper;

class Play extends World
{
	public static var music: Sfx;

	public static inline var szBord  : Int = 30;
	public static inline var szGraveW: Int = 30;
	public static inline var szGraveH: Int = 50;
	public static inline var szPathH : Int = 20;
	public static inline var szMPathW: Int = 40;
	public static inline var szGGapW : Int = 10;
	public static inline var nbGraveW: Int =  4;
	public static inline var nbGraveH: Int =  4;
	public static inline var szKeeper: Int = 30;
	
	public static inline var nbGraveWx2: Int = nbGraveW * 2;
	public static inline var szGraveTW : Int = szGraveW + szGGapW;
	public static inline var szGraveTH : Int = szGraveH + szPathH;
	public static inline var szGrYardW : Int = (szGraveTW * nbGraveW) - szGGapW;
	public static inline var szKeepX3  : Int = szKeeper * 3;
	public static inline var iniKeepPX : Float = szBord + szGrYardW + ((szMPathW - szKeeper) / 2);
	public static inline var iniKeepPY : Float = (szBord/2) + (szGraveTH * nbGraveH) - szPathH;

	public var tmRising   : Int = 5750;
	public var tmRisingMin: Int = 3750;
	public var tmRiseDec  : Int =  250;
	public var tmKpMoveX3 : Int =  840;
	public var tmSndKpMove: Int =  280;
	public var tmHit      : Int =  340;
	public var nbRiseBefUp: Int =    3;
	public var tmWin      : Int = 211000;
	
	public var mouseClick: Bool;
	public var timerGame: Int;
	public var timerRise: Int;
	public var timerKeep: Int;
	public var timerHit: Int;
	public var nbRises: Int;
	
	public var needleH: Needle;
	public var needleM: Needle;
	public var keeper: Keeper;
	public var graves: Array<Array<Grave>>;
	
	public var hiting: Bool;
	public var pendingHit: Bool;
	public var nextHitX: Int;
	public var nextHitY: Int;
	public var nextHitPx: Point;
	public var moved: Float;

	public var sndKpStep: Array<Sfx>;
	public var lastKpStep: Int;
	public var sndHit: Array<Sfx>;
	public var lastHit: Int;
	
    public function new()
    {
        super();
		
		addGraphic(new Image("gfx/Background.png"));

		
		needleH = new Needle(16,21,4,12);
		needleM = new Needle(16,21,6,60);
		graves = new Array();
		keeper = new Keeper(iniKeepPX, iniKeepPY);
		
		nextHitPx = new Point(0, 0);
		
		sndKpStep = new Array();
		sndKpStep[1] = new Sfx("sfx/KeepStep1.wav");
		sndKpStep[2] = new Sfx("sfx/KeepStep2.wav");
		sndKpStep[3] = new Sfx("sfx/KeepStep3.wav");
		sndKpStep[4] = new Sfx("sfx/KeepStep4.wav");
		sndKpStep[5] = new Sfx("sfx/KeepStep5.wav");
		sndHit = new Array();
		sndHit[1] = new Sfx("sfx/Hit1.wav");
		sndHit[2] = new Sfx("sfx/Hit2.wav");
		sndHit[3] = new Sfx("sfx/Hit3.wav");
		sndHit[4] = new Sfx("sfx/Hit4.wav");
		sndHit[5] = new Sfx("sfx/Hit5.wav");
    }
	
	public override function begin() : Void
	{
	  	super.begin();

		mouseClick = false;

		#if (!windows)
			music = new Sfx("music/Midnight_lullaby.mp3");
		#end

		#if (!windows)
			music.loop();
			music.volume = 0.5;
		#end
		
		for (ix in 1...nbGraveW + 1) {
			var posX: Int = szBord+(ix-1)*szGraveTW;
			graves[ix] = new Array();
			graves[ix+nbGraveW] = new Array();
			for (iy in 1...nbGraveH+1) {
				var posY: Int = szBord+(iy-1)*szGraveTH;
				graves[ix][iy] = new Grave(posX,posY);
				add(graves[ix][iy]);
				graves[ix+nbGraveW][iy] = new Grave(szGrYardW+szMPathW+posX+Std.random(5)-2,posY+Std.random(3)-1);
				add(graves[ix+nbGraveW][iy]);
			}
		}
		
		add(needleH); add(needleM);
		add(keeper);
		
		hiting = false; pendingHit = false; nbRises = 0;
		timerGame = Lib.getTimer() + tmWin;
		timerRise = Lib.getTimer() + tmRising;
		timerKeep = 0; timerHit = 0; moved = 0;
		lastKpStep = 1; lastHit = 1;
  	}

    public override function update() : Void
    {
		var cTime: Int = Lib.getTimer();
		
		var eTimeM: Int = Math.floor((tmWin-(timerGame-cTime)) / 250) + 1050;
		var eTimeH: Float = eTimeM / 60;
		eTimeM -= 60 * Math.floor(eTimeH);
		while (eTimeH >= 12) { eTimeH -= 12; }
		needleH.setCadran(eTimeH);
		needleM.setCadran(eTimeM);
		
		if (Input.mousePressed) {
			mouseClick = true;
		} else if (Input.mouseReleased && mouseClick) {
			mouseClick = false;
			if (hiting) { hiting = false; keeper.Stay(); }
			var rightPart: Bool = false;
			var posX: Float = Math.round(this.mouseX+(szGGapW/2));
			var posY: Float = Math.round(this.mouseY);
			if (posX > (szBord + szGrYardW + (szMPathW / 2))) {
				rightPart = true; posX -= szGrYardW + szMPathW;
			}
			var selX: Int = 1 + Math.floor((posX - szBord + 1) / szGraveTW);
			var selY: Int = 1 + Math.floor((posY - szBord + 1) / szGraveTH);
			if ((selX >= 1) && (selX <= nbGraveW) && (selY >= 1) && (selY <= nbGraveH)) {
				pendingHit = true; moved = tmSndKpMove;
				timerKeep = cTime;
				nextHitX = selX; nextHitY = selY;
				nextHitPx.x = szBord + (selX-1)*szGraveTW;
				if (rightPart) { nextHitX += nbGraveW; nextHitPx.x += szGrYardW + szMPathW; }
				nextHitPx.y = (szBord/3) + (selY-1)*szGraveTH + szGraveH;
			} else {
				pendingHit = false;
				timerKeep = 0;
				nextHitX = 0; nextHitY = 0;
				nextHitPx.x = 0; nextHitPx.y = 0;
				moved = 0; if (sndKpStep[lastKpStep].playing) { sndKpStep[lastKpStep].stop(); }
			}
		}
		
		if ((cTime >= timerRise) && (cTime < (timerGame-tmRisingMin))) {
			var RiseStat, rndX, rndY: Int;
			do {
				rndX = 1 + Std.random(nbGraveWx2);
				rndY = 1 + Std.random(nbGraveH);
				RiseStat = graves[rndX][rndY].RiseLevel;
			} while (RiseStat > 0);
			for (ix in 1...nbGraveWx2 + 1) {
				for (iy in 1...nbGraveH + 1) {
					if (((ix==rndX) && (iy==rndY)) || (graves[ix][iy].RiseLevel>0)) {
						if (graves[ix][iy].Rise()) {
							if ((hiting) && (ix == nextHitX) && (iy == nextHitY)) { hiting = false; keeper.Stay(); }
							//rise a skeleton ? nope, not enough time to do that.
							Lose();
						} else if ((! pendingHit) && (! hiting) && (ix == nextHitX) && (iy == nextHitY)) {
							hiting = true; timerHit = cTime + Math.round((tmHit/2));
						}
					}	
				}
			}
			timerRise = cTime + tmRising;		
			if (tmRising > tmRisingMin) {
				nbRises += 1;
				if (nbRises >= nbRiseBefUp) { tmRising -= tmRiseDec; nbRises = 0; }
			}
		}
		
		if (pendingHit) {
			if ((nextHitPx.x != keeper.x) || (nextHitPx.y != keeper.y)) {
				var deltaT: Int = cTime - timerKeep;
				if (deltaT > 0) {
					moved += deltaT;
					var deltaPx: Float = (deltaT / tmKpMoveX3) * szKeepX3;
					while (deltaPx > 0) {
						if (keeper.x == iniKeepPX) {
							var distY: Float = nextHitPx.y - keeper.y;
							if (Math.abs(distY)<deltaPx) {
								keeper.y = nextHitPx.y;
								deltaPx -= Math.abs(distY);
							} else {
								if (distY < 0) { deltaPx *= -1; }
								keeper.y += deltaPx;
								deltaPx = 0;
							}
						}
						if (deltaPx > 0) {
							var destX: Float = 0;
							if (nextHitPx.y == keeper.y) { destX = nextHitPx.x; } else { destX = iniKeepPX;}
							var distX: Float = destX - keeper.x;
							if (Math.abs(distX)<deltaPx) {
								keeper.x = destX;
								if (destX == nextHitPx.x) { deltaPx = 0; } else { deltaPx -= Math.abs(distX); }
							} else {
								if (distX < 0) { deltaPx *= -1; }
								keeper.x += deltaPx;
								deltaPx = 0;
							}
						}
					}
					if (moved >= tmSndKpMove) {
						moved -= tmSndKpMove;
						var KpStep: Int;
						do {
							KpStep = 1 + Std.random(5);
						} while (KpStep == lastKpStep);
						lastKpStep = KpStep;
						sndKpStep[KpStep].play();						
					}
				}
				timerKeep = cTime;
			}
			if ((nextHitPx.x == keeper.x) && (nextHitPx.y == keeper.y)) {
				//keeper hit rising skeleton
				pendingHit = false;
				timerKeep = 0; nextHitPx.x = 0; nextHitPx.y = 0;
				if (graves[nextHitX][nextHitY].RiseLevel>0) {
					hiting = true; timerHit = cTime + Math.round((tmHit/2));
				}
				moved = 0; if (sndKpStep[lastKpStep].playing) { sndKpStep[lastKpStep].stop(); }
			}
		}
		
		if ((! pendingHit) && (cTime >= timerHit)) {
			if (hiting) {
				if (! graves[nextHitX][nextHitY].Hit()) { hiting = false; }
				timerHit = cTime + tmHit;
				var Hit: Int;
				do {
					Hit = 1 + Std.random(5);
				} while (Hit == lastHit);
				lastHit = Hit;
				sndHit[Hit].play();
				keeper.Hit();
			} else { keeper.Stay(); }
		}
		
		if (cTime >= timerGame) { Win(); }
		
  		super.update();
	}
	
	public function Win()
	{
		#if (!windows)
			music.stop();
		#end
		HXP.world = new Win();
	}
	
	public function Lose()
	{
		#if (!windows)
			music.stop();
		#end
		HXP.world = new Lose();
	}

}

