import numpy as np
import pandas as pd
import sys

TrainerData = pd.read_csv("trainers.csv", index_col = "Name")
MovesetData = pd.read_csv("BFpokemon.csv")
MovesData = pd.read_csv("movesdata.csv")
CombatChart = pd.read_csv("combatchart.csv")
PokemonData = pd.read_csv("pokemondata.csv")
NaturesTable = pd.read_csv("naturestable.csv")
MovesetData.fillna(0, inplace = True)
MovesData.fillna(0,inplace = True)

# USE ONLY LEVEL 100 OR LEVEL 50 FOR NOW
Level = 50 

#==================================#
#=== DON'T TOUCH ANYTHING BELOW ===#
#==================================#

class OwnPoke:
	def __init__(self, Name):
		self.Name = Name
		self.Type1 = PokemonData.loc[PokemonData["Species"] == Name, "Type1"].values[0]
		self.Type2 = PokemonData.loc[PokemonData["Species"] == Name, "Type2"].values[0]

class PokemonSet:
	def __init__(self, Name):
		self.Name = Name
		self.Entry = int(MovesetData.loc[MovesetData["Name"] == Name,"Entry"].values[0])
		self.Species = MovesetData.loc[MovesetData["Name"] == Name,"Species"].values[0]
		self.Instance = MovesetData.loc[MovesetData["Name"] == Name,"Instance"].values[0]
		self.Item = MovesetData.loc[MovesetData["Name"] == Name,"Item"].values[0]
		self.Nature = MovesetData.loc[MovesetData["Name"] == Name,"Nature"].values[0]
		self.Species = MovesetData.loc[MovesetData["Name"] == Name,"Species"].values[0]
		self.Move1 = MovesetData.loc[MovesetData["Name"] == Name,"Move 1"].values[0]
		self.Move2 = MovesetData.loc[MovesetData["Name"] == Name,"Move 2"].values[0]
		self.Move3 = MovesetData.loc[MovesetData["Name"] == Name,"Move 3"].values[0]
		self.Move4 = MovesetData.loc[MovesetData["Name"] == Name,"Move 4"].values[0]
		self.Speed = int(MovesetData.loc[MovesetData["Name"] == Name,"Lv " + str(Level) + " Speed"].values[0])
		self.MoveList = [self.Move1, self.Move2, self.Move3, self.Move4]

		self.EV_HP = int(MovesetData.loc[MovesetData["Name"] == Name,"HP EV"].values[0])
		self.EV_Atk = int(MovesetData.loc[MovesetData["Name"] == Name,"Atk EV"].values[0])
		self.EV_Def = int(MovesetData.loc[MovesetData["Name"] == Name,"Def EV"].values[0])
		self.EV_SpA = int(MovesetData.loc[MovesetData["Name"] == Name,"SpA EV"].values[0])
		self.EV_SpD = int(MovesetData.loc[MovesetData["Name"] == Name,"SpD EV"].values[0])
		self.EV_Spe = int(MovesetData.loc[MovesetData["Name"] == Name,"Speed EV"].values[0])

	def Setprinter(self):
		print(f'{self.Name:>12}',f'{self.Item:>13}',f'{self.Nature:>8}',f'{self.Move1:>13}',f'{self.Move2:>13}',f'{self.Move3:>13}',f'{self.Move4:>13}')

def stat_to_number(stat):
	if stat == 'HP':
		return 0
	elif stat == 'Atk':
		return 1
	elif stat == 'Def':
		return 2
	elif stat == 'SpA':
		return 3
	elif stat == 'SpD':
		return 4
	elif stat == 'Spe':
		return 5
	else:
		return 6

def natureModifier(stat, nature):
	mod = NaturesTable.loc[NaturesTable["nature"] == nature,stat].values[0]
	return mod

def HasMoves(team, moves):
	teamMoves = team[0].MoveList + team[1].MoveList + team[2].MoveList
	for move in moves:
		if move != 'none' and move not in teamMoves:
			return False
	return True

def BattleStyleCheck(team, bstyle):
	teamMoves = team[0].MoveList + team[1].MoveList + team[2].MoveList

	DMG = 0
	ACCURATE = 0
	RARE = 0
	EFFECT = 0
	LOWPP = 0
	COMBO = 0
	POPULAR = 0
	STATLOWER = 0
	POWERFUL = 0
	STRONG = 0
	LUCK = 0
	STATRAISE = 0
	HEAL = 0
	STATUS = 0
	RISKY = 0
	DEF = 0

	for move in teamMoves:
		DMG += int(MovesData.loc[MovesData["Name"] == move,"DMG"].values[0])
		ACCURATE += int(MovesData.loc[MovesData["Name"] == move,"ACCURATE"].values[0])
		RARE += int(MovesData.loc[MovesData["Name"] == move,"RARE"].values[0])
		EFFECT += int(MovesData.loc[MovesData["Name"] == move,"EFFECT"].values[0])
		LOWPP += int(MovesData.loc[MovesData["Name"] == move,"LOWPP"].values[0])
		COMBO += int(MovesData.loc[MovesData["Name"] == move,"COMBO"].values[0])
		POPULAR += int(MovesData.loc[MovesData["Name"] == move,"POPULAR"].values[0])
		STATLOWER += int(MovesData.loc[MovesData["Name"] == move,"STATLOWER"].values[0])
		POWERFUL += int(MovesData.loc[MovesData["Name"] == move,"POWERFUL"].values[0])
		STRONG += int(MovesData.loc[MovesData["Name"] == move,"STRONG"].values[0])
		LUCK += int(MovesData.loc[MovesData["Name"] == move,"LUCK"].values[0])
		STATRAISE += int(MovesData.loc[MovesData["Name"] == move,"STATRAISE"].values[0])
		HEAL += int(MovesData.loc[MovesData["Name"] == move,"HEAL"].values[0])
		STATUS += int(MovesData.loc[MovesData["Name"] == move,"STATUS"].values[0])
		RISKY += int(MovesData.loc[MovesData["Name"] == move,"RISKY"].values[0])
		DEF += int(MovesData.loc[MovesData["Name"] == move,"DEF"].values[0])

	if RISKY >= 1:
		style = 0
	elif HEAL >= 2 and STATUS >= 1 and DEF >= 2:
		style = 1
	elif COMBO >= 1 and STATRAISE >= 1 and STATLOWER >= 1 and HEAL >= 1 and STATUS >= 1 and DEF >= 1:
		style = 2
	elif COMBO >= 3:
		style = 3
	elif RARE >= 2:
		style = 4
	elif RARE >= 1:
		style = 5
	elif HEAL >= 3:
		style = 6
	elif STATRAISE >= 1 and HEAL >= 1:
		style = 7
	elif STATLOWER >= 1 and STATUS >= 1:
		style = 8
	elif LUCK >= 2:
		style = 9
	elif STATRAISE >= 1 and HEAL >= 1 and DEF >= 1 and POPULAR >= 1 and STRONG >= 1:
		style = 10
	elif LOWPP >= 3:
		style = 11
	elif STATRAISE >= 1 and STATUS >= 1:
		style = 12
	elif HEAL >= 2 and DEF >= 2:
		style = 13
	elif STATUS >= 2:
		style = 14
	elif ACCURATE >= 3 and STRONG >= 3:
		style = 15
	elif STRONG >= 4:
		style = 16
	elif DEF >= 3:
		style = 17
	elif STATLOWER >= 2 and STATUS >= 2:
		style = 18
	elif POWERFUL >= 3 and POPULAR >= 3:
		style = 19
	elif COMBO >= 2:
		style = 20
	elif HEAL >= 1 and ACCURATE >= 3:
		style = 21
	elif POWERFUL >= 4:
		style = 22
	elif DMG >= 7:
		style = 23
	elif DEF >= 4:
		style = 24
	elif POPULAR >= 2 and STRONG >= 4:
		style = 25
	elif EFFECT >= 4:
		style = 26
	else:
		style = 27

	return style == bstyle


def EVStylesCheck(team, ENB, stat1, stat2):
	HPEVTotal = 0
	AtkEVTotal = 0
	DefEVTotal = 0
	SpAEVTotal = 0
	SpDEVTotal = 0
	SpeEVTotal = 0

	for pokemon in team:
		HPEVTotal  += pokemon.EV_HP
		AtkEVTotal += pokemon.EV_Atk * natureModifier('Atk', pokemon.Nature)
		DefEVTotal += pokemon.EV_Def * natureModifier('Def', pokemon.Nature)
		SpAEVTotal += pokemon.EV_SpA * natureModifier('SpA', pokemon.Nature)
		SpDEVTotal += pokemon.EV_SpD * natureModifier('SpD', pokemon.Nature)
		SpeEVTotal += pokemon.EV_Spe * natureModifier('Spe', pokemon.Nature)

	EVTotal = HPEVTotal + AtkEVTotal + DefEVTotal + SpAEVTotal + SpDEVTotal + SpeEVTotal

	HPEmph  = HPEVTotal  / EVTotal >= 0.3
	AtkEmph = AtkEVTotal / EVTotal >= 0.3
	DefEmph = DefEVTotal / EVTotal >= 0.3
	SpAEmph = SpAEVTotal / EVTotal >= 0.3
	SpDEmph = SpDEVTotal / EVTotal >= 0.3
	SpeEmph = SpeEVTotal / EVTotal >= 0.3

	HPNegl  =  HPEVTotal == 0
	AtkNegl = AtkEVTotal == 0
	DefNegl = DefEVTotal == 0
	SpANegl = SpAEVTotal == 0
	SpDNegl = SpDEVTotal == 0
	SpeNegl = SpeEVTotal == 0

	EVector = [HPEmph, AtkEmph, DefEmph, SpAEmph, SpDEmph, SpeEmph]
	NVector = [HPNegl, AtkNegl, DefNegl, SpANegl, SpDNegl, SpeNegl]

	if ENB == 'B':
		return not(any(EVector) or any(NVector))

	elif ENB == 'E':
		number1 = stat_to_number(stat1)
		number2 = stat_to_number(stat2)
		if number2 == 6:
			templist = [0,1,2,3,4,5]
			templist.remove(number1)
			importantelements = [EVector[x] for x in templist]
			return EVector[number1] and not any(importantelements)
		else:
			return EVector[number1] and EVector[number2]

	elif ENB == 'N':
		number1 = stat_to_number(stat1)
		number2 = stat_to_number(stat2)
		if any(EVector) == True:
			return False
		elif number2 == 6:
			templist = [0,1,2,3,4,5]
			templist.remove(number1)
			importantelements = [NVector[x] for x in templist]
			return NVector[number1] and not any(importantelements)
		else:
			return NVector[number1] and NVector[number2]


def printline():
	print("----------------------------------------------------------------------------------------------")

printline()
print("Who is your next opponent? Fill in the trainer name.")
Trainer = input()
Trainer = Trainer.upper()
printline()

# CONVERT ALL SET IDENTIFIERS TO POKEMON WITH INFORMATION FROM TABLE
trainerSets = list(TrainerData.loc[[Trainer]].values[0])
del trainerSets[0]
trainerSets = [x for x in trainerSets if x == x]
PokemonList = []
for identifier in trainerSets:
	Pokemon = PokemonSet(identifier)
	if Level != 50 or Pokemon.Entry <= 850:
		PokemonList.append(Pokemon)

# CALCULATE ALL POSSIBLE TEAMS AS A LIST OF 3-TUPLES

print("Fill in the different species you see, separated by Enters.")
Species1 = input()
Species2 = input()
Species3 = input()
printline()

TeamCombinations = []
for pokemon1 in PokemonList:
	for pokemon2 in PokemonList:
		for pokemon3 in PokemonList:
			if (    pokemon1.Species == Species1
				and pokemon2.Species == Species2
				and pokemon3.Species == Species3
				and pokemon1.Item    != pokemon2.Item
				and pokemon2.Item    != pokemon3.Item
				and pokemon1.Item    != pokemon3.Item):
				TeamCombinations.append((pokemon1,pokemon2,pokemon3))

if len(TeamCombinations) == 1:
	print("There is only one possible team:")
	TeamCombinations[0][0].Setprinter()
	TeamCombinations[0][1].Setprinter()
	TeamCombinations[0][2].Setprinter()
	sys.exit()

print("Select the battle style from the following list:")
print(" (0) Willing to risk total disaster at times")
print(" (1) Skilled at enduring long battles")
print(" (2) Varies tactics to suit the opponent")
print(" (3) Has a tough winning pattern")
print(" (4) Occasionally uses a very rare move")
print(" (5) Uses startling and disruptive moves")
print(" (6) Constantly watches HP in battle")
print(" (7) Good at storing then loosing power")
print(" (8) Skilled at enfeebling foes")
print(" (9) Prefers tactics that rely on luck")
print("(10) Attacks with a regal atmosphere")
print("(11) Attacks with powerful, low-PP moves")
print("(12) Skilled at enfeebling, then attacking")
print("(13) Battles while enduring all attacks")
print("(14) Skilled at upsetting foes emotionally")
print("(15) Uses strong and straightforward moves")
print("(16) Aggressively uses strong moves")
print("(17) Battles while cleverly dodging attacks")
print("(18) Skilled at using upsetting attacks")
print("(19) Uses many popular moves")
print("(20) Has moves for powerful combinations")
print("(21) Uses high-probability attacks")
print("(22) Aggressively uses spectacular moves")
print("(23) Emphasizes offense over defense")
print("(24) Emphasizes defense over offense")
print("(25) Attacks quickly with strong moves")
print("(26) Often uses moves with added effects")
print("(27) Uses a well-balanced mix of moves")

battleStyle = int(input())
printline()

print("Is there emphasis (E) or neglection (N) of any stats, or is the team 'well-balanced' (B)?")
emphneglbala = input()
printline()

if emphneglbala != 'B':
	print("Which stat(s)? Type 'none' if the second input is not necessary.")
	ENstat1 = input()
	ENstat2 = input()
	printline()
if emphneglbala == 'B':
	ENstat1 = 'none'
	ENstat2 = 'none'

print("Finally, enter any moves you know via the Tourney Tree, or 'none' if you don't know.")
knownMove1 = input()
if knownMove1 != 'none':
	knownMove2 = input()
else:
	knownMove2 = 'none'
if knownMove1 != 'none' and knownMove2 != 'none':
	knownMove3 = input()
else:
	knownMove3 = 'none'
printline()


i = 1
for team in TeamCombinations:
	if EVStylesCheck(team, emphneglbala, ENstat1, ENstat2) and BattleStyleCheck(team, battleStyle) and HasMoves(team, [knownMove1,knownMove2,knownMove3]):
		print("=== Option", i, "===")
		team[0].Setprinter()
		team[1].Setprinter()
		team[2].Setprinter()
		i += 1


