package player;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;

import animations.PlayerAttackAnimations;
import engine.Game;
import engine.Handler;
import interfaces.Directions;
import interfaces.ItemIDs;
import interfaces.Mob;
import utils.Animation;
import utils.Stack;
import utils.Utils;

/*  ------------- Nihilne ------------
    * Created by Mitch Weaver - 2016 *
      ~~~~ All Rights Reserved ~~~~ */

public final class PlayerAttackSequence implements ItemIDs, Directions {
	// NOTE: hitbox is actually a hit-circle
	
	/*  GOALS:
	 * 
	 * 0) --- axioms ---
	 * 		0a) player is holding a weapon that can be used to attack --- check
	 * 		0b) player is not already undergoing an attack ---- (timer is not == 0)
	 * 1) Begin the animation sequence -- (start)
	 * 		1a) calculate the damage that should be used
	 * 		1b) calculate which base animation to play + set the timer to its length
	 * 		1c) set the player's attacking boolean to true
	 * 		1d) calculate the timer delay before the hitbox appears
	 * 		1e) calculate the size the hitbox should be
	 * 2) Tick and Render the current animation until the timer is up.
	 * 		2a) move the hitbox infront of the player in the correct direction of facing
	 * 		2b) according to the 'currentWeapon' tick the correct animation
	 * 		2c) "    " ---- render said animation
	 * 		2d) when timer is up, reset all variables, reset animation, set Player.attacking to false;
	 */

	// ANIMATIONS:
	private static int currentWeapon;
	
	public static volatile long timer;
	
	private static final int ATTACK_COOLDOWN_TIMER_AMOUNT = 10;
	public static int ATTACK_COOLDOWN_TIMER;
	
	public final static void start(){
		if(Player.attacking || ATTACK_COOLDOWN_TIMER > 0) return;
		Player.currentDamage = calcWeaponDamage();
		if(Player.currentDamage <= 0) return; /* 0 because is not holding a valid weapon */
		currentWeapon = calcAnimationType(); /* also sets the correct timer amount */
		Player.attacking = true;
		hitMobs.clear();
	}
	
	
	public final static void tick() {
		if(!Player.attacking) return;
		Player.using = false;
		
		moveHitbox();
		
		tickAnimations();
		
		if(--timer <= 0) {
			Player.attacking = false;
			Player.currentDamage = 0;
			ATTACK_COOLDOWN_TIMER = ATTACK_COOLDOWN_TIMER_AMOUNT;
			// substract durability
			for(final Animation a : PlayerAttackAnimations.basePlayerAttack)   a.restartAnimation();
			for(final Animation a : PlayerAttackAnimations.crudeAxeAttack)     a.restartAnimation();
			for(final Animation a : PlayerAttackAnimations.crudePickAxeAttack) a.restartAnimation();
			for(final Animation a : PlayerAttackAnimations.goblinSpearAttack)  a.restartAnimation();
			return;
		}
		
		if(currentWeapon != Player.equipmentSlots[HAND_SLOT].itemId)  timer = 0;  /* this happens if player unequips item mid-swing, stop animation */
	
		combat();
	}
	
	public static final Stack<Mob> hitMobs = new Stack<Mob>();
	private static final void combat() {
		// LOOP THROUGH ALL MOBS, THEN CALL THEIR HIT-BY-PLAYER-METHOD
		// IF THE MOB HAS ALREADY BEEN HIT, DONT HIT AGAIN
		for(Mob m : Handler.mobs) {
			if(!hitMobs.contains(m) && Utils.intersects(Player.attackBounds, m.center, m.radius)){
				m.HIT_BY_PLAYER();
				hitMobs.add(m);
			}
		}
	}
	
	private static final void moveHitbox(){
	    // MAKE THE ATTACK BOUNDS UNUSABLE UNTIL A CERTAIN DELAY
	    // NOTE THE NUMBER IS YOUR TIMER AMOUNT
		if(timer < 28 * 0.75f)  Player.attackBounds.center.x = Player.attackBounds.center.y = -99999999;
		
		
		
		// FIRST SWITCH = ANIMATION TYPE -----  SECOND SWITCH = ROTATION, THIRD = FACING
		
		int x = (int)Player.center.x;
		int y = (int)Player.center.y;
		
				switch(Game.rotation){
					case 1: {	 switch(Player.facing){
						case NORTH:{ 		 x+=0; y-=6;  break; }
						case NORTHEAST:{ 	 x+=8; y+=2;  break; }
						case EAST:{ 		 x+=5; y+=3;  break; }
						case SOUTHEAST:{ 	 x+=1; y+=20; break; }
						case SOUTH:{ 		 x-=6; y+=15; break; }
						case SOUTHWEST:{ 	 x-=5; y+=12; break; }
						case WEST:{			 x-=10; y+=4; break; }
						case NORTHWEST:{     x-=5; y-=3;  break; }
					} break; }
					case 2:{    switch(Player.facing){
						case 1:{ x-=2; y-=5;   break; }
						case 2:{ x+=7; y-=4;   break; }
						case 3:{ x+=6; y-=1;   break; }
						case 4:{ x+=15; y+=13; break; }
						case 5:{ x+=6; y+=16;  break; }
						case 6:{ x+=5; y+=13;  break; }
						case 7:{ x-=5; y+=11;  break; }
						case 8:{ x-=7; y+=2;   break; }
					} break; }
					case 3:{    switch(Player.facing){
						case 1:{ x-=5; y-=0;  break; }
						case 2:{ x+=3; y-=7;  break; }
						case 3:{ x+=3; y-=4;  break; }
						case 4:{ x+=19; y-=1; break; }
						case 5:{ x+=15; y+=8; break; }
						case 6:{ x+=11; y+=8; break; }
						case 7:{ x+=4; y+=12; break; }
						case 8:{ x-=4; y+=6;  break; }
					} break; }
					case 4:{    switch(Player.facing){
						case 1:{ x-=5; y+=5;   break; }
						case 2:{ x-=3; y-=6;   break; }
						case 3:{ x-=1; y-=4;   break; }
						case 4:{ x+=14; y-=14; break; }
						case 5:{ x+=16; y-=4;  break; }
						case 6:{ x+=13; y-=2;  break; }
						case 7:{ x+=10; y+=7;  break; }
						case 8:{ x+=3; y+=7;   break; }
	  				} break; }
					case 5:{    switch(Player.facing){
						case 1:{ x-=0; y+=8;  break; }
						case 2:{ x-=7; y-=2;  break; }
						case 3:{ x-=4; y-=2;  break; }
						case 4:{ x-=1; y-=20; break; }
						case 5:{ x+=8; y-=14; break; }
						case 6:{ x+=8; y-=11; break; }
						case 7:{ x+=12; y-=1; break; }
						case 8:{ x+=7; y+=4;  break; }
					} break; }
					case 6:{    switch(Player.facing){
						case 1:{ x+=6; y+=6;   break; }
						case 2:{ x-=6; y+=4;   break; }
						case 3:{ x-=5; y+=1;   break; }
						case 4:{ x-=14; y-=13; break; }
						case 5:{ x-=5; y-=15;  break; }
						case 6:{ x-=2; y-=12;  break; }
						case 7:{ x+=7; y-=9;   break; }
						case 8:{ x+=7; y-=2;   break; }
					} break; }
					case 7:{    switch(Player.facing){
						case 1:{ x+=8; y+=0;  break; }
						case 2:{ x-=1; y+=8;  break; }
						case 3:{ x-=3; y+=5;  break; }
						case 4:{ x-=20; y+=1; break; }
						case 5:{ x-=14; y-=6; break; }
						case 6:{ x-=11; y-=6; break; }
						case 7:{ x-=1; y-=11; break; }
						case 8:{ x+=4; y-=5;  break; }
					} break; }
					case 8:{    switch(Player.facing){
						case 1:{ x+=5; y-=4;   break; }
						case 2:{ x+=4; y+=6;   break; }
						case 3:{ x+=1; y+=6;   break; }
						case 4:{ x-=14; y+=15; break; }
						case 5:{ x-=15; y+=7;  break; }
						case 6:{ x-=13; y+=5;  break; }
						case 7:{ x-=10; y-=4;  break; }
						case 8:{ x-=1; y-=7;   break; }
					} break; }
				}
				
				Player.attackBounds.center.x = x;
				Player.attackBounds.center.y = y;
	}
	
	private static Graphics2D copy; private static AffineTransform transform = new AffineTransform();
	public final static void render(Graphics2D g2d){
		transform.setToRotation(-Utils._45 * (Game.rotation - 1), Player.center.x, Player.center.y); copy = (Graphics2D) g2d.create(); copy.transform(transform);
		
		// draw the base animation --- TODO: you will need to switch these around for other animations
		
		PlayerAttackAnimations.basePlayerAttack[Player.facing - 1].drawAnimation(copy, Player.pos.x - Player.width/2, Player.pos.y - Player.height/2);
		
		switch(currentWeapon) { /* will be the itemId of the current weapon */
			case CRUDE_AXE:     {  PlayerAttackAnimations.crudeAxeAttack[Player.facing - 1].drawAnimation(copy, Player.pos.x - Player.width/2, Player.pos.y - Player.height/2);    break;  }
			case CRUDE_PICKAXE: {  PlayerAttackAnimations.crudePickAxeAttack[Player.facing -1].drawAnimation(copy, Player.pos.x - Player.width/2, Player.pos.y - Player.height/2); break;  }
			case GOBLIN_SPEAR:  {  PlayerAttackAnimations.goblinSpearAttack[Player.facing - 1].drawAnimation(copy, Player.pos.x - Player.width/2, Player.pos.y - Player.height/2); break;  }
 		}
		
		if(Game.drawPlayerAttackBounds){
			g2d.setColor(Color.red);
			g2d.drawOval((int)(Player.attackBounds.center.x-Player.attackBounds.radius), (int)(Player.attackBounds.center.y-Player.attackBounds.radius), (int)Player.attackBounds.radius*2, (int)Player.attackBounds.radius*2);
		}
		copy.dispose(); copy = null;
	}
	
	private static final void tickAnimations() {
		for(final Animation a : PlayerAttackAnimations.basePlayerAttack) a.runAnimation();

		switch(currentWeapon) {
			case CRUDE_AXE: 	{ for(final Animation a : PlayerAttackAnimations.crudeAxeAttack) 	 a.playAnimationOnce(); break; }
			case CRUDE_PICKAXE: { for(final Animation a : PlayerAttackAnimations.crudePickAxeAttack) a.playAnimationOnce(); break; }
			case GOBLIN_SPEAR:  { for(final Animation a : PlayerAttackAnimations.goblinSpearAttack)  a.playAnimationOnce(); break; }
		}
	}
	
	private static final int ATTACK_BOUNDS_RADIUS = 7;
	private final static float calcWeaponDamage() {
		// change based on weapon
		Player.attackBounds.radius = ATTACK_BOUNDS_RADIUS;
		
		switch(Player.equipmentSlots[HAND_SLOT].itemId) {
			case CRUDE_AXE: 	return 5; // crude axe
			case CRUDE_PICKAXE: return 6; // crude pickaxe
			case GOBLIN_SPEAR:  return 6.75f; // goblin spear
			default: return 0;
		}
	}
	
	private final static int calcAnimationType(){
		timer = 28; /* needs to be the speed * number of frames in animation */
		return Player.equipmentSlots[HAND_SLOT].itemId;
	}
}
