package com.iamberry.wechat.service.game;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.iamberry.wechat.core.entity.ResultMsg;
import com.iamberry.wechat.core.entity.coupon.CouponItem;
import com.iamberry.wechat.core.entity.coupon.CouponType;
import com.iamberry.wechat.core.entity.game.GameLogsDto;
import com.iamberry.wechat.core.entity.game.GamePrizeDto;
import com.iamberry.wechat.core.entity.game.GameSystemDto;
import com.iamberry.wechat.core.entity.order.Order;
import com.iamberry.wechat.face.game.GameService;
import com.iamberry.wechat.service.game.dao.GameDao;
import com.iamberry.wechat.service.mapper.CouponItemMapper;
import com.iamberry.wechat.service.mapper.CouponTypeMapper;
import com.iamberry.wechat.service.system.dao.SystemDao;
import com.iamberry.wechat.tools.DateTimeUtil;
import com.iamberry.wechat.tools.StrUtils;

@Service
public class GameServiceImpl implements GameService {

	@Autowired
	private GameDao gameDao;
	
	@Autowired
	private GameServiceImpl thisService;
	
	@Autowired
	private CouponTypeMapper couponTypeMapper;		//优惠券类型
	@Autowired
	private CouponItemMapper couponItemMapper;		//优惠券详情
	@Autowired
	private SystemDao systemDao;
	
	private Object lock = new Object();
	
	@Override
	public GameSystemDto selectGameByGameType(GameSystemDto gameSystemDto) {
		// TODO Auto-generated method stub
		return gameDao.selectGameByGameType(gameSystemDto);
	}

	@Override
	public GameSystemDto selectGameByGameId(GameSystemDto gameSystemDto) {
		// TODO Auto-generated method stub
		return gameDao.selectGameByGameId(gameSystemDto);
	}

	@Override
	public List<GamePrizeDto> selectGamePrizeByGameId(GamePrizeDto gamePrizeDto) {
		// TODO Auto-generated method stub
		return gameDao.selectGamePrizeByGameId(gamePrizeDto);
	}

	@Override
	public List<GameLogsDto> selectGameLogsByGameId(GameLogsDto gameLogsDto) {
		// TODO Auto-generated method stub
		return gameDao.selectGameLogsByGameId(gameLogsDto);
	}

	@Override
	public Integer selectGameLogsCount(GameLogsDto gameLogsDto) {
		// TODO Auto-generated method stub
		return gameDao.selectGameLogsCount(gameLogsDto);
	}

	@Override
	public Integer selectOrderByCreateDate(Order order) {
		// TODO Auto-generated method stub
		return gameDao.selectOrderByCreateDate(order);
	}

	@Override
	public ResultMsg getPrize(Integer gameId, String orderId, String openID) {
		// TODO Auto-generated method stub
		ResultMsg resultMsg = new ResultMsg();
		
		// 判断游戏是否存在
		GameSystemDto gameLogsDto = new GameSystemDto();
		gameLogsDto.setGameId(gameId);
		GameSystemDto dto = this.selectGameByGameType(gameLogsDto);
		if (dto == null || dto.getGameId() == null) {
			resultMsg.setMessage("活动不存在!");
			resultMsg.setStatus(false);
			resultMsg.setResultCode("404");
			resultMsg.setData(0);
			return resultMsg;
		}
		
		// 判断是否开始
		Date now = new Date();
		if (dto.getGameStartDate() == null || dto.getGameStartDate().getTime() > now.getTime()) {
			resultMsg.setMessage("活动尚未开始!");
			resultMsg.setStatus(false);
			resultMsg.setResultCode("404");
			resultMsg.setData(0);
			return resultMsg;
		}
		
		// 判断是否结束
		if (dto.getGameEndDate() == null || dto.getGameEndDate().getTime() < now.getTime()) {
			resultMsg.setMessage("活动已经结束!");
			resultMsg.setStatus(false);
			resultMsg.setResultCode("404");
			resultMsg.setData(0);
			return resultMsg;
		}
		
		// 判断抽奖次数
		GameLogsDto logsDto = new GameLogsDto();
		logsDto.setLogsGameId(gameId);
		logsDto.setLogsOpenId(openID);
		if (orderId != null && !"".equals(orderId)) {
			logsDto.setLogsOrderId(orderId);
		}
		int count = this.selectGameLogsCount(logsDto);
		if (count >= dto.getGameThresholdNum()) {
			resultMsg.setMessage("对不起，您的次数已经用尽!");
			resultMsg.setStatus(false);
			resultMsg.setResultCode("404");
			resultMsg.setData(0);
			return resultMsg;
		}
		
		// 抽奖
		if (thisService.doLuckyDraw(gameId, orderId, openID, logsDto)) {
			resultMsg.setMessage("中奖啦!");
			resultMsg.setStatus(true);
			resultMsg.setResultCode("200");
			resultMsg.setData(logsDto.getLogsPrizeId());
			return resultMsg;
		} else {
			resultMsg.setMessage("对不起，您没有中奖!");
			resultMsg.setStatus(false);
			resultMsg.setResultCode("404");
			resultMsg.setData(0);
			return resultMsg;
		}
	}
	
	private Object drawLock = new Object();

	/**
	 * 实际的抽奖方法，需要事务存在
	 * @param resultMsg
	 * @param gameId
	 * @param orderId
	 * @param openID
	 */
	public boolean doLuckyDraw(Integer gameId, String orderId, String openID, GameLogsDto logsDto) {
		
		// 可以抽奖,如果此处只有一个奖品时，但是另外一个事务正在进行时，为了保证不脏读，所以不会读取没有提交的数据，可能导致超出
		GamePrizeDto gamePrizeDto = new GamePrizeDto();
		gamePrizeDto.setPrizeGameId(gameId);
		synchronized (drawLock) {
			List<GamePrizeDto> dtos = this.selectGamePrizeByGameId(gamePrizeDto);
			if (dtos == null || dtos.size() <= 0) {
				return false;
			}
		
			// 抽奖
			List<Double> probList = new ArrayList<Double>();
			/**
			 * 抽奖人数：1000
			 * 1:0.01
			 * 2:0.03
			 * 3:0.06
			 * 4:0.1
			 * 5:0.1
			 * 6:0.8
			 * total=1
			 */
			
			/**
			 * 第二次抽奖人数：999
			 * 如果将第一次奖品抽取，概率为
			 * 2:0.03
			 * 3:0.06
			 * 4:0.1
			 * 5:0.1
			 * 6:0.8
			 * total=0.99
			 */
			
			// 挑选还有数量的奖品
			List<GamePrizeDto> prizes = new ArrayList<GamePrizeDto>();
			// 已经被抽取的奖品概率
			double winningDouble = 0.0;
			double totalWinning = 0.0;
			for (GamePrizeDto gamePrizeDto2 : dtos) {
				double temp = Double.parseDouble(gamePrizeDto2.getPrizeProbability().toString()) / 10000.0;
				if (gamePrizeDto2.getPrizeNum().intValue() >= 1) {
					totalWinning = totalWinning + temp;
					prizes.add(gamePrizeDto2);
					probList.add(temp);
				} else {
					winningDouble = winningDouble + temp;
				}
			}
			
			// 没有奖品
			if (probList == null || probList.size() <= 0) {
				return false;
			}

			// 将已经被抽取光了的奖品概率平分
			for (int i = 0; i < probList.size(); i++) {
				// 按比例重新计算概率
				double temp = probList.get(i);
				// 当前奖品比例 在所占奖品中占比 = 当前比例
				// 当前比例 * 已经抽光的奖品概率 = 当前奖品需要被平分的奖品
				temp = temp + ((temp / totalWinning) * winningDouble);
				
				// 重置
				probList.set(i, temp);
			}
			
			// 抽奖 
			int i = getGameWinningLevel(probList, 10000);
			if (i <= -1) {
				// 没有中奖
				logsDto.setLogsState(1);
				gameDao.saveLogs(logsDto);
				return false;
			}
			GamePrizeDto dto = prizes.get(i-1);

			// 修改,此处通过mysql行级锁保证，所以尽可能的在事务中开启的迟
			int count = gameDao.updatePrizeNum(dto.getPrizeId());
			logsDto.setLogsPrizeId(dto.getPrizeId());
			if (count < 1) {
				// 没有奖品啦，不予中奖
				logsDto.setLogsState(1);
				gameDao.saveLogs(logsDto);
				return false;
			}
			
			// 已经扣除奖品，中奖
			logsDto.setLogsState(2);
			// 保存抽奖记录
			count = gameDao.saveLogs(logsDto);
			
			//若抽中优惠券 则领取对应的优惠券
			boolean flag = true;
			int prizeId = dto.getPrizeId().intValue();
			if(prizeId == 2 || prizeId == 5 || prizeId == 17 || prizeId == 20){
				//规则表中存放奖品等级对应的优惠券id 如：二等奖 则存：ruleId=202	ruleNum=couponTypeId
				int couponTypeId = systemDao.selectOneShopRuleById(200 + prizeId).getRuleNum().intValue();
				flag = getCoupon(openID, couponTypeId);	//领取优惠券是否成功
			}
			if (count >= 1 && flag) {
				logsDto.setLogsPrizeId(dto.getPrizeId());
				return true;
			} else {
				throw new RuntimeException("没有中奖");
			}
		}
	}
	
	/**
	 * 抽奖算法 ： 传入中奖概率和参与基数，返回用户中奖等级数
	 * @param probList 概率集合列表
	 * @param cardinalNumber 参与基数（ 当cardinalNumber=1000时，统计学理论上抽奖1000次时，所有奖品均已经出现。 ）
	 */
	public static int getGameWinningLevel(List<Double> probList,
			Integer cardinalNumber) {
		// 中奖等级：-1未中奖
		int winningLevel = -1;
		if (probList == null || probList.size() <= 0) {
			return winningLevel;
		}
		// 中奖随机号
		int randomWinningNo = 0;
		// 存储中奖范围
		int args[] = new int[probList.size() * 2];
		// Math.round -> 向上取余数， 返回最接近参数的 int
		// Math.random -> 返回带正号的double值，该值大于等于 0.0 且小于 1.0
		// 作为生成中奖随机数
		int temp = (int) Math.round(Math.random() * cardinalNumber);
		int j = 0;
		for (int i = 0; i < probList.size(); i++) {
			double tmpWinningPro = (double) probList.get(i);
			if (j == 0) {
				args[j] = randomWinningNo;
			} else {
				args[j] = args[j - 1] + 1;
			}
			// 对tmpWinningPro向上取余数， 返回最接近参数的 int
			args[j + 1] = args[j] + (int) Math.round(tmpWinningPro * cardinalNumber) - 1;
			if (temp >= args[j] && temp <= args[j + 1]) {
				// 中奖等级，返回
				winningLevel = i + 1;
				return winningLevel;
			}
			j += 2;
		}
		return winningLevel;
	}

	@Override
	public List<GameLogsDto> selectWinningLogsByGameId(GameLogsDto logDto) {
		// TODO Auto-generated method stub
		return gameDao.selectWinningLogsByGameId(logDto);
	}
 
	@Override
	public Integer selectWinningCount(GameLogsDto logDto) {
		// TODO Auto-generated method stub
		int count =  gameDao.selectWinningCount(logDto);
		return	count = count % logDto.getPage().getPageSize() == 0? count/logDto.getPage().getPageSize():(count/logDto.getPage().getPageSize())+1;
	}
	
	
	/**
	 * 领取优惠券 
	 * 2016年9月30日
	 * @return
	 */
	private boolean getCoupon(String openId,int couponTypeId){
		synchronized (lock) {
			CouponType ct = couponTypeMapper.getCouponTypeById(couponTypeId);
			if(checkCoupon(openId, ct)){
				return getCouponItem(openId, ct);
			}else{
				return false;
			}
		}
	}
	
	/**
	 * 校验某用户是否符合领取优惠券条件
	 * 2016年9月30日
	 * @param member
	 * @param couponType
	 * @return
	 */
	private boolean checkCoupon(String openId,CouponType couponType){
		if(StringUtils.isEmpty(openId) || couponType == null){
			return false;
		}
		Date now = new Date();
		//券已过期
		if(couponType.getCouponEndDate() != null && couponType.getCouponEndDate().before(now)){
			return false;
		}
		//券已领完
		if(couponType.getCouponRemainingNum() < 1){
			return false;
		}
		//待加条件：是否必须购买	消费总金额要求（满足这个条件才能领券）
		return true;
	}
	
	/**
	 * 领取某类型的优惠券 总数量减少 优惠券详情表插入数据
	 * 2016年9月30日
	 * @param openid
	 * @param couponType
	 * @return
	 */
	private boolean getCouponItem(String openId,CouponType couponType){
		//减少该类型的优惠券总数
		int i = couponTypeMapper.updateCouponTypeForItem(couponType.getCouponId());
		
		//增加单张优惠券
		CouponItem couponItem = new CouponItem();
		couponItem.setCouponItemUseropenid(openId);
		couponItem.setCouponId(couponType.getCouponId());
		couponItem.setCouponUseStatus(1);	//使用状态:未使用
		Date endDate = DateTimeUtil.addHour(DateTimeUtil.now(), 
				couponType.getCouponLimitHours());	//券的过期时间
		//若券的过期时间早于此类型券的截止时间，则取券的过期时间
		if(endDate.before(couponType.getCouponEndDate())){	
			couponItem.setCouponUseEndDate(endDate);
		}else{		//券的过期时间晚于此类型券的截止时间则取此类型券的截止时间
			couponItem.setCouponUseEndDate(couponType.getCouponEndDate());
		}
		String uuidStr = StrUtils.getUUID();
		couponItem.setCouponItemId(uuidStr);
		Integer j = couponItemMapper.insertCouponItem(couponItem);
		
		if(i > 0 && j != null && j.intValue() > 0){
			return true;
		}
		return false;
	}
}
