package gameplay.match;

import engine.Engine;
import engine.gui.UIElement;
import engine.gui.UIElementText;
import engine.gui.UIInputList;
import engine.input.Button;
import engine.input.GamepadInput;
import engine.math.Vector3f;
import engine.object.Hitbox;
import engine.object.HorizontalProgressBar;
import engine.object.ObjectGl;
import engine.object.Sprite;
import gameplay.Characters.Blue.CharacterBlue;
import gameplay.actions.Attack;
import gameplay.actions.attackPart;
import gameplay.actions.Throw;
import gameplay.actions.ThrowPart;
import gameplay.entities.Status;
import gameplay.frames.Frame;
import gameplay.hitboxes.*;
import gameplay.input.InputBuffer;
import gameplay.entities.Character;
import gameplay.input.Inputs;
import gameplay.input.ButtonIG;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.system.CallbackI;

import static org.lwjgl.glfw.GLFW.*;

/**
 * Main class that describes the base structure of the match, with characters, timer and such
 * @author Victor Azra
 *
 */
public class match {

	/**
	 * the number of inputs read for each character, a.k.a. for how many frames the inputs are saved in memory.
	 */
	private static final int inputBufferSize = 120;

	/**
	 * the level of the "ground", used to determine if a character is in the air or not.
	 */
	private static final int groundLevel = 180;
	
	private static int timer;
	private static InputBuffer inputsP1, inputsP2;
	private static int roundsWonP1=0, roundsWonP2=0;
	private static Character p1, p2; //characters of player 1 and 2

	private static long timeStamp1;
	private static long timeStamp2;
	private static int frameCount;
	private static int oldPosXp1;
	private static int oldPosXp2;
	private static int oldPosYp1;
	private static int oldPosYp2;
	private static GamepadInput gamepad1 = null;
	private static GamepadInput gamepad2 = null;

	// GUI / HUD ?
	private static UIElementText coordP1;
	private static UIElementText coordP2;
	private static UIElement healthBarP1;
	private static UIElement healthBarP2;
	private static HorizontalProgressBar healthBarP1Obj;
	private static HorizontalProgressBar healthBarP2Obj;
	private static UIElementText timerUI;
	private static UIElementText fpsCounter;
	private static UIInputList inputListP1;

	// Debug
	public static boolean showP1Hitbox = false; // TODO modifier pour le rendre activable
	public static boolean showP2Hitbox = false;
	private static List<Hitbox> listHitboxObj = new ArrayList<>();
	private static float slowFactor = 1f;
	private static long timeStampFpsCounter;
	private static int frameCounter;

	private static Sprite objP1,objP2;
	private static Engine engine;
	private static Frame f;
	private static int acCode = 0;
	private static int height, width;
	private static boolean roundP1=false ;

	/**
	 * Starts a new round, by placing the timer back at base value, characters back at full hp and such.
	 */
	private static void startNewRound() {
		timer = 99;
		inputsP1 = new InputBuffer(inputBufferSize);
		inputsP2 = new InputBuffer(inputBufferSize);
		p1.setPos(-750, groundLevel); //TODO : change to better values if needed
		p2.setPos((int) (750 - objP2.getWidth() * objP2.getScalingFactor()), groundLevel); //TODO : change to better values if needed
		p1.setCurrentHP(p1.getMaxHP());
		p2.setCurrentHP(p2.getMaxHP());
		objP1.translate(new Vector3f(p1.getPosX(),p1.getPosY(),0));
		objP2.translate(new Vector3f(p2.getPosX(),p2.getPosY(),0));
		// TODO meilleur implémentation possible
		objP1.getShadow().translate(new Vector3f(0f,p1.getPosY(),0));
		objP2.getShadow().translate(new Vector3f(0f,p2.getPosY(),0));
		// Crée l'InputList
		inputListP1 = new UIInputList(inputsP1, 10f, 0f, 0.85f, 110f, engine);
		engine.add_uiElement(inputListP1);
		inputListP1.init();
	}

	/**
	 * Ends the round.
	 * Used for playing animations and such.
	 * TODO : Implement this once we know what to do.
	 */
	private static void endRound() {
		
		if(roundP1) {
			System.out.println("P1 won the round");
		}
		else {
			System.out.println("P2 won the round");
		}
	}

	/**
	 * Ends the match.
	 * Used for playing animations and such.
	 * TODO : Implement this once we know what to do.
	 */
	private static void endMatch() {
		if (roundsWonP1 > roundsWonP2 ) {System.out.println("P1 won the match");}
		else {System.out.println("P1 won the match");}
		 GLFW.glfwSetWindowShouldClose(Engine.getWindow(), true);
	}
	public static void parse() throws FileNotFoundException {

		JSONParser jsonP = new JSONParser();
		try {
			JSONObject jsonO = (JSONObject) jsonP.parse(new FileReader("game.set"));
			JSONArray game = (JSONArray) jsonO.get("game");
			JSONObject settings = (JSONObject) game.get(0);

			height = Integer.parseInt((String) settings.get("height"));
			width = Integer.parseInt((String) settings.get("width"));
		} catch (ParseException | IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws Exception {
		parse();
		engine = new Engine(width, height, new Vector3f(4.0f, 3.0f));
		engine.init();

		boolean Joystick1Present = glfwJoystickPresent(GLFW_JOYSTICK_1);
		boolean Joystick2Present = glfwJoystickPresent(GLFW_JOYSTICK_2);

		String path = "textures/Sprite_sans_grille_9comp.png";
		String pathToBG = "textures/arena1.png";

		ObjectGl background = new ObjectGl(0f,1f,1f,2.5f, pathToBG, null);
		background.setTextureWrap(0, 0, 1914f, 701f);
		background.translate(new Vector3f(-1350f, 1000f, 0f));
		engine.add_objectGl(background);

		p1 = CharacterBlue.generateCharBlue();
		p2 = CharacterBlue.generateCharBlue();
		objP1 = new Sprite(14f, 5f, path, null);
		objP2 = new Sprite(15f, 5f, path, new Vector3f(1.0f,0.0f,1.0f));
		engine.add_objectGl(objP1);
		engine.add_objectGl(objP2);

		f = p1.getCurrentframe();
		objP1.setTextureWrap(f.getSprite()[0], f.getSprite()[1], f.getSprite()[2], f.getSprite()[3]);

		f = p2.getCurrentframe();
		objP2.setTextureWrap(f.getSprite()[0], f.getSprite()[1], f.getSprite()[2], f.getSprite()[3]);
		objP2.flipTextureWrapH();

		//Création des ombres
		objP1.setShadow();
		engine.add_objectGl(objP1.getShadow());
		objP2.setShadow();
		engine.add_objectGl(objP2.getShadow());

		if(Joystick1Present) {
			gamepad1 = new GamepadInput(GLFW_JOYSTICK_1);
			gamepad1.inputRefresh();
			System.out.println("P1 Controller: " + gamepad1.getGamepadName());
		}
		if(Joystick2Present) {
			gamepad2 = new GamepadInput(GLFW_JOYSTICK_2);
			gamepad2.inputRefresh();
			System.out.println("P2 Controller: " + gamepad2.getGamepadName());
		}

		/*
		GUI Setup
		 */
		coordP1 = new UIElementText("objP1: " + objP1.getXPos() + ":" + objP1.getYPos() + " P1: " + p1.getPosX() +":" + p1.getPosY(), 5f, 0f, 0.15f, 70f, engine);
		coordP1.setBackground(new Vector3f(0f,0f,0f));
		engine.add_uiElement(coordP1);
		coordP2 = new UIElementText("objP2: " + objP2.getXPos() + ":" + objP2.getYPos() + " P1: " + p2.getPosX() +":" + p2.getPosY(), 5f, 0f, 0.1f, 70f, engine);
		coordP2.setBackground(new Vector3f(0f,0f,0f));
		engine.add_uiElement(coordP2);
		// Barre de vie
		healthBarP1Obj = new HorizontalProgressBar(80f, 8.5f, 0.4f, 100f, p1.getCurrentHP(), p1.getMaxHP(), false);
		healthBarP1Obj.setShader("shaders/StylishShaders/BasicNoTexVert.glsl", "shaders/StylishShaders/HorizontalProgressBarGradientSquareFrag.glsl");
		healthBarP1Obj.setUseHeight(true);
		healthBarP1Obj.useTime = true;
		healthBarP1 = new UIElement(healthBarP1Obj, 0.0138f, 0.980f, engine);
		healthBarP2Obj = new HorizontalProgressBar(80f, 8.5f, 0.4f, 100f, p2.getCurrentHP(), p2.getMaxHP(), true);
		healthBarP2Obj.setShader("shaders/StylishShaders/BasicNoTexVert.glsl", "shaders/StylishShaders/HorizontalProgressBarGradientSquareFrag.glsl");
		healthBarP2Obj.setUseHeight(true);
		healthBarP2Obj.useTime = true;
		healthBarP2 = new UIElement(healthBarP2Obj, 0.563f, 0.980f, engine);
		engine.add_uiElement(healthBarP1);
		engine.add_uiElement(healthBarP2);
		// Habillage barre de vie
        ObjectGl healthBarP1Hab = new ObjectGl(81f, 1f, 1f, 1f, "textures/health_bar.png", null);
        healthBarP1Hab.setTextureWrap(0,0, 883, 158);
        UIElement healthBarP1HabUI = new UIElement(healthBarP1Hab, 0.005f, 0.995f, engine);
        engine.add_uiElement(healthBarP1HabUI);
		ObjectGl healthBarP2Hab = new ObjectGl(81f, 1f, 1f, 1f, "textures/health_bar.png", null);
		healthBarP2Hab.setTextureWrap(0,0, 883, 158);
		healthBarP2Hab.flipTextureWrapH();
		UIElement healthBarP2HabUI = new UIElement(healthBarP2Hab, 0.555f, 0.995f, engine);
		engine.add_uiElement(healthBarP2HabUI);
		// Timer
		timerUI = new UIElementText(timer + "", 10f, 0.453f, 0.995f, 85f, engine);
		engine.add_uiElement(timerUI);
		// FPS counter
		fpsCounter = new UIElementText("Boulevard Combattant", 5f, 0f, 0.04f, 100f, engine);
		fpsCounter.setBackground(new Vector3f(0f,0f,0f));
		engine.add_uiElement(fpsCounter);
		timeStampFpsCounter = System.currentTimeMillis();
		//SetTracking
		engine.setCameraTrackingSF3ThirdStrike(objP1, objP2);

		while(frameCount < 5940 && engine.getRunning()) {
			ac(acCode);
			if(engine.shouldClose()) engine.setRunning(false);
		}

		engine.setRunning(false);


	}

	private static void ac(int i) {
//		System.out.println(i);
		switch (i) {

			//initiate a round
			case 0 :
				startNewRound();
				timeStamp1 = System.currentTimeMillis();
				frameCount = 0;
				acCode = 10;
				break;

				//checks if one or both of the chars are out of health
			case 10:
				oldPosXp1 = p1.getPosX();
				oldPosXp2 = p2.getPosX();
				oldPosYp1 = p1.getPosY();
				oldPosYp2 = p2.getPosY();

				if(p1.getCurrentHP() <= 0 && p2.getCurrentHP() <= 0) { acCode = 11;}
				else if(p1.getCurrentHP() <= 0) { acCode = 12;}
				else if(p2.getCurrentHP() <= 0) { acCode = 13;}
				else { acCode = 20;}
				break;

				//end round
			case 11:
				endRound();
				if(roundsWonP1 >= 2 || roundsWonP2 >= 2) { endMatch();} //TODO : will probably need to specify more
				acCode = 0;
				break;

				//if p1 is at 0 health
			case 12:
				roundsWonP2++;
				roundP1 =false;
				acCode = 11;
				break;

				//if p2 is at 0 health
			case 13:
				roundsWonP1++;
				roundP1=true;
				acCode = 11;
				break;

				//read both players inputs
			case 20:
				if (glfwJoystickPresent(GLFW_JOYSTICK_1)) {
					gamepad1.inputRefresh();
					inputsP1.recordInputsFromGamepad(gamepad1, p1.getPosX() < p2.getPosX());
					handleInputs(p1, inputsP1);
				}
				if (glfwJoystickPresent(GLFW_JOYSTICK_2)) {
					gamepad2.inputRefresh();
					inputsP2.recordInputsFromGamepad(gamepad2, p2.getPosX() <= p1.getPosX());	
					handleInputs(p2, inputsP2);
				}
				
				//
				acCode = 21;
				break;

				//start of the handling of hitboxes
			case 21:
				try {
					handleThrows(p1, p2);
				} catch (IndexOutOfBoundsException e) {}
				try {
					handleHits(p1, p2, inputsP2);
					handleHits(p2, p1, inputsP1);
				}catch (IndexOutOfBoundsException e) {};
				acCode = 22;
				break;

				//Update of the current frame of each character
			case 22:
				if(p1.getCurrentframe().islastFrameOfHit()) {
					p1.removeFirstAttackPart();
				}
				if(p2.getCurrentframe().islastFrameOfHit()) {
					p2.removeFirstAttackPart();
				}

				nextFrame(p1,inputsP1);
				nextFrame(p2,inputsP2);

				boolean p1LooksRight = p1.getPosX() < p2.getPosX();
				updatePos(p1,p1LooksRight);
				updatePos(p2,!p1LooksRight);

				if(p1LooksRight) {

					f = p1.getCurrentframe();
					objP1.setTextureWrap(f.getSprite()[0], f.getSprite()[1], f.getSprite()[2], f.getSprite()[3]);
					objP1.translate(new Vector3f(p1.getPosX()-oldPosXp1,p1.getPosY()-oldPosYp1,0));

					f = p2.getCurrentframe();
					objP2.setTextureWrap(f.getSprite()[0], f.getSprite()[1], f.getSprite()[2], f.getSprite()[3]);
					objP2.translate(new Vector3f(p2.getPosX() - oldPosXp2,p2.getPosY()-oldPosYp2,0));

					Frame nf = new Frame();
					nf.clone(p2.getCurrentframe());
					nf.invertHitBoxes();
					p2.setCurrentFrame(nf);
					objP2.flipTextureWrapH();

				} else {

					Frame p1f = p1.getCurrentframe();
					objP1.setTextureWrap(p1f.getSprite()[0], p1f.getSprite()[1], p1f.getSprite()[2], p1f.getSprite()[3]);
					objP1.translate(new Vector3f(p1.getPosX()-oldPosXp1,p1.getPosY()-oldPosYp1,0));

					Frame p2f = p2.getCurrentframe();
					objP2.setTextureWrap(p2f.getSprite()[0], p2f.getSprite()[1], p2f.getSprite()[2], p2f.getSprite()[3]);
					objP2.translate(new Vector3f(p2.getPosX()-oldPosXp2,p2.getPosY()-oldPosYp2,0));

					Frame nf = new Frame();
					nf.clone(p1.getCurrentframe());
					nf.invertHitBoxes();
					p1.setCurrentFrame(nf);
					objP1.flipTextureWrapH();
				}


				// Debug Hitbox Management
				removeHitboxEngine();
				if (showP1Hitbox){
					addHitbox(p1);
				} if (showP2Hitbox) {
					addHitbox(p2);
				}
				addHitboxEngine();

				engine.update();
				engine.render();
				acCode = 23;
				break;

				//Waits the end of 1/60th of a second since start of frame then loops back to  start
			case 23:
				// GUI update here
				coordP1.setText("objP1: " + objP1.getXPos() + ":" + objP1.getYPos() + " P1: " + p1.getPosX() +":" + p1.getPosY());
				coordP2.setText("objP2: " + objP2.getXPos() + ":" + objP2.getYPos() + " P2: " + p2.getPosX() +":" + p2.getPosY());
				healthBarP1Obj.setCurrent(p1.getCurrentHP()); healthBarP1Obj.setMax(p1.getMaxHP());
				healthBarP2Obj.setCurrent(p2.getCurrentHP()); healthBarP2Obj.setMax(p2.getMaxHP());
				timerUI.setText(timer + "");
				// Tracking update
				engine.cameraTracking();

				timer = 99 - frameCount/60;

				timeStamp2 = System.currentTimeMillis();
				while(timeStamp2-timeStamp1<(1000/(60 * slowFactor))) {
					timeStamp2 = System.currentTimeMillis();
				}

				frameCounter++;
				if (System.currentTimeMillis() - timeStampFpsCounter >= 1000){
					fpsCounter.setText("FPS: " + frameCounter);
					frameCounter = 0;
					timeStampFpsCounter = System.currentTimeMillis();
				}

				frameCount++;
				timeStamp1 = System.currentTimeMillis();
				acCode=10;
				break;

		}
	}

	/**
	 * Will handle the inputs recorder and have the character do a corresponding action if possible
	 * Order of priority is Throw > Special > Normal > Jump > Dash > Crouch > Move > do nothing
	 * @param c
	 * @param input
	 */
	private static void handleInputs(Character c, InputBuffer input) {
		Inputs latestIn = input.getLatestInputs();
		boolean actionSet = false;
		if(latestIn.containsButtonTab(c.getNormalthrow().getCommand()[0]) && c.getCurrentframe().isNormalCancellable()) {
			c.clearNextFrames();
			c.addNextFramesList(c.getNormalthrow().getFrame());
			actionSet = true;
		} else {
			int atkCount = 0;
			//do an attack if possible
			while(atkCount < c.getAttacks().length && !actionSet) {
				Attack atk = c.getAttacks()[atkCount];
				boolean attackIsPossible = input.commandRecognized(atk.getCommand())
						&& atk.getRequiredStatus().equals(c.getStatus())
						&& ((atk.isSpecial() && c.getCurrentframe().isSpecialCancellable())
							|| (!atk.isSpecial() && c.getCurrentframe().isNormalCancellable()));
				if(attackIsPossible) {
					c.clearNextFrames();
					c.addNextFramesList(atk.getFrame());
					c.setAttackPartsArray(atk.getParts());
					actionSet = true;
				}
				atkCount++;
			}
			if(c.getCurrentframe().isJumpCancellable() && !actionSet) {
				if (input.commandRecognized(c.getForwardJump().getCommand())) {
					c.clearNextFrames();
					c.addNextFramesList(c.getForwardJump().getFrame());
					actionSet = true;
					c.setStatus(Status.JUMPING);
				} else if (input.commandRecognized(c.getBackJump().getCommand())) {
					c.clearNextFrames();
					c.addNextFramesList(c.getBackJump().getFrame());
					actionSet = true;
					c.setStatus(Status.JUMPING);
				} else if (input.commandRecognized(c.getNeutralJump().getCommand())) {
					c.clearNextFrames();
					c.addNextFramesList(c.getNeutralJump().getFrame());
					actionSet = true;
					c.setStatus(Status.JUMPING);
				}
			}
			if(c.getCurrentframe().isDashCancellable() && !actionSet) {
				if (input.commandRecognized(c.getForwardDash().getCommand())) {
					c.clearNextFrames();
					c.addNextFramesList(c.getForwardDash().getFrame());
					actionSet = true;
				} else if (input.commandRecognized(c.getBackDash().getCommand())) {
					c.clearNextFrames();
					c.addNextFramesList(c.getBackDash().getFrame());
					actionSet = true;
				}
			}
			if(c.getCurrentframe().isMoveCancellable() && !actionSet) {
				if(input.getLatestInputs().containsInput(ButtonIG.DOWN)) {
					c.clearNextFrames();
					c.addNextFrames(c.getDefaultCrouchingFrames());
				} else if(input.getLatestInputs().containsInput(ButtonIG.BACK)) {
					c.clearNextFrames();
					c.addNextFrames(c.getBackWalkFrames());
				} if(input.getLatestInputs().containsInput(ButtonIG.FORWARD)) {
					c.clearNextFrames();
					c.addNextFrames(c.getForwardWalkFrames());
				}
			}

		}


	}

	private static void handleThrows(Character p1, Character p2) {
		ArrayList<Active_throw_Hitbox> activeP1 = new ArrayList<>(p1.getCurrentframe().getActThrowHitBox());
		ArrayList<Passive_throw_HitBox> passiveP2 = new ArrayList<>(p2.getCurrentframe().getPassThrowHitBox());
		ArrayList<ThrowPart> tP = new ArrayList<>(p1.getNextThrowParts());
		ThrowPart hit = new ThrowPart(tP.get(0).getFrames());
		hit.clone(tP.get(0));
		for(Active_throw_Hitbox atH : activeP1) {
			for(Passive_throw_HitBox ptH : passiveP2) {
				if(!hit.hasHit()){
					boolean p1LooksRight =  p1.getPosX() < p2.getPosX();
					boolean touchH = (p1LooksRight && (atH.getPosX()+p1.getPosX()+ atH.getSize_x() > ptH.getPosX()+p2.getPosX()+ptH.getSize_x())
													&& (atH.getPosX() < ptH.getPosX()))
									|| (!p1LooksRight && (atH.getPosX()+p1.getPosX()+ atH.getSize_x() < ptH.getPosX()+p2.getPosX()+ptH.getSize_x())
														&& (atH.getPosX() > ptH.getPosX()));

					boolean touchV = (atH.getPosY() - atH.getSize_y() < ptH.getPosY()) && (atH.getPosY() > ptH.getPosY() - ptH.getSize_y());
					if(touchH && touchV) {
						hit.setHasHit(true);
						tP.set(0,hit);
					}

				}
			}
		}
	}

	/**
	 * handles the if the first character hits the second one
	 * @param p1 the character whose hits to handle
	 * @param p2 the character who is or isn't hit
	 * @param inputsP2 the inputs of the player 2, used to see if they're guarding
	 */
	private static void handleHits(Character p1, Character p2, InputBuffer inputsP2) {
		ArrayList<Active_HitBox> activeP1 = new ArrayList<>(p1.getCurrentframe().getActHitBox());
		ArrayList<Passive_HitBox> passiveP2 = new ArrayList<>(p2.getCurrentframe().getPassHitBox());
		ArrayList<attackPart> aP = new ArrayList<>(p1.getNextAttackParts());
		attackPart hit = new attackPart(aP.get(0).getFrames());
		hit.clone(aP.get(0));
		for(Active_HitBox aH : activeP1) {
			for(Passive_HitBox pH : passiveP2) {
				if(!hit.hasHit()){
					boolean p1LooksRight =  p1.getPosX() < p2.getPosX();
					boolean touchH = (p1LooksRight && (aH.getPosX()+p1.getPosX()+ aH.getSize_x() > pH.getPosX()+p2.getPosX()+pH.getSize_x())
													&& (aH.getPosX() < pH.getPosX()))
									|| (!p1LooksRight && (aH.getPosX()+p1.getPosX()+ aH.getSize_x() < pH.getPosX()+p2.getPosX()+pH.getSize_x())
														&& (aH.getPosX() > pH.getPosX()));

					boolean touchV = (aH.getPosY() - aH.getSize_y() < pH.getPosY()) && (aH.getPosY() > pH.getPosY() - pH.getSize_y());
					if(touchH && touchV) {
						getHit(p2,hit,inputsP2.getLatestInputs());
						hit.setHasHit(true);
						aP.set(0,hit);
					}

				}
			}
		}
	}

	/**
	 * Handles a character getting hit by an attack part.
	 * @param c the character that's getting hit
	 * @param aP the attackPart hitting the character
	 * @param inputs the current inputs of c
	 */
	private static void getHit(Character c, attackPart aP, Inputs inputs) {
		boolean getsHit = (c.getStatus() == Status.JUMPING) || (c.getStatus() == Status.HITINAIR) || (c.getStatus() == Status.FALLING)
					|| inputs.containsInput(ButtonIG.BACK)
					|| (aP.isLow() && !inputs.containsInput(ButtonIG.DOWN))
					|| (aP.isOverHead() && inputs.containsInput(ButtonIG.DOWN));
		Frame[] nextFrames;
		c.clearNextFrames();
		if(getsHit) {
			switch (c.getStatus()) {
				case JUMPING: case HITINAIR: case FALLING :

					nextFrames = new Frame[20];
					for(int i = 0; i < nextFrames.length; i++) {
						nextFrames[i] = c.getHitInAirFrame();
					}
					c.addNextFrames(nextFrames);
					c.reduceHP(aP.getDamage());
					c.setStatus(Status.HITINAIR);
					break;
				default :
					c.clearNextFrames();
					if(!aP.knocksDown()) {
					nextFrames = new Frame[aP.getHitstun()];
						if (inputs.containsInput(ButtonIG.DOWN)) {
							for (int i = 0; i < nextFrames.length; i++) {
								nextFrames[i] = c.getCrouchHitFrame();
							}
						} else {
							for (int i = 0; i < nextFrames.length; i++) {
								nextFrames[i] = c.getStandHitFrame();
							}
						}
					} else {
						nextFrames = new Frame[c.getKnockedDownFrames().length];
						for (int i = 0; i < nextFrames.length; i++) {
							nextFrames[i] = c.getKnockedDownFrames()[i];
						}
					}
					c.addNextFrames(nextFrames);
					c.reduceHP(aP.getDamage());
					break;
			}
		} else {
			nextFrames = new Frame[aP.getBlockstun()];
			if(inputs.containsInput(ButtonIG.DOWN)) {
				for(int i = 0; i < nextFrames.length; i++) {
					nextFrames[i] = c.getCrouchGuardFrame();
				}
			} else {
				for(int i = 0; i < nextFrames.length; i++) {
					nextFrames[i] = c.getStandGuardFrame();
				}
			}
			c.reduceHP(aP.getChipDamage());
			c.addNextFrames(nextFrames);
		}
	}

	/**
	 * Sets the character to its next logical frame
	 * @param c the character
	 * @param in the input buffer corresponding to the character
	 */
	private static void nextFrame(Character c, InputBuffer in) {
		try {
		//if(!c.getFrames().getNextframe().equals(null)){
			c.goToNextFrames();
		} catch (NullPointerException e) {
			switch(c.getStatus()) {
				case FALLING:case HITINAIR:
					if(c.getPosY() > groundLevel){
						c.setStatus(Status.FALLING);
						c.setCurrentFrame(c.getFallingframe());
					} else {
						c.setPos(c.getPosX(),groundLevel);
						c.setStatus(Status.KNOCKEDDOWN);
						c.addNextFrames(c.getKnockedDownFrames());
					}
					break;
				default:
					c.setStatus(Status.NORMAL);
					if(in.getLatestInputs().containsInput(ButtonIG.DOWN)) {
						c.addNextFrames(c.getDefaultCrouchingFrames());
					}
					else {
						c.addNextFrames(c.getDefaultStandingFrames());
					}
					break;
			}
		}
	}

	private static void updatePos(Character c, boolean looksRight) {
		if(looksRight) {c.setPos((int)(c.getPosX()+c.getCurrentframe().getMove_x()),(int)(c.getPosY()+c.getCurrentframe().getMove_y()));}
		else {c.setPos((int)(c.getPosX()-c.getCurrentframe().getMove_x()),(int)(c.getPosY()+c.getCurrentframe().getMove_y()));}
	}

	/*
	HITBOX DEBUG METHOD
	 */


	private static void addHitboxEngine(){
		for (ObjectGl obj : listHitboxObj){
			engine.add_objectGl(obj);
		}
	}

	private static void removeHitboxEngine(){
		for (ObjectGl obj : listHitboxObj){ // Il faut le cast en ObjectGl
			engine.remove_objectGl(obj);
		}
		listHitboxObj = new ArrayList<>();
	}

	private static void addHitbox(Character c){
		Frame f = c.getCurrentframe();
		Vector3f posC = new Vector3f(c.getPosX(), c.getPosY(), 100f);
		Vector3f darkBlue = new Vector3f(8f/255f, 29f/255f, 153f/255f);
		Vector3f green = new Vector3f(33f/255f, 135f/255f, 12f/255f);
		Vector3f red = new Vector3f(120f/255f, 19f/255f, 12f/255f);
		Vector3f lightBlue = new Vector3f(32f/255f, 103f/255f, 201f/255f);
		Vector3f purple = new Vector3f(116f/255f, 52f/255f, 235f/255f);
		float offset = 0;
		Push_HitBox pushHitBox = f.getPushHitBox();
		if (pushHitBox != null){
			Hitbox hb = new Hitbox(100f + offset, pushHitBox.getSize_x(), pushHitBox.getSize_y(), 1f, purple);
			hb.translate(new Vector3f(pushHitBox.getPosX() + posC.x, pushHitBox.getPosY() + posC.y));
			listHitboxObj.add(hb);
			offset+=0.1f;
		}
		for (Passive_HitBox passive_hitBox : f.getPassHitBox()){
			Hitbox hb = new Hitbox(100f + offset, passive_hitBox.getSize_x(), passive_hitBox.getSize_y(), 1f, darkBlue);
			hb.translate(new Vector3f(passive_hitBox.getPosX() + posC.x, passive_hitBox.getPosY() + posC.y));
			listHitboxObj.add(hb);
			offset+=0.1f;
		}
		for (Passive_throw_HitBox passive_throw_hitBox : f.getPassThrowHitBox()){
			Hitbox hb = new Hitbox(100f + offset, passive_throw_hitBox.getSize_x(), passive_throw_hitBox.getSize_y(), 1f, green);
			hb.translate(new Vector3f(passive_throw_hitBox.getPosX() + posC.x, passive_throw_hitBox.getPosY() + posC.y));
			listHitboxObj.add(hb);
			offset+=0.1f;
		}
		for (Active_HitBox active_hitBox : f.getActHitBox()){
			Hitbox hb = new Hitbox(100f + offset, active_hitBox.getSize_x(), active_hitBox.getSize_y(), 1f, red);
			hb.translate(new Vector3f(active_hitBox.getPosX() + posC.x, active_hitBox.getPosY() + posC.y));
			listHitboxObj.add(hb);
			offset+=0.1f;
		}
		for (Active_throw_Hitbox active_throw_hitbox : f.getActThrowHitBox()){
			Hitbox hb = new Hitbox(100f + offset, active_throw_hitbox.getSize_x(), active_throw_hitbox.getSize_y(), 1f, lightBlue);
			hb.translate(new Vector3f(active_throw_hitbox.getPosX() + posC.x, active_throw_hitbox.getPosY() + posC.y));
			listHitboxObj.add(hb);
			offset+=1;
		}
	}

}