package com.xinqihd.sns.gameserver.ai;

import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

import org.apache.commons.math.util.FastMath;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xinqihd.sns.gameserver.battle.BuffToolIndex;
import com.xinqihd.sns.gameserver.chat.ChatType;
import com.xinqihd.sns.gameserver.entity.user.User;
import com.xinqihd.sns.gameserver.geom.SimplePoint;
import com.xinqihd.sns.gameserver.proto.XinqiBceAskRoundOver.BceAskRoundOver;
import com.xinqihd.sns.gameserver.proto.XinqiBceChat.BceChat;
import com.xinqihd.sns.gameserver.proto.XinqiBceRoleAttack.BceRoleAttack;
import com.xinqihd.sns.gameserver.proto.XinqiBceRoleUseTool.BceRoleUseTool;
import com.xinqihd.sns.gameserver.proto.XinqiBseRoundStart.BseRoundStart;
import com.xinqihd.sns.gameserver.script.function.UserCalculator;
import com.xinqihd.sns.gameserver.util.MathUtil;

public class AIAction {
	
	private static final Logger logger = LoggerFactory.getLogger(AIAction.class);

	/**
	 * Determine the role attack
	 * @param aiUser
	 * @param serverIoSession
	 * @param userAngle
	 * @param userPower
	 * @param userHitPoint
	 * @param lastAngle
	 * @param lastPower
	 * @param lastHitPoint
	 */
	public static final BceRoleAttack roleAttack(User aiUser, IoSession serverIoSession, 
			int wind, int myX, int myY, int targetX, int targetY) {
		//Get last round data.
		String      mapId       = (String)aiUser.getUserData(AIManager.BATTLE_MAP_ID);
		Integer     lastAngle   = (Integer)aiUser.getUserData(AIManager.BATTLE_LAST_ROUND_ANGLE);
		Integer     lastPower   = (Integer)aiUser.getUserData(AIManager.BATTLE_LAST_ROUND_POWER);
		SimplePoint lastHitPoint    = (SimplePoint)aiUser.getUserData(AIManager.BATTLE_LAST_ROUND_HITPOINT);
		SimplePoint lastTargetPoint = (SimplePoint)aiUser.getUserData(AIManager.BATTLE_LAST_ROUND_TARGETPOINT);
		Integer     userAngle   = (Integer)aiUser.getUserData(AIManager.BATTLE_USER_ANGLE);
		Integer     userPower   = (Integer)aiUser.getUserData(AIManager.BATTLE_USER_POWER);
		SimplePoint userHitPoint    = (SimplePoint)aiUser.getUserData(AIManager.BATTLE_USER_HITPOINT);
		Boolean     userHurtAI  = (Boolean)aiUser.getUserData(AIManager.BATTLE_USER_HURTAI);
		Integer     totalRoundNotHit   = (Integer)aiUser.getUserData(AIManager.BATTLE_TOTAL_ROUND_NOT_HIT);
		
		int angle = 30, power = 50;
		CHECK: {
			/**
			 * 不使用用户的数值
			 */
			/*
			if ( userHurtAI != null && userHurtAI.booleanValue() ) {
				angle = 180 - userAngle;
				power = userPower;
				aiUser.putUserData(AIManager.BATTLE_USER_HURTAI, null);
				break CHECK;
			}
			*/
			if ( lastAngle != null ) {
				angle = lastAngle.intValue();
				//当玩家位置改变时,调整方向
				if ( myX < targetX && angle > 90 ) {
					angle = 180 - angle;
					logger.debug("当玩家位置改变时,调整方向. angle:{}, power:{}", angle, power);
				} else if ( myX > targetX && angle < 90 ) {
					angle = 180 - angle;
					logger.debug("当玩家位置改变时,调整方向. angle:{}, power:{}", angle, power);
				}
			} else {
			  //第一次射击,计算起始力度
				/**
				 * 当敌人在X轴距离我较近,但Y轴距离我较高时,使用大力度平直射击
				 */
				if ( myY - targetY >= 200 ) {
					double a = Math.abs(myY - targetY);
					double b = Math.abs(myX - targetX);
					double c = FastMath.sqrt(a*a+b*b);
					//double ang = FastMath.asin(a/c)*180/Math.PI;
					double ang = 80;
					if ( myX > targetX ) {
						ang = 180 - ang;
					}
					angle = (int)ang;
					power = calculatePower(angle, targetX, targetY, 0);
					logger.debug("当敌人在X轴距离我较近,但Y轴距离我较高时,使用大力度平直射击. angle:{}, power:{}", angle, power);
				} else {
					angle = calculateAngle(myX, myY, targetX, targetY);
					logger.debug("为第一次射击计算数值. angle:{}, power:{}", angle, power);
				}
			}
			if ( lastPower != null ) {
				power = lastPower.intValue();
				if ( lastTargetPoint != null ) {
					if ( lastTargetPoint.getX() < targetX - 20 || lastTargetPoint.getX() > targetX + 20 ) {
						//The target user moved.
						power = Math.abs(calculatePower(angle,
								Math.abs((myX-targetX)), 
								Math.abs((myY-targetY)), 
								wind));
						logger.debug("敌人在X轴移动超过20像素. angle:{}, power:{}", angle, power);
					} else if ( lastTargetPoint.getY() < targetY - 50 || lastTargetPoint.getY() > targetY + 50 ) {
						power = Math.abs(calculatePower(angle,
								Math.abs((myX-targetX)), 
								Math.abs((myY-targetY)), 
								wind));
						logger.debug("敌人在Y轴移动超过50像素. angle:{}, power:{}", angle, power);
					} else {
						//The target user does not move
						if ( lastHitPoint != null && lastHitPoint.getX() < targetX+60 && lastHitPoint.getX() > targetX-60 &&
								lastHitPoint.getY() < targetY+60 && lastHitPoint.getY() > targetY - 60 ) {
							logger.debug("Last power hit the user. use it");
							logger.debug("上一次的打击命中,继续保持. angle:{}, power:{}", angle, power);
						} else {
							if ( totalRoundNotHit == null ) {
								totalRoundNotHit = new Integer(1);
							} else {
								totalRoundNotHit = totalRoundNotHit.intValue() + 1;
							}
							boolean recalcuate = totalRoundNotHit.intValue() > 5;
							if ( recalcuate ) {
								//如果很久没有击中用户了,需要更换一下策略
								aiUser.putUserData(AIManager.BATTLE_LAST_ROUND_ANGLE, null);
								aiUser.putUserData(AIManager.BATTLE_LAST_ROUND_POWER, null);
								aiUser.putUserData(AIManager.BATTLE_TOTAL_ROUND_NOT_HIT, null);
								angle = 50 + (int)(MathUtil.nextDouble()*20); 
								if ( myX > targetX ) {
									angle = 180 - angle;
								}
								power = Math.abs(calculatePower(angle,
										Math.abs((myX-targetX)), 
										Math.abs((myY-targetY)), 
										wind));
								logger.debug("连续5回合没有命中了,重新计算. angle:{}, power:{}", angle, power);
							} else {
								//否则继续统计是否命中
								aiUser.putUserData(AIManager.BATTLE_TOTAL_ROUND_NOT_HIT, totalRoundNotHit);
								
								if ( lastHitPoint == null ) {
									//子弹出屏幕了, 计算起始力度
									aiUser.putUserData(AIManager.BATTLE_LAST_ROUND_ANGLE, null);
									aiUser.putUserData(AIManager.BATTLE_LAST_ROUND_POWER, null);
									/**
									 * 当敌人在X轴距离我较近,但Y轴距离我较高时,使用大力度平直射击
									 */
									if ( myY - targetY >= 200 ) {
										double a = Math.abs(myY - targetY);
										double b = Math.abs(myX - targetX);
										double c = FastMath.sqrt(a*a+b*b);
										double ang = FastMath.asin(a/c)*180/Math.PI;
										if ( myX > targetX ) {
											ang = 180 - (ang + 10);
										}
										angle = (int)ang;
										power = 90;
										logger.debug("当敌人在X轴距离我较近,但Y轴距离我较高时,使用大力度平直射击. angle:{}, power:{}", angle, power);
									} else {
										angle = calculateAngle(myX, myY, targetX, targetY);
										logger.debug("为第一次射击计算数值. angle:{}, power:{}", angle, power);
									}
								} else {
									Boolean aiHurtEnemy = (Boolean)aiUser.getUserData(AIManager.BATTLE_AI_HURTENEMY);
									if ( aiHurtEnemy == null || !aiHurtEnemy.booleanValue() ) {
										int distancex = lastHitPoint.getX() - targetX;
										if ( distancex < 0 && angle > 90 ) {
											power -= Math.abs(distancex)/40.0f;
											logger.debug("Last power is too high to hit. distance:{}",distancex);
										} else if ( distancex < 0 && angle < 90 ) {
											power += Math.abs(distancex)/40.0f;
											logger.debug("Last power is too low to hit. distance:{}",distancex);
										} else if ( distancex > 0 && angle > 90 ) {
											power += distancex/40.0f;
											logger.debug("Last power is too low to hit. distance:{}",distancex);
										} else if ( distancex > 0 && angle < 90 ) {
											power -= distancex/40.0f;
											logger.debug("Last power is too high to hit. distance:{}",distancex);
										}
										int distancey = lastHitPoint.getY() - targetY;
										if ( distancey > 200 ) {
											//玩家在AI的高点位置,之前的炮弹落点太低了
											power += 10;
										} else if ( distancey > 0 ) {
											//angle += distancey/60;
											power += distancey/30;
										}
									} else {
										logger.debug("AI已经命中目标,不需要再次调整. angle:{}, power:{}", angle, power);	
									}
								}
							}
							
							if ( power <= 0 ) {
								power = 10;
							}
							logger.debug("根据先前的打击情况进行调整. angle:{}, power:{}", angle, power);
						}
					}
				}
			} else {
				power = Math.abs(calculatePower(angle,
					Math.abs((myX-targetX)), 
					Math.abs((myY-targetY)), 
					wind));
				//稍减小一点力度
				if ( angle <= 40 || angle >= 160 ) {
					power -= 5;
				}
				logger.debug("上一回合没有力度值,重新计算. angle:{}, power:{}", angle, power);
			}	
			
		}
				
		logger.debug("AI my position:({},{}), target:({},{}), angle: {}, power: {}", 
				new Object[]{myX, myY, targetX, targetY, angle, power});
		aiUser.putUserData(AIManager.BATTLE_LAST_ROUND_ANGLE, angle);
		aiUser.putUserData(AIManager.BATTLE_LAST_ROUND_POWER, power);
		aiUser.putUserData(AIManager.BATTLE_LAST_ROUND_TARGETPOINT, new SimplePoint(targetX, targetY));
		aiUser.putUserData(AIManager.BATTLE_LAST_ROUND_HITPOINT, null);
		
		BceRoleAttack.Builder roleAttackBuilder = BceRoleAttack.newBuilder();
		roleAttackBuilder.setAngle(angle*1000);
		roleAttackBuilder.setAtkAngle(angle*1000);
		roleAttackBuilder.setPower(power);
		roleAttackBuilder.setUserx(myX);
		roleAttackBuilder.setUsery(myY);
		roleAttackBuilder.setDirection(0);
		BceRoleAttack attack = roleAttackBuilder.build();
		
		return attack;
	}

	/**
	 * @param myX
	 * @param myY
	 * @param targetX
	 * @param targetY
	 * @param power
	 * @return
	 */
	private static int calculateAngle(int myX, int myY, int targetX, int targetY) {
		int angle;
		if ( myY > targetY + 200 ) {
			//高抛
			angle = 55 + (int)(MathUtil.nextDouble()*20);
			logger.debug("玩家位置高于我200像素,使用高抛. angle:{}", angle);
		} else {
			//平射
			angle = 40 + (int)(MathUtil.nextDouble()*20);
			logger.debug("玩家位置在200像素内,使用平射. angle:{}", angle);
		}
		//纠正方向
		if ( myX > targetX ) {
			angle = 180 - angle;
		}
		return angle;
	}
	
	/**
	 * Random use a tool
	 */
	public static final void roleUseTool(User aiUser, IoSession serverIoSession, BseRoundStart roundStart) {
		//Use BuffTool
		if ( aiUser == null || aiUser.getSessionKey() == null ) return;
		final String aiUserSessionId = aiUser.getSessionKey().toString();
		if ( roundStart.getSessionId().equals(aiUserSessionId) ) {
			int aiUserIndex = 0;
			boolean loop = true;
			for ( int i=0; loop && i<roundStart.getUserIdCount(); i++ ) {
				String sessionId = roundStart.getUserId(i);
				
				if (aiUserSessionId.equals(sessionId)) {
					aiUserIndex = i;
					loop = false;
				}
			}
			final int blood = roundStart.getBlood(aiUserIndex);
			final int energy = roundStart.getEnergy(aiUserIndex);
			final int thew = roundStart.getStrength(aiUserIndex);
			ArrayList toolList = UserCalculator.calculateMaxHurtRatio(aiUser, thew);
			for ( int i=0; i<toolList.size(); i++ ) {
				BuffToolIndex toolIndex = (BuffToolIndex)toolList.get(i);
				BceRoleUseTool useTool = BceRoleUseTool.newBuilder().setSlot(toolIndex.slot()).build();
				AIManager.getInstance().sendServerMessageToAIClient(
						serverIoSession, aiUser.getSessionKey(), useTool, 2000, TimeUnit.MILLISECONDS);
			}
			/*
			BuffToolIndex toolIndex = BuffToolIndex.values()[(int)(MathUtil.nextDouble()*(BuffToolIndex.values().length-3))];
			int index = toolIndex.slot();
			if ( blood < blood * 0.2f ) {
				index = BuffToolIndex.UserTool1.ordinal();
			}
			
			BceRoleUseTool useTool = BceRoleUseTool.newBuilder().setSlot(index).build();
			AIManager.getInstance().sendServerMessageToAIClient(
					serverIoSession, aiUser.getSessionKey(), useTool, 2000, TimeUnit.MILLISECONDS);
			*/
			if ( energy >= 100 ) {
				BceRoleUseTool useTool = BceRoleUseTool.newBuilder().setSlot(
						BuffToolIndex.UsePower.ordinal()).build();
				AIManager.getInstance().sendServerMessageToAIClient(
						serverIoSession, aiUser.getSessionKey(), useTool, 2000, TimeUnit.MILLISECONDS);
			}
			
			UserCalculator.calculateMaxHurtRatio(aiUser);
		}
	}
	
	/**
	 * Random use a tool
	 */
	public static final void roleUseTool(User aiUser, IoSession serverIoSession, int blood, int energy) {
		//Use BuffTool
		BuffToolIndex toolIndex = BuffToolIndex.values()[(int)(MathUtil.nextDouble()*(BuffToolIndex.values().length-3))];
		int index = toolIndex.slot();
		if ( blood < blood * 0.2f ) {
			index = BuffToolIndex.UserTool1.ordinal();
		}
		
		BceRoleUseTool useTool = BceRoleUseTool.newBuilder().setSlot(index).build();
		AIManager.getInstance().sendServerMessageToAIClient(
				serverIoSession, aiUser.getSessionKey(), useTool, 2000, TimeUnit.MILLISECONDS);

		if ( energy >= 100 ) {
			useTool = BceRoleUseTool.newBuilder().setSlot(
					BuffToolIndex.UsePower.ordinal()).build();
			AIManager.getInstance().sendServerMessageToAIClient(
					serverIoSession, aiUser.getSessionKey(), useTool, 2000, TimeUnit.MILLISECONDS);
		}
	}
	
	/**
	 * Random use a tool
	 */
	public static final void roleUseGivenTool(User aiUser, IoSession serverIoSession, 
			BuffToolIndex toolIndex) {
		//Use BuffTool
		BceRoleUseTool useTool = BceRoleUseTool.newBuilder().setSlot(
				toolIndex.slot()).build();
		AIManager.getInstance().sendServerMessageToAIClient(
				serverIoSession, aiUser.getSessionKey(), useTool, 2000, TimeUnit.MILLISECONDS);
	}
	
	/**
	 * 
	 * @param aiUser
	 * @param serverIoSession
	 */
	public static final void roleSendExpress(User aiUser, 
			IoSession serverIoSession, int delay) {
		String message = "/e"+(int)((MathUtil.nextDouble()*8));
		roleChat(aiUser, serverIoSession, message, delay);
	}
	
	/**
	 * 
	 * @param aiUser
	 * @param serverIoSession
	 */
	public static final void roleChat(User aiUser, IoSession serverIoSession, 
			String message, int delay) {
		BceChat.Builder builder = BceChat.newBuilder();
		builder.setMsgType(ChatType.ChatCurrent.ordinal());
		builder.setMsgContent(message);
		
		AIManager.getInstance().sendServerMessageToAIClient(
				serverIoSession, aiUser.getSessionKey(), builder.build(), 
				delay, TimeUnit.MILLISECONDS);
	}
	
	/**
	 * Change the round to user.
	 * @param aiUser
	 * @param serverIoSession
	 * @param delay
	 */
	public static final void askRoundOver(User aiUser, IoSession serverIoSession, int delay) {
		BceAskRoundOver.Builder roundOver = BceAskRoundOver.newBuilder();
		AIManager.getInstance().sendServerMessageToAIClient(
				serverIoSession, aiUser.getSessionKey(), roundOver.build(), 
				delay, TimeUnit.MILLISECONDS);
	}
	
	/**
	 * Use the (hitx,hity) point and given angle to calculate the power needed.
	 * 
	 * @param angle
	 * @param hitx
	 * @param hity
	 * @return
	 */
	public static final int calculatePower(int angle, int hitx, int hity, int wind) {
		//Caculate the running time
		//0.055
		//double K = GameDataManager.getInstance().getGameDataAsDouble(GameDataKey.BATTLE_ATTACK_K, 0.059081);
		//double F = GameDataManager.getInstance().getGameDataAsDouble(GameDataKey.BATTLE_ATTACK_F, 0.075);
		//int    g = GameDataManager.getInstance().getGameDataAsInt(GameDataKey.BATTLE_ATTACK_G, 760);
		double rad = angle/180.0*Math.PI;
		double sin = FastMath.sin(rad);
		double cos = FastMath.cos(rad);
		double tx = hitx/3;
		int ty = 0;

		double a = sin;
		double b = -ty;
		double c = 0;
		double d = Math.abs(tx*tx / (2*cos));
		int power = (int)MathUtil.solveCubicEquation(a, b, c, d);
		logger.debug("a:{},b:{},c:{},d:{},wind:{},power:{}", new Object[]{a, b, c, d,wind, power});
		if ( power < 0 ) {
			power = -power;
		}
		if ( power > 100 ) {
			power = 100;
		}
		/**
		 * wind < 0 风向向右侧
		 * wind > 0 风向向左侧
		 */
		if ( wind < 0 && angle > 90 ) {
			power += -wind * 2 + 5;
		} else if ( wind < 0 && angle < 90 ) {
			/**
			 * 村口小桥顺风情况下计算的力度偏小,所以
			 * 这里去掉了风力的数值
			 * 2013-01-14
			 */
			//power -= -wind * 2 - 5;
		} else if ( wind > 0 && angle < 90 ) {
			power += wind * 2 + 5;
		} else if ( wind > 0 && angle > 90 ) {
			power -= wind * 2 - 5;
		}
		return (int)power;
	}

}