#include "Dice_Funcs.h"

static uint8_t Has_Straight(uint8_t *roll_results, uint8_t dice){
	/* return 1 if dice are a straight, else 0 
	should only be run when dice == full amount (first roll)*/
	if(dice == 1)
		return 1;
	if(dice == 0)
	return 0;
	uint8_t num1 = 0;
	uint8_t num2 = SIDES;
	uint8_t match_found = 0;
	uint8_t *roll_results_ordered = malloc(dice*sizeof(uint8_t) );

	//find lowest rolled number
	for(num1=0; num1<dice; num1++){
		if(roll_results[num1] < num2)
			num2 = roll_results[num1];
	}
	roll_results_ordered[0] = num2;

	//search for roll that is 1 higher than previous for dice, return 0 if broken
	for(num1=1; num1<dice; num1++){
		match_found = 0;
		for(num2=0; num2<dice; num2++){
			if(roll_results[num2] == roll_results_ordered[num1-1]+1){
				roll_results_ordered[num1] = roll_results[num2];
				match_found = 1;
				break;
			}
		}
		if(match_found == 0)
			return 0;
	}
	free(roll_results_ordered);
	return 1;
}

static uint8_t Has_Three_Pair(uint8_t *num_counts, uint8_t dice){
	/* return 1 if has 3 pair else 0 */
	if(dice < 6)
		return 0;
	uint8_t num1 = 0;
	uint8_t num2 = 0;
	for(num1=0; num1<SIDES; num1++){
		if(num_counts[num1] > 1)
			num2++;
	}
	if(num2 > 2)
		return 1;
	return 0;
}

void Roll_Dice(void){
	uint8_t num1 = 0;
	uint8_t num2 = pd[gs.Cur_Player].rdc;
	for(num1=0; num1<num2; num1++){
		gs.Dice_Values[num1] = (uint8_t)( lrand(&(gs.rseed)) % SIDES + 1);
	}
}

void AI_Make_Selection(){
	//AI marks Dice_Selections appropriately

}

void AI_Do_Roll(){
	//return ?


}

uint32_t AI_Do_Turn(uint32_t p_num){
	//return score 0+
	
}

void Gen_Num_Counts(void){
	uint8_t num1 = 0;
	uint8_t num2 = pd[gs.Cur_Player].rdc;
	(void)memset(gs.Num_Counts, '\0', sizeof(uint8_t) * SIDES);
	for(num1=0; num1<num2; num1++){
		gs.Num_Counts[ gs.Dice_Values[num1]-1 ]++;
	}

}

uint32_t Gen_Num_Scores(void){
	uint8_t num1 = 0;
	uint8_t num2 = 0;
	uint32_t Score = 0;
	(void)memset(gs.Num_Scores, '\0', sizeof(uint32_t) * SIDES);
	for(num1=0; num1<SIDES; num1++){
		if(num1 == 0){
			if(gs.Num_Counts[0] < 3){
				gs.Num_Scores[0] = 100*gs.Num_Counts[0];
				continue;
			}
			gs.Num_Scores[0] = 1000;
			for(num2=3; num2<gs.Num_Counts[0]; num2++){
				gs.Num_Scores[0] *= 2;
			}
			continue;
		}
		if(num1 == 4){
			if(gs.Num_Counts[4] < 3){
				gs.Num_Scores[4] = 50*gs.Num_Counts[4];
				continue;
			}
			num_scores[4] = 500;
			for(num2=3; num2<gs.Num_Counts[4]; num2++){
				gs.Num_Scores[4] *= 2;
			}
			continue;
		}
		if(gs.Num_Counts[num1] < 3)
			continue;
		gs.Num_Scores[num1] = 100*(num1+1);
		for(num2=3; num2<gs.Num_Counts[num1]; num2++){
			gs.Num_Scores[num1] *= 2;
		}
	}
	//Score = 0;
	for(num1=0; num1<SIDES; num1++){
		Score += gs.Num_Scores[num1];
	}
	return Score;
}

uint8_t Gen_Kept_Dice(void){
	uint8_t num1 = 0;
	uint8_t num2 = pd[gs.Cur_Player].rdc;
	uint8_t Dice_Kept = 0;

	(void)memset(gs.Kept_Dice, '\0', sizeof(uint8_t) * num2);
	for(num1=0; num1<num2; num1++){
		if(gs.Dice_Values[num1] == 1){
			gs.Kept_Dice[num1] = 1;
			Dice_Kept++;
			continue;
		}
		if(gs.Dice_Values[num1] == 5){
			gs.Kept_Dice[num1] = 1;
			Dice_Kept++;
		}
	}
	return Dice_Kept;
}

static uint32_t Roll_Dice_old(uint32_t p_num){
//static uint32_t Roll_Dice(uint64_t *s, struct Player_Data *pd){
	uint32_t num1, num2, num3;

	//uint8_t roll_results[Dice];
	uint8_t *roll_results = (uint8_t *)malloc(gs.Dice * sizeof(uint8_t));

	uint8_t num_counts[SIDES];
	uint32_t num_scores[SIDES];

	//uint8_t kept_dice[DICE];
	uint8_t *kept_dice = (uint8_t *)malloc(gs.Dice * sizeof(uint8_t));

	uint32_t score = 0;
	uint32_t score_final = 0;
	uint32_t rolling_dice = gs.Dice;
	uint8_t turn_not_over = 1;

	while(turn_not_over == 1){
		printf("\t");
		for(num2=0; num2<rolling_dice; num2++){
			roll_results[num2] = lrand(&(pd[p_num].rseed)) % SIDES + 1;
			printf("%u ", roll_results[num2]);
		}
		printf("\n");
		sleep(1);
		(void)memset(num_counts, '\0', sizeof(uint8_t) * SIDES);
        for(num2=0; num2<rolling_dice; num2++){
            num_counts[ roll_results[num2]-1 ]++;
        }
		(void)memset(num_scores, '\0', sizeof(uint32_t) * SIDES);
		(void)memset(kept_dice, '\0', sizeof(uint8_t) * gs.Dice);
		//set kept_dice
		for(num2=0; num2<rolling_dice; num2++){
			if(roll_results[num2] == 1 || roll_results[num2] == 5){
				kept_dice[num2] = 1;
			}
		}
		for(num2=0; num2<SIDES; num2++){
			if(num_counts[num2] > 2){
				for(num3=0; num3<rolling_dice; num3++){
					if(roll_results[num3] == num2+1)
						kept_dice[num3] = 1;
				}
			}
		}
		//set num_scores
		for(num2=0; num2<SIDES; num2++){
			if(num2 == 0){
				if(num_counts[0] < 3){
					num_scores[0] = 100*num_counts[0];
					continue;
				}
				num_scores[0] = 1000;
				for(num3=3; num3<num_counts[0]; num3++){
					num_scores[0] *= 2;
				}
				continue;
			}
			if(num2 == 4){
				if(num_counts[4] < 3){
					num_scores[4] = 50*num_counts[4];
					continue;
				}
				num_scores[4] = 500;
				for(num3=3; num3<num_counts[4]; num3++){
					num_scores[4] *= 2;
				}
				continue;
			}
			if(num_counts[num2] < 3)
				continue;
			num_scores[num2] = 100*(num2+1);
			for(num3=3; num3<num_counts[num2]; num3++){
				num_scores[num2] *= 2;
			}
		}
		score = 0;
		for(num3=0; num3<SIDES; num3++){
			score += num_scores[num3];
		}
		if(rolling_dice > 5){
			if( Has_Three_Pair(num_counts, rolling_dice) ){
			//must do entirely different if greater than 6 dice
			//could potentially have multiple three pairs ...
			//or three pairs + 1 + 5 + 3 of kind + etc.
				if(score < gs.Three_Pairs_Score){
					rolling_dice = gs.Dice;
					score_final += gs.Three_Pairs_Score;
					continue;
				}
			}
		}
		if(rolling_dice == gs.Dice){
			if( Has_Straight(roll_results, rolling_dice) ){
				if(score < gs.Straight_Score){
					rolling_dice = gs.Dice;
					score_final += gs.Straight_Score;
					continue;
				}
			}
		}
		//do zilch test here
		if(score == 0){
			turn_not_over = 0;
			score_final = 0;
			continue;
		}
		num1 = rolling_dice; //backup
		num3 = 0;
		for(num2=0; num2<rolling_dice; num2++){
			if(kept_dice[num2] == 1)
				num3++;
		}
		if(rolling_dice == num3){
			rolling_dice = gs.Dice;
			score_final += score;
			//can use all dice
			continue;
		}else{
			rolling_dice -= num3;
		}
		//decide what dice to keep/roll or to end turn here
		if(pd[p_num].onboard != 1){
			if(score_final+score < gs.Min_Get_Onboard){
				//must keep rolling
				num3 = 0;
				//have 3+ of kind ?
				for(num2=0; num2<SIDES; num2++){
					if(num_counts[num2] > 2){ //FIXME: for more than 6 dice
						rolling_dice = num1 - num_counts[num2];
						num3 = 1;
						score_final += num_scores[num2];
						break;
					}
				}
				if(num3 == 1)
					continue;
				//keep only a 1 to increase probabilities?
				if(num_counts[0] != 0){ //we have a 1
					if(num_counts[0] < 3){ //we have less than 1000
						//keep just 1
						rolling_dice = num1 - 1;
						score_final += 100;
						continue;
					}
					//keep them all
					rolling_dice = num1 - num_counts[0];
					score_final += num_scores[0];
					continue;
				}
				//have only a 5 ?
				if(num_counts[4] != 0){ //we have a 5
					rolling_dice = num1 - 1;
					score_final += 50;
					continue;
				}
				//FIXME: function return value for main cleanup ie: errno
				printf("Error 1 !\n");
				exit(0);
			}
		}
		if(score_final+score < pd[p_num].min_score_will_keep){
			//must keep rolling
			num3 = 0;
			for(num2=0; num2<SIDES; num2++){
				if(num_counts[num2] > 2){//FIXME: for more than 6 dice
					rolling_dice = num1 - num_counts[num2];
					num3 = 1;
					score_final += num_scores[num2];
					break;
				}
			}
			if(num3 == 1)
				continue;
			if(num_counts[0] != 0){
				if(num_counts[0] < 3){
					rolling_dice = num1 - 1;
					score_final += 100;
					continue;
				}
				rolling_dice = num1 - num_counts[0];
				score_final += num_scores[0];
				continue;
			}
			if(num_counts[4] != 0){
				rolling_dice = num1 - 1;
				score_final += 50;
				continue;
			}
			//FIXME: see above
			printf("Error 2 !\n");
			exit(0);
		}else{
			turn_not_over = 0;
		}
		if( rolling_dice < pd[p_num].min_dice_will_roll){
			//will keep rolling if new rolling_dice value is not too low
			//take score and end turn
			turn_not_over = 0;
		}
		score_final += score;
	}
	free(roll_results);
	free(kept_dice);
	return score_final;
}

void *Worker_Func(void *data){
	uint32_t num1, num2;
	uint32_t turn = 0;
	uint8_t ten_k_reached = 0;
	uint32_t tentative_winner = 0;
	uint32_t leader_score = 0;
	
	struct timeval cur;
	gettimeofday(&cur, NULL);
	//pd = (struct Player_Data *)malloc(sizeof(struct Player_Data) * Num_Players);

	while(1==1){
		sleep(1);
		
		if(gs.Exit_Program)
			break;
	}
	//free(pd);
	//pthread_exit(NULL);
	return NULL;
}

uint32_t Score_Of_Selection(){
	//return the score of currently selected dice
	//call from any selection activity callback
	//maybe use for AI too


}

uint8_t Valid_Dice_Selection(uint8_t index){
	//return TRUE if selected dice are valid and it is OK for a LOCAL_PLAYER to roll
	//all selected dice must contribute to points
	//If FALSE then Dice_Selections must be reset, and user given another chance to select properly

	//Function Don't edit any objects outside of itself

	uint8_t num1 = 0;
	uint8_t num2 = 0;
	uint8_t num_dice_selected = 0;
	uint8_t num_counts[SIDES];
	for(num1=0; num1<gs.Dice_Rolled; num1++){
		if(gs.Dice_Selections[num1] == 1)
			num_dice_selected++;
	}
	//only ok if all dice are used to generate points
	//if(num_dice_selected == 0){

	if(gs.Dice_Rolled == gs.Dice){
		if(Has_Straight(gs.Dice_Values, gs.Dice))
			return TRUE;
	}
	if(gs.Dice_Rolled > 5){
		if(Has_Three_Pair(gs.Num_Counts, gs.Dice_Rolled))
			return TRUE;
	}


		//has x of kind
        (void)memset(num_counts, '\0', sizeof(uint8_t) * SIDES);
        for(num2=0; num2<gs.Dice; num2++){
            num_counts[ gs.Dice_Values[num2]-1 ]++;
        }
		for(num2=0; num2<SIDES; num2++){
			if(num_counts[num2] == gs.Dice)
				return TRUE;
		}

	}
	//if


	

	return FALSE;
}