import time, asyncio, json, jsonpickle, random, math, os
import league_storage as league_db
from weather import WeatherChains, all_weathers
from itertools import chain
from copy import deepcopy
from games import team, game
from discord import Embed, Color
from uuid import uuid4

data_dir = "data"
league_dir = "leagues"

class league_structure(object):
    def __init__(self, name):
        self.name = name
        self.historic = False
        self.owner = None
        self.season = 1
        self.autoplay = -1
        self.champion = None
        self.weather_forecast = {}
        self.weather_override = None #set to a weather for league-wide weather effects
        self.last_weather_event_day = 0
        self.weather_event_duration = 0
        self.postseason = True
        self.subbed_channels = []

    def setup(self, league_dic, division_games = 1, inter_division_games = 1, inter_league_games = 1, games_per_hour = 2):
        self.league = league_dic # { subleague name : { division name : [team object] } }
        self.constraints = {
            "division_games" : division_games,
            "inter_div_games" : inter_division_games,
            "inter_league_games" : inter_league_games,
            "division_leaders" : 0,
            "wild_cards" : 0
            }
        self.day = 1
        self.schedule = {}
        self.series_length = 3 #can be changed
        self.game_length = None
        self.active = False
        self.games_per_hour = games_per_hour
        



    def season_reset(self):
        self.season += 1
        self.day = 1
        self.champion = None
        self.schedule = {}
        self.generate_schedule()
        save_league(self)

    def add_stats_from_game(self, players_dic):
        league_db.add_stats(self.name, players_dic)

    def update_standings(self, results_dic):
        league_db.update_standings(self.name, results_dic)


    def last_series_check(self):
        return str(math.ceil((self.day)/self.series_length) + 1) not in self.schedule.keys()

    def day_to_series_num(self, day):
        return math.ceil((self.day)/self.series_length)

    def tiebreaker_required(self):
        standings = {}
        matchups = []
        tournaments = []
        for team_name, wins, losses, run_diff in league_db.get_standings(self.name):
            standings[team_name] = {"wins" : wins, "losses" : losses, "run_diff" : run_diff}

        for subleague in iter(self.league.keys()):
            team_dic = {}          
            subleague_array = []
            wildcard_leaders = []
            for division in iter(self.league[subleague].keys()):
                division_standings = []
                division_standings += self.division_standings(self.league[subleague][division], standings)
                division_leaders = division_standings[:self.constraints["division_leaders"]]
                for division_team, wins, losses, diff, gb in division_standings[self.constraints["division_leaders"]:]:
                    if division_team.name != division_leaders[-1][0].name and standings[division_team.name]["wins"] == standings[division_leaders[-1][0].name]["wins"]:
                        matchups.append((division_team, division_standings[self.constraints["division_leaders"]-1][0], f"{division} Tiebreaker"))

                this_div_wildcard = [this_team for this_team, wins, losses, diff, gb in self.division_standings(self.league[subleague][division], standings)[self.constraints["division_leaders"]:]]
                subleague_array += this_div_wildcard
            if self.constraints["wild_cards"] > 0:
                wildcard_standings = self.division_standings(subleague_array, standings)
                wildcard_leaders = wildcard_standings[:self.constraints["wild_cards"]]
                for wildcard_team, wins, losses, diff, gb in wildcard_standings[self.constraints["wild_cards"]:]:
                    if wildcard_team.name != wildcard_leaders[-1][0].name and standings[wildcard_team.name]["wins"] == standings[wildcard_leaders[-1][0].name]["wins"]:
                        matchups.append((wildcard_team, wildcard_standings[self.constraints["wild_cards"]-1][0], f"{subleague} Wildcard Tiebreaker"))
        
        for team_a, team_b, type in matchups:
            tourney = tournament(f"{self.name} {type}",{team_a : {"wins" : 1}, team_b : {"wins" : 0}}, finals_series_length=1, secs_between_games=int(3600/self.games_per_hour), secs_between_rounds=int(7200/self.games_per_hour))
            tourney.build_bracket(by_wins = True)
            tourney.league = self
            tournaments.append(tourney)
        return tournaments

    def find_team(self, team_search):
        for subleague in iter(self.league.keys()):
            for division in iter(self.league[subleague].keys()):
                for team in self.league[subleague][division]:
                    if team.name == team_search.name:
                        return (subleague, division)
        return (None, None)

    def teams_in_league(self):
        teams = []
        for division in self.league.values():
            for teams_list in division.values():
                teams += teams_list
        return teams

    def team_names_in_league(self):
        teams = []
        for division in self.league.values():
            for teams_list in division.values():
                for team in teams_list:
                    teams.append(team.name)
        return teams

    def teams_in_subleague(self, subleague_name):
        teams = []
        if subleague_name in self.league.keys():
            for division_list in self.league[subleague_name].values():
                teams += division_list
            return teams
        else:
            print("League not found.")
            return None

    def teams_in_division(self, subleague_name, division_name):
        if subleague_name in self.league.keys() and division_name in self.league[subleague_name].keys():
            return self.league[subleague_name][division_name]
        else:
            print("Division in that league not found.")
            return None

    def make_matchups(self):
        matchups = []
        batch_subleagues = [] #each sub-array is all teams in each subleague
        subleague_max = 1
        league = deepcopy(self.league)
        for subleague in league.keys():
            teams = deepcopy(self.teams_in_subleague(subleague))
            if subleague_max < len(teams):
                subleague_max = len(teams)
            batch_subleagues.append(teams)

        for subleague in batch_subleagues:
            while len(subleague) < subleague_max:
                subleague.append("OFF")
   
        for i in range(0, self.constraints["inter_league_games"]): #generates inter-league matchups
            unmatched_indices = [i for i in range(0, len(batch_subleagues))]
            for subleague_index in range(0, len(batch_subleagues)):
                if subleague_index in unmatched_indices:
                    unmatched_indices.pop(unmatched_indices.index(subleague_index))
                    match_with_index = random.choice(unmatched_indices)
                    unmatched_indices.pop(unmatched_indices.index(match_with_index))
                    league_a = batch_subleagues[subleague_index].copy()
                    league_b = batch_subleagues[match_with_index].copy()
                    random.shuffle(league_a)
                    random.shuffle(league_b)
                    a_home = True
                    for team_a, team_b in zip(league_a, league_b):
                        if a_home:
                            matchups.append([team_b.name, team_a.name])
                        else:
                            matchups.append([team_a.name, team_b.name])
                        a_home = not a_home
                    
        for i in range(0, self.constraints["inter_div_games"]): #inter-division matchups
            extra_teams = []
            for subleague in league.keys():
                divisions = []
                for div in league[subleague].keys():
                    divisions.append(deepcopy(league[subleague][div]))

                #Check if there's an odd number of divisions
                last_div = None
                if len(divisions) % 2 != 0:
                    last_div = divisions.pop()

                #Get teams from half of the divisions
                divs_a = list(chain(divisions[int(len(divisions)/2):]))[0]
                if last_div is not None: #If there's an extra division, take half of those teams too
                    divs_a.extend(last_div[int(len(last_div)/2):])

                #Get teams from the other half of the divisions
                divs_b = list(chain(divisions[:int(len(divisions)/2)]))[0]
                if last_div is not None: #If there's an extra division, take the rest of those teams too
                    divs_b.extend(last_div[:int(len(last_div)/2)])

                #Ensure both groups have the same number of teams
                #Uness logic above changes, divs_a will always be one longer than divs_b or they'll be the same
                if len(divs_a) > len(divs_b):
                    divs_b.append(divs_a.pop())
    
                #Now we shuffle the groups
                random.shuffle(divs_a)
                random.shuffle(divs_b)
                
                #If there are an odd number of teams overall, then we need to remember the extra team for later
                if len(divs_a) < len(divs_b):
                    extra_teams.append(divs_b.pop())

                #Match up teams from each group
                a_home = True
                for team_a, team_b in zip(divs_a, divs_b):
                    if a_home:
                        matchups.append([team_b.name, team_a.name])
                    else:
                        matchups.append([team_a.name, team_b.name])
                    a_home = not a_home

            #Pair up any extra teams
            if extra_teams != []:
                if len(extra_teams) % 2 == 0:
                    for index in range(0, int(len(extra_teams)/2)):
                        matchups.append([extra_teams[index].name, extra_teams[index+1].name])
                        

        for subleague in league.keys():
            for division in league[subleague].values(): #generate round-robin matchups
                if len(division) % 2 != 0:
                    division.append("OFF")

                for i in range(0, len(division)-1):
                    teams_a = division[int(len(division)/2):]
                    teams_b = division[:int(len(division)/2)]
                    teams_b.reverse()

                    for team_a, team_b in zip(teams_a, teams_b):
                        if team_a != "OFF" and team_b != "OFF":
                            for j in range(0, self.constraints["division_games"]):                            
                                if i % 2 == 0:
                                    matchups.append([team_b.name, team_a.name])
                                else:
                                    matchups.append([team_a.name, team_b.name])

                            division.insert(1, division.pop())
        return matchups       
    
    def generate_schedule(self):
        matchups = self.make_matchups()
        random.shuffle(matchups)
        for game in matchups:
            scheduled = False      
            day = 1
            while not scheduled:
                found = False
                if str(day) in self.schedule.keys():
                    for game_on_day in self.schedule[str(day)]:
                        for team in game:
                            if team in game_on_day:
                                found = True
                    if not found:
                        self.schedule[str(day)].append(game)
                        scheduled = True
                else:
                    self.schedule[str(day)] = [game]
                    scheduled = True
                day += 1

        #now do forecasts
        for this_team in self.teams_in_league():
            start_weather = WeatherChains.starting_weather() #gets a random starting weather class
            start_weather_duration = random.randint(start_weather.duration_range[0], start_weather.duration_range[1])
            self.weather_forecast[this_team.name] = [start_weather.name] * start_weather_duration
            forecasted_days = []
            for i in range(start_weather_duration, len(self.schedule.keys())):
                if i not in forecasted_days:
                    prev_weather = self.weather_forecast[this_team.name][i-1] #get last weather name
                    next_weather = WeatherChains.chain_weather(all_weathers()[prev_weather]) #ask weatherchains for next weather
                    next_weather_duration = random.randint(next_weather.duration_range[0], next_weather.duration_range[1])
                    self.weather_forecast[this_team.name] += [next_weather.name] * next_weather_duration
                    forecasted_days = [n for n in range(i, i + next_weather_duration)]

    def new_weathers_midseason(self, team_name): #generate new forecast for specific team
        start_weather = WeatherChains.starting_weather() #gets a random starting weather class
        start_weather_duration = self.day - 1 if self.day > 1 else random.randint(start_weather.duration_range[0], start_weather.duration_range[1])
        self.weather_forecast[team_name] = [start_weather.name] * start_weather_duration
        forecasted_days = []
        for i in range(start_weather_duration, len(self.schedule.keys()) * self.series_length):
            if i not in forecasted_days:
                prev_weather = self.weather_forecast[team_name][i-1] #get last weather name
                next_weather = WeatherChains.chain_weather(all_weathers()[prev_weather]) #ask weatherchains for next weather
                next_weather_duration = random.randint(next_weather.duration_range[0], next_weather.duration_range[1])
                self.weather_forecast[team_name] += [next_weather.name] * next_weather_duration
                forecasted_days = [n for n in range(i, i + next_weather_duration)]

    def division_standings(self, division, standings):
        def sorter(team_in_list):
            if team_in_list[2] == 0 and team_in_list[1] == 0:
                return (0, team_in_list[3])
            return (team_in_list[1]/(team_in_list[1]+team_in_list[2]), team_in_list[3])

        teams = division.copy()
        
        for index in range(0, len(teams)):
            this_team = teams[index]
            teams[index] = [this_team, standings[teams[index].name]["wins"], standings[teams[index].name]["losses"], standings[teams[index].name]["run_diff"], 0]

        teams.sort(key=sorter, reverse=True)
        return teams

    def past_standings(self, season_num):
        this_embed = Embed(color=Color.purple(), title=self.name)
        standings = {}
        for team_name, wins, losses, run_diff in league_db.get_past_standings(self.name, season_num):
            standings[team_name] = {"wins" : wins, "losses" : losses, "run_diff" : run_diff}

        this_embed.add_field(name=league_db.get_past_champion(self.name, season_num), value=f"Season {season_num} champions", inline = False)

        for subleague in iter(self.league.keys()):
            this_embed.add_field(name="Subleague:", value=f"**{subleague}**", inline = False)
            for division in iter(self.league[subleague].keys()):
                teams = self.division_standings(self.league[subleague][division], standings)

                for index in range(0, len(teams)):
                    if index == self.constraints["division_leaders"] - 1:
                        teams[index][4] = "-"
                    else:
                        games_behind = ((teams[self.constraints["division_leaders"] - 1][1] - teams[index][1]) + (teams[index][2] - teams[self.constraints["division_leaders"] - 1][2]))/2
                        teams[index][4] = games_behind
                teams_string = ""
                for this_team in teams:
                    if this_team[2] != 0 or this_team[1] != 0:
                        teams_string += f"**{this_team[0].name}\n**{this_team[1]} - {this_team[2]} WR: {round(this_team[1]/(this_team[1]+this_team[2]), 3)} GB: {this_team[4]}\n\n"
                    else:
                        teams_string += f"**{this_team[0].name}\n**{this_team[1]} - {this_team[2]} WR: - GB: {this_team[4]}\n\n"

                this_embed.add_field(name=f"{division} Division:", value=teams_string, inline = False)
        
        this_embed.set_footer(text=f"Season {season_num} Final Standings")
        return this_embed

    def season_length(self):
        return int(list(self.schedule.keys())[-1]) * self.series_length
    
    def standings_embed(self):
        this_embed = Embed(color=Color.purple(), title=f"{self.name} Season {self.season}")
        standings = {}
        for team_name, wins, losses, run_diff in league_db.get_standings(self.name):
            standings[team_name] = {"wins" : wins, "losses" : losses, "run_diff" : run_diff}
        for subleague in iter(self.league.keys()):
            this_embed.add_field(name="Subleague:", value=f"**{subleague}**", inline = False)
            for division in iter(self.league[subleague].keys()):
                teams = self.division_standings(self.league[subleague][division], standings)

                for index in range(0, len(teams)):
                    if index == self.constraints["division_leaders"] - 1:
                        teams[index][4] = "-"
                    else:
                        games_behind = ((teams[self.constraints["division_leaders"] - 1][1] - teams[index][1]) + (teams[index][2] - teams[self.constraints["division_leaders"] - 1][2]))/2
                        teams[index][4] = games_behind
                teams_string = ""
                for this_team in teams:
                    if this_team[2] != 0 or this_team[1] != 0:
                        teams_string += f"**{this_team[0].name}\n**{this_team[1]} - {this_team[2]} WR: {round(this_team[1]/(this_team[1]+this_team[2]), 3)} GB: {this_team[4]}\n\n"
                    else:
                        teams_string += f"**{this_team[0].name}\n**{this_team[1]} - {this_team[2]} WR: - GB: {this_team[4]}\n\n"

                this_embed.add_field(name=f"{division} Division:", value=teams_string, inline = False)
        
        this_embed.set_footer(text=f"Standings as of day {self.day-1} / {self.season_length()}")
        return this_embed

    def standings_embed_div(self, division, div_name):
        this_embed = Embed(color=Color.purple(), title=f"{self.name} Season {self.season}")
        standings = {}
        for team_name, wins, losses, run_diff in league_db.get_standings(self.name):
            standings[team_name] = {"wins" : wins, "losses" : losses, "run_diff" : run_diff}
        teams = self.division_standings(division, standings)

        for index in range(0, len(teams)):
            if index == self.constraints["division_leaders"] - 1:
                teams[index][4] = "-"
            else:
                games_behind = ((teams[self.constraints["division_leaders"] - 1][1] - teams[index][1]) + (teams[index][2] - teams[self.constraints["division_leaders"] - 1][2]))/2
                teams[index][4] = games_behind
        teams_string = ""
        for this_team in teams:
            if this_team[2] != 0 or this_team[1] != 0:
                teams_string += f"**{this_team[0].name}\n**{this_team[1]} - {this_team[2]} WR: {round(this_team[1]/(this_team[1]+this_team[2]), 3)} GB: {this_team[4]}\n\n"
            else:
                teams_string += f"**{this_team[0].name}\n**{this_team[1]} - {this_team[2]} WR: - GB: {this_team[4]}\n\n"

        this_embed.add_field(name=f"{div_name} Division:", value=teams_string, inline = False)
        this_embed.set_footer(text=f"Standings as of day {self.day-1} / {self.season_length()}")
        return this_embed

    def wildcard_embed(self):
        this_embed = Embed(color=Color.purple(), title=f"{self.name} Wildcard Race")
        standings = {}
        for team_name, wins, losses, run_diff in league_db.get_standings(self.name):
            standings[team_name] = {"wins" : wins, "losses" : losses, "run_diff" : run_diff}
        for subleague in iter(self.league.keys()):
            subleague_array = []
            for division in iter(self.league[subleague].keys()):
                this_div = [this_team for this_team, wins, losses, diff, gb in self.division_standings(self.league[subleague][division], standings)[self.constraints["division_leaders"]:]]
                subleague_array += this_div

            teams = self.division_standings(subleague_array, standings)
            teams_string = ""
            for index in range(0, len(teams)):
                if index == self.constraints["wild_cards"] - 1:
                    teams[index][4] = "-"
                else:
                    games_behind = ((teams[self.constraints["wild_cards"] - 1][1] - teams[index][1]) + (teams[index][2] - teams[self.constraints["wild_cards"] - 1][2]))/2
                    teams[index][4] = games_behind

            for this_team in teams:
                if this_team[2] != 0 or this_team[1] != 0:
                    teams_string += f"**{this_team[0].name}\n**{this_team[1]} - {this_team[2]} WR: {round(this_team[1]/(this_team[1]+this_team[2]), 3)} GB: {this_team[4]}\n\n"
                else:
                    teams_string += f"**{this_team[0].name}\n**{this_team[1]} - {this_team[2]} WR: - GB: {this_team[4]}\n\n"

            this_embed.add_field(name=f"{subleague} League:", value=teams_string, inline = False)
        
        this_embed.set_footer(text=f"Wildcard standings as of day {self.day-1}")
        return this_embed

    def champ_series(self):
        tournaments = []
        standings = {}
        
        for team_name, wins, losses, run_diff in league_db.get_standings(self.name):
            standings[team_name] = {"wins" : wins, "losses" : losses, "run_diff" : run_diff}

        for subleague in iter(self.league.keys()):
            team_dic = {}
            division_leaders = []
            subleague_array = []
            wildcard_leaders = []
            for division in iter(self.league[subleague].keys()):
                division_leaders += self.division_standings(self.league[subleague][division], standings)[:self.constraints["division_leaders"]]                
                this_div_wildcard = [this_team for this_team, wins, losses, diff, gb in self.division_standings(self.league[subleague][division], standings)[self.constraints["division_leaders"]:]]
                subleague_array += this_div_wildcard
            if self.constraints["wild_cards"] > 0:
                wildcard_leaders = self.division_standings(subleague_array, standings)[:self.constraints["wild_cards"]]

            for this_team, wins, losses, diff, gb in division_leaders + wildcard_leaders:
                team_dic[this_team] = {"wins" : wins}
            
            subleague_tournament = tournament(f"{self.name} {subleague} Subleague Series", team_dic, series_length=3, finals_series_length=5, secs_between_games=int(3600/self.games_per_hour), secs_between_rounds=int(7200/self.games_per_hour))
            subleague_tournament.build_bracket(by_wins = True)
            subleague_tournament.league = self
            tournaments.append(subleague_tournament)

        return tournaments

    def stat_embed(self, stat_name):
        this_embed = Embed(color=Color.purple(), title=f"{self.name} Season {self.season} {stat_name} Leaders")
        stats = league_db.get_stats(self.name, stat_name.lower(), day = self.day)        
        if stats is None:
            return None
        else:
            stat_names = list(stats[0].keys())[2:]
            for index in range(0, min(10,len(stats))):
                this_row = list(stats[index])
                player_name = this_row.pop(0)
                content_string = f"**{this_row.pop(0)}**\n"
                for stat_index in range(0, len(this_row)):
                    content_string += f"**{stat_names[stat_index]}**: {str(this_row[stat_index])}; "
                this_embed.add_field(name=player_name, value=content_string, inline=False)
            return this_embed

    def get_weather_now(self, team_name):
        if self.weather_override is None or self.weather_event_duration <= 0: #if no override set or if past event expired
            if self.day < len(self.weather_forecast[team_name]) and random.random() < 0.08: #8% chance the forcast was wrong
                if random.random() < 0.33:
                    return all_weathers()[self.weather_forecast[team_name][self.day]] #next weather came a day early
                elif random.random() < 0.66:
                    return random.choice(WeatherChains.parent_weathers(all_weathers()[self.weather_forecast[team_name][self.day]])) #pivot to different parent weather to lead in
                else:
                    return WeatherChains.chain_weather(all_weathers()[self.weather_forecast[team_name][self.day - 1]]) #jump to a child weather for a day
            return all_weathers()[self.weather_forecast[team_name][self.day - 1]]
        else:
            if self.weather_event_duration == 1 and random.random() < 0.1: #once per weather event, roll for forecast regen
                self.new_weathers_midseason(team_name)
            return self.weather_override

    def weather_event_check(self): #2 for new event, 1 for continued event, 0 for no event
        if self.day - self.last_weather_event_day > 20: #arbitrary cooldown between weather events
            if random.random() < 0.05: #5% chance for weather event?
                self.weather_override = all_weathers()["Supernova"]
                self.last_weather_event_day = self.day
                self.weather_event_duration = random.randint(self.weather_override.duration_range[0], self.weather_override.duration_range[1])
                return 2
        else:
            self.weather_event_duration -= 1
            return 1 if self.weather_event_duration > 0 else 0


class tournament(object):
    def __init__(self, name, team_dic, series_length = 5, finals_series_length = 7, max_innings = 9, id = None, secs_between_games = 300, secs_between_rounds = 600): 
        self.name = name
        self.teams = team_dic #key: team object, value: wins
        self.bracket = None
        self.results = None
        self.series_length = series_length
        self.finals_length = finals_series_length
        self.game_length = max_innings
        self.active = False
        self.delay = secs_between_games
        self.round_delay = secs_between_rounds
        self.finals = False
        self.id = id
        self.league = None
        self.winner = None
        self.day = None

        if id is None:
            self.id = str(uuid4())
        else:
            self.id = id


    def build_bracket(self, random_sort = False, by_wins = False, manual = False):
        teams_list = list(self.teams.keys()).copy()

        if random_sort:
            def sorter(team_in_list):
                return random.random()

        elif by_wins:
            def sorter(team_in_list):
                return self.teams[team_in_list]["wins"] #sorts by wins

        else: #sort by average stars
            def sorter(team_in_list):
                return team_in_list.average_stars()
    
        if not manual:
            teams_list.sort(key=sorter, reverse=True)
        

        bracket_layers = int(math.ceil(math.log(len(teams_list), 2)))
        empty_slots = int(math.pow(2, bracket_layers) - len(teams_list))

        for i in range(0, empty_slots):
            teams_list.append(None)

        previous_bracket_layer = teams_list.copy()
        for i in range(0, bracket_layers - 1):
            this_layer = []
            for pair in range(0, int(len(previous_bracket_layer)/2)):
                if pair % 2 == 0: #if even number
                    this_layer.insert(0+int(pair/2), [previous_bracket_layer.pop(0), previous_bracket_layer.pop(-1)]) #every other pair goes at front of list, moving forward
                else:
                    this_layer.insert(0-int((1+pair)/2), [previous_bracket_layer.pop(int(len(previous_bracket_layer)/2)-1), previous_bracket_layer.pop(int(len(previous_bracket_layer)/2))]) #every other pair goes at end of list, moving backward
            previous_bracket_layer = this_layer
        self.bracket = bracket(previous_bracket_layer, bracket_layers)

    def round_check(self):
        if self.bracket.depth == 1:
            self.finals = True
            return True
        else:
            return False

class bracket(object):
    this_bracket = []

    def __init__(self, bracket_list, depth):
        self.this_bracket = bracket_list
        self.depth = depth
        self.bottom_row = []

    def get_bottom_row(self):
        self.depth = 1
        self.bottom_row = []
        self.dive(self.this_bracket)
        return self.bottom_row

    def dive(self, branch):
        if not isinstance(branch[0], list): #if it's a pair of games
            self.bottom_row.append(branch)
        else:
            self.depth += 1
            return self.dive(branch[0]), self.dive(branch[1])

    def set_winners_dive(self, winners_list, index = 0, branch = None, parent = None):
        if branch is None:
            branch = self.this_bracket.copy()
        if not isinstance(branch[0], list): #if it's a pair of games
            if branch[0].name in winners_list or branch[1] is None:
                winner = branch[0]
                if parent is not None:
                    parent[index] = winner
            elif branch[1].name in winners_list:
                winner = branch[1]
                if parent is not None:
                    parent[index] = winner        
        else:
            self.set_winners_dive(winners_list, index = 0, branch = branch[0], parent = branch)
            self.set_winners_dive(winners_list, index = 1, branch = branch[1], parent = branch)

        if parent is None:
            self.this_bracket = branch
            return branch

def save_league(this_league):
    if not league_db.league_exists(this_league.name):
        league_db.init_league_db(this_league)
        with open(os.path.join(data_dir, league_dir, this_league.name, f"{this_league.name}.league"), "w") as league_file:
            league_json_string = jsonpickle.encode(this_league.league, keys=True)
            json.dump(league_json_string, league_file, indent=4)
    league_db.save_league(this_league)

def save_league_as_new(this_league):
    league_db.init_league_db(this_league)
    with open(os.path.join(data_dir, league_dir, this_league.name, f"{this_league.name}.league"), "w") as league_file:
        league_json_string = jsonpickle.encode(this_league.league, keys=True)
        json.dump(league_json_string, league_file, indent=4)
    league_db.save_league(this_league)

def load_league_file(league_name):
    if league_db.league_exists(league_name):
        state = league_db.state(league_name)
        this_league = league_structure(league_name)
        with open(os.path.join(data_dir, league_dir, league_name, f"{this_league.name}.league")) as league_file:
            this_league.league = jsonpickle.decode(json.load(league_file), keys=True, classes=team)
        with open(os.path.join(data_dir, league_dir, league_name, f"{this_league.name}.state")) as state_file:
            state_dic = json.load(state_file)

        this_league.day = state_dic["day"]
        this_league.schedule = state_dic["schedule"]
        this_league.constraints = state_dic["constraints"]
        this_league.game_length = state_dic["game_length"]
        this_league.series_length = state_dic["series_length"]
        this_league.owner = state_dic["owner"]
        this_league.games_per_hour = state_dic["games_per_hour"]
        this_league.historic = state_dic["historic"]
        this_league.season = state_dic["season"]
        try:
            this_league.champion = state_dic["champion"]
        except:
            this_league.champion = None
        try: 
            this_league.weather_forecast = state_dic["forecasts"] #handles legacy leagues
        except: 
            this_league.weather_forecast = {}
            for this_team in this_league.teams_in_league(): #give them all fresh forecasts starting at current day
                this_league.new_weathers_midseason(this_team.name)
            save_league(this_league)
        try:
            this_league.last_weather_event_day = state_dic["last_weather_event"]
        except:
            this_league.last_weather_event_day = 0
        try:
            this_league.subbed_channels = state_dic["subs"]
        except:
            this_league.subbed_channels = []
        return this_league