# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.

# %% auto 0
__all__ = ['GameType', 'SetScoreType', 'SetEndType', 'MatchType', 'TourMatchType', 'Scorer', 'TiebreakScorer',
           'MatchTiebreakScorer', 'GamePointScorer', 'SetTracker', 'MatchTracker']

# %% ../nbs/00_core.ipynb 3
import random
import math

from enum import Enum


# %% ../nbs/00_core.ipynb 4
class GameType(Enum):
    """
    Attributes:
        AdScore:
        NoAdScore:
        FirstTo4:
        FirstTo7:
        FirstTo10:
    """

    ADSCORE = "adscore"
    NOADSCORE = "noadscore"
    FIRSTTO4 = "firstto4"
    FIRSTTO7 = "firstto7"
    FIRSTTO10 = "firstto10"

    def __str__(self):
        return self.value

    
class SetScoreType(Enum):
    """
    Attributes:
        Firstto4:
        Firstto6:
        Firstto9:
        Firstto12:
    """

    FIRSTTO4 = 4
    FIRSTTO6 = 6
    FIRSTTO9 = 9
    FIRSTTO10 = 10
    FIRSTTO12 = 12

    def __str__(self):
        return self.value
    
class SetEndType(Enum):
    """
    Attributes:
        Advantage:
        Tiebreak7:
        Tiebreak10:
        FirstTo:
    """
    ADVANTAGE = 0
    TIEBREAK7 = 7
    TIEBREAK10 = 10
    MATCHTIEBREAK = 10

#     ADVANTAGE = "advantage"
#     TIEBREAK7 = "tiebreak7"
#     TIEBREAK10 = "tiebreak10"
#     FIRSTTO = "firstto"

    def __str__(self):
        return self.value
    
class MatchType(Enum):
    pass
    
class TourMatchType(Enum):
    """
    Attributes:
        ATP_SINGLES:
        ATP_DOUBLES:
        ATP_MIXED:
        ATP_SINGLES_WIMBLEDON:
        ATP_SINGLES_FRENCHOPEN:
        ATP_SINGLES_AUSOPEN:
        ATP_SINGLES_USOPEN:
        ATP_DOUBLES_WIMBLEDON:
        ATP_DOUBLES_FRENCHOPEN:
        ATP_DOUBLES_AUSOPEN:
        ATP_DOUBLES_USOPEN:
        ATP_MIXED_WIMBLEDON:
        ATP_MIXED_FRENCHOPEN:
        ATP_MIXED_AUSOPEN:
        ATP_MIXED_USOPEN:
        WTA_SINGLES:
        WTA_DOUBLES:
        WTA_MIXED:
        WTA_SINGLES_WIMBLEDON:
        WTA_SINGLES_FRENCHOPEN:
        WTA_SINGLES_AUSOPEN:
        WTA_SINGLES_USOPEN:
        WTA_DOUBLES_WIMBLEDON:
        WTA_DOUBLES_FRENCHOPEN:
        WTA_DOUBLES_AUSOPEN:
        WTA_DOUBLES_USOPEN:
        WTA_MIXED_WIMBLEDON:
        WTA_MIXED_FRENCHOPEN:
        WTA_MIXED_AUSOPEN:
        WTA_MIXED_USOPEN:
    """
    #               Name, num_sets, SetScorer, SetEndtype, SetEndtypeFinalSet, GameType
    ATP_SINGLES = ('ATP_SINGLES', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.ADSCORE)
    ATP_DOUBLES = ('ATP_DOUBLES', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.NOADSCORE)

    ATP_SINGLES_WIMBLEDON = ('ATP_SINGLES_WIMBLEDON', 5, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO12, SetEndType.TIEBREAK7), GameType.ADSCORE)
    ATP_SINGLES_FRENCHOPEN = ('ATP_SINGLES_FRENCHOPEN', 5, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.ADVANTAGE), GameType.ADSCORE)
    ATP_SINGLES_AUSOPEN = ('ATP_SINGLES_AUSOPEN', 5, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK10), GameType.ADSCORE)
    ATP_SINGLES_USOPEN = ('ATP_SINGLES_USOPEN', 5, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.ADSCORE)
    ATP_DOUBLES_WIMBLEDON = ('ATP_DOUBLES_WIMBLEDON', 5, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO12, SetEndType.TIEBREAK7), GameType.ADSCORE)
    ATP_DOUBLES_FRENCHOPEN = ('ATP_DOUBLES_FRENCHOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.ADSCORE)
    ATP_DOUBLES_AUSOPEN = ('ATP_DOUBLES_AUSOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO10, SetEndType.ADVANTAGE), GameType.ADSCORE)
    ATP_DOUBLES_USOPEN = ('ATP_DOUBLES_USOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.ADSCORE)
    MIXED = ('MIXED', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), ((SetEndType.TIEBREAK10)), GameType.ADSCORE)
    MIXED_WIMBLEDON = ('MIXED_WIMBLEDON', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO12, SetEndType.TIEBREAK7), GameType.ADSCORE)
    MIXED_FRENCHOPEN = ('MIXED_FRENCHOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), ((SetEndType.TIEBREAK10)), GameType.ADSCORE)
    MIXED_AUSOPEN = ('MIXED_AUSOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO10, SetEndType.ADVANTAGE), GameType.NOADSCORE)
    MIXED_USOPEN = ('MIXED_USOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO10, SetEndType.ADVANTAGE), GameType.NOADSCORE)
    WTA_SINGLES = ('WTA_SINGLES', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.ADSCORE)
    WTA_DOUBLES = ('WTA_DOUBLES', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.ADSCORE)
    
    WTA_SINGLES_WIMBLEDON = ('WTA_SINGLES_WIMBLEDON', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO12, SetEndType.TIEBREAK7), GameType.ADSCORE)
    WTA_SINGLES_FRENCHOPEN = ('WTA_SINGLES_FRENCHOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.ADVANTAGE), GameType.ADSCORE)
    WTA_SINGLES_AUSOPEN = ('WTA_SINGLES_AUSOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK10), GameType.ADSCORE)
    WTA_SINGLES_USOPEN = ('WTA_SINGLES_USOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.ADSCORE)
    WTA_DOUBLES_WIMBLEDON = ('WTA_DOUBLES_WIMBLEDON', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO12, SetEndType.TIEBREAK7), GameType.NOADSCORE)
    WTA_DOUBLES_FRENCHOPEN = ('WTA_DOUBLES_FRENCHOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.ADSCORE)
    WTA_DOUBLES_AUSOPEN: ('WTA_DOUBLES_AUSOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK10), GameType.ADSCORE)
    WTA_DOUBLES_USOPEN = ('WTA_DOUBLES_USOPEN', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), GameType.ADSCORE)
    
    ATP_SINGLES_GRANDSLAM = ('ATP_SINGLES_WIMBLEDON', 5, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK10), GameType.ADSCORE)
    WTA_SINGLES_GRANDSLAM = ('WTA_SINGLES_WIMBLEDON', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK10), GameType.ADSCORE)
    
    DE_BTV = ('DE_BTV', 3, (SetScoreType.FIRSTTO6, SetEndType.TIEBREAK7), (SetScoreType.FIRSTTO10, SetEndType.MATCHTIEBREAK), GameType.ADSCORE)
        
    def __str__(self):
        return self.value
    
# Final set either games+open play or games until certain score and then tiebreak/matchtiebreak or direct matchtiebreak


# https://www.usta.com/en/home/improve/tips-and-instruction/national/tennis-scoring-rules.html
# Australian Open: Final set. First to 10 points, tiebreak at 6-6
# French Open: Final set. Advantage set, with no tiebreak
# Wimbledon: Final set. First to 7 points, tiebreak at 12-12
# US Open: Final set. First to 7 points, tiebreak at 6-6
# Mixed Final Set Tiebreak10

# %% ../nbs/00_core.ipynb 6
class Scorer:
    def __init__(self, home_score=0, away_score=0, score_diff=1):
        # init with score or start with 0-0
        self.home_score = home_score
        self.away_score = away_score
        self.score_diff = score_diff

    def increase(self, is_home, score_diff=None):
        if not self.finished:
            score_diff = score_diff if score_diff else self.score_diff
            if is_home:
                self.home_score += score_diff
            else:
                self.away_score += score_diff

    def decrease(self, is_home, score_diff=None):
        if not self.finished:
            score_diff = score_diff if score_diff else self.score_diff
            if is_home:
                self.home_score -= score_diff
            else:
                self.away_score -= score_diff

    def update(self, home_score, away_score):
        self.home_score = home_score
        self.away_score = away_score

    def __repr__(self):
        return f"{self.__class__.__name__}({self.home_score}, {self.away_score})"

    @property
    def finished(self):
        raise NotImplementedError('You need to implement this function in your Scorer')

    @property
    def is_winner_home(self):
        """returns winner:
           - still playing -> None
           - True: home won
           - False: away won
        """
        if self.finished:
            return self.home_score > self.away_score
        return None

    def get_score(self):
        return f"{self.home_score}:{self.away_score}"



class TiebreakScorer(Scorer):
    final_point = 7
    min_distance = 2

    def __init__(self, final_point=None, **kwargs):
        super().__init__(**kwargs)
        self.final_point = final_point if final_point else self.final_point

    @property
    def finished(self):
        # someone reaches seven points and is at least two points ahead
        if ((self.home_score >= self.final_point or self.away_score >= self.final_point)
           and abs(self.home_score - self.away_score) >= self.min_distance):
            return True

        return False
    
    def get_score(self, key="tiebreakScore"):
        return {key: {"a": self.home_score, "b": self.away_score}}

class MatchTiebreakScorer(Scorer):
    final_point = 10
    min_distance = 2

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    @property
    def finished(self):
        # someone reaches seven points and is at least two points ahead
        if ((self.home_score >= self.final_point or self.away_score >= self.final_point)
           and abs(self.home_score - self.away_score) >= self.min_distance):
            return True
        return False
    
    def get_score(self, key="matchTiebreakScore"):
        return {key: {"a": self.home_score, "b": self.away_score}}


# Need: Track winner, track stats

class GamePointScorer(Scorer):
    internal_to_game_mapping = {0: '0', 1:'15', 2:'30', 3:'40', 4:'Ad', 5:'F'}
    game_to_internal_mapping = {'0':0, '15':1, '30':2, '40':3, 'Ad':4, 'F':5}
    final_point = 4
    
    def __init__(self, home_score='0', away_score='0', score_diff=1, game_type=GameType.ADSCORE):
        # init with score or start with 0-0
        int_home_score = self.game_to_internal_mapping[home_score]
        int_away_score = self.game_to_internal_mapping[away_score]
        self.deciding_point = game_type == GameType.NOADSCORE
        super().__init__(int_home_score, int_away_score, score_diff)
        
    def increase(self, is_home, score_diff=None):
        super().increase(is_home, score_diff)                
        if self.home_score == self.final_point and self.away_score == self.final_point:
            self.home_score, self.away_score = self.final_point-1, self.final_point-1
            
    def update(self, home_score, away_score):
        try:
            self.home_score = self.game_to_internal_mapping[home_score]
            self.away_score = self.game_to_internal_mapping[away_score]
        except:
            raise ValueError('No valid score ({list(self.internal_to_game_mapping.values())})')
                      
    @property
    def finished(self):
        # someone reaches Ad point and is at least two points ahead
        max_score = max(self.home_score, self.away_score)
        min_score = min(self.home_score, self.away_score)
        diff_score = max_score - min_score
        
        if max_score >= 4:
            if diff_score>=2:
                return True
        if self.deciding_point:
            if max_score == 4 and min_score == 3:
                return True
            
        return False
    
    def __repr__(self):
        home_score = self.internal_to_game_mapping[self.home_score]
        away_score = self.internal_to_game_mapping[self.away_score]
        return f"{self.__class__.__name__}({home_score}, {away_score})"
    
    def get_score(self):
        home_score = self.internal_to_game_mapping[self.home_score]
        away_score = self.internal_to_game_mapping[self.away_score]
#         return f"{home_score}:{away_score}"
        return {"gameScore": {"a": home_score, "b": away_score}}


# Need to track winner of each GamePoint to decide when it is finished (return True/False should be enough)
class SetTracker(Scorer):
    min_distance = 2
    tiebreak = True # 6-6 -> Tiebreak

    def __init__(self, home_score=0, 
                 away_score=0, 
                 home_pointscore='0', 
                 away_pointscore='0',
                 score_diff=1, 
                 final_point=6, 
                 final_tiebreak_point=7, 
                 game_type=GameType.ADSCORE):
        super().__init__(home_score,away_score,score_diff)
        self.games = [GamePointScorer(game_type=game_type) for i in range(final_point*2)]
        self.games = self.games + [TiebreakScorer(final_point=final_tiebreak_point)]
        self.current_gamepoint = home_score + away_score
        self.games[self.current_gamepoint] = GamePointScorer(game_type=game_type, home_score=home_pointscore, away_score=away_pointscore)
        self.final_point = final_point
        #self.deciding_point = deciding_point
        #self.score_diff = score_diff
        #self.home_score = 0
        #self.away_score = 0
        # points[12] of PointTracker(deuce=True)
        # tiebreak of TiebreakTracker

    @property
    def is_winner_home(self):
        """returns winner:
           - still playing -> None
           - True: home won
           - False: away won
        """
        if self.finished:
            return self.home_score > self.away_score
        return None

    @property
    def current_game(self):
        return self.games[self.current_gamepoint]

    @property
    def finished(self):
        # someone reaches seven points and is at least two points ahead
        max_score = max(self.home_score, self.away_score)
        min_score = min(self.home_score, self.away_score)
        diff_score = max_score - min_score
        if max_score == self.final_point + 1:
            return True
        elif ((self.home_score == self.final_point or self.away_score == self.final_point)
            and diff_score>=2):
            return True

        return False

    def increase(self, is_home, score_diff=None):
        if not self.finished:
            # score_diff = score_diff if score_diff else self.score_diff
            self.current_game.increase(is_home, score_diff)
            if self.current_game.finished:
                if self.current_game.is_winner_home is not None:
                    if self.current_game.is_winner_home:
                        self.home_score+=1
                    else:
                        self.away_score+=1
                if self.current_gamepoint < len(self.games)-1:
                    self.current_gamepoint += 1


    def update(self, home_score, away_score):
        self.home_score = home_score
        self.away_score = away_score
        self.current_gamepoint = home_score + away_score

    def __repr__(self):
        return f"{self.__class__.__name__}({self.home_score}, {self.away_score}), {self.current_game}"

    def get_score(self, key="setScore"):
#         return f"{self.home_score}:{self.away_score}"
        out = {key: {"a": self.home_score, "b": self.away_score}}
        out.update(self.current_game.get_score())
        return out

# class MatchScorer(Scorer):
#     def __init__(self, 
#                  home_score=0, 
#                  away_score=0, 
#                  score_diff=1, 
#                  sets=None):
#         super().__init__(home_score,away_score,score_diff)
#         self.sets = sets
#         self.current_set_id = home_score + away_score

#     @property
#     def is_winner_home(self):
#         """returns winner:
#            - still playing -> None
#            - True: home won
#            - False: away won
#         """
#         if self.finished:
#             return self.home_score > self.away_score
#         return None

#     @property
#     def current_set(self):
#         return self.sets[self.current_set_id]

#     @property
#     def finished(self):
#         # someone reaches seven points and is at least two points ahead
#         max_score = max(self.home_score, self.away_score)
#         min_score = min(self.home_score, self.away_score)
#         if max_score == math.ceil(self.best_of/2):
#             return True
#         return False

#     def increase(self, is_home, score_diff=None):
#         if not self.finished:
#             # score_diff = score_diff if score_diff else self.score_diff
#             self.current_set.increase(is_home, score_diff)
#             if self.current_set.finished:
#                 if self.current_set.is_winner_home is not None:
#                     if self.current_set.is_winner_home:
#                         self.home_score+=1
#                     else:
#                         self.away_score+=1
#                 if (self.current_set_id < len(self.sets)-1) and not self.finished:
#                     self.current_set_id += 1


#     def update(self, home_score, away_score):
#         self.home_score = home_score
#         self.away_score = away_score
#         self.current_set_id = home_score + away_score

#     def __repr__(self):
#         return f"{self.__class__.__name__}({self.home_score}, {self.away_score}), {self.current_set}"

#     def get_score(self):
#         finished_sets = [set_.get_score() for i, set_ in enumerate(self.sets) if i <= self.current_set_id]
#         if hasattr(self.current_set,'current_game'):
#             return ";".join(finished_sets) + '-' + self.current_set.current_game.get_score()

#         return ";".join(finished_sets) + '-' + self.current_set.get_score()


class MatchTracker(Scorer):

    def __init__(self, home_score=0, away_score=0, score_diff=1, best_of=3, sets=None):
        super().__init__(home_score,away_score,score_diff)
        self.best_of = best_of
        self.sets = sets
#         self.match_tiebreak = match_tiebreak
#         self.sets = [SetTracker(deciding_point=deciding_point) for i in range(best_of-1)]
#         if self.match_tiebreak:
#             self.sets = self.sets + [MatchTiebreakScorer()]# might add MatchTiebreak here
#         else:
#             self.sets = self.sets + [SetTracker(deciding_point=deciding_point)]
        self.current_set_id = home_score + away_score

    @property
    def is_winner_home(self):
        """returns winner:
           - still playing -> None
           - True: home won
           - False: away won
        """
        if self.finished:
            return self.home_score > self.away_score
        return None

    @property
    def current_set(self):
        return self.sets[self.current_set_id]

    @property
    def finished(self):
        # someone reaches sets needed and is at least two points ahead
        max_score = max(self.home_score, self.away_score)
        min_score = min(self.home_score, self.away_score)
        if max_score == math.ceil(self.best_of/2):
            return True
        return False

    def increase(self, is_home, score_diff=None):
        if not self.finished:
            # score_diff = score_diff if score_diff else self.score_diff
            self.current_set.increase(is_home, score_diff)
            if self.current_set.finished:
                if self.current_set.is_winner_home is not None:
                    if self.current_set.is_winner_home:
                        self.home_score+=1
                    else:
                        self.away_score+=1
                if (self.current_set_id < len(self.sets)-1) and not self.finished:
                    self.current_set_id += 1


    def update(self, home_score, away_score):
        self.home_score = home_score
        self.away_score = away_score
        self.current_set_id = home_score + away_score

    def __repr__(self):
        return f"{self.__class__.__name__}({self.home_score}, {self.away_score}), {self.current_set}"
    
#     def init_state(self, sets=None):
#         if not sets:
#             self.current_set_id = 0
#             self.home_score = 0
#             self.away_score = 0
#             self._state = {"current_set": self.current_set_id+1}
#             return
        
#         # sets ordered  by setnumber
#         score = {"current_set": self.current_set_id+1} 
#         for i, set_ in enumerate(self.sets):
#             if i >= self.current_set_id:
#                 break
#             score.update(set_.get_score(key=f'set{i+1}Score'))
            
#         j = self.current_set_id + 1
#         current_score = self.current_set.get_score(key=f'set{j}Score')
#         score.update(current_score)
        
#         self._state = score
        

#     @property
#     def state(self):
#         return self._state
    
#     @state.setter
#     def state(self, value):
#         self.current_set_id = value["current_set"] - 1
        
# #         self._state = 
    
    

    @classmethod
    def from_matchtype(cls, matchtype=TourMatchType.ATP_SINGLES, **kwargs):
        name, best_of, setscore, final_setscore, game_type = matchtype.value

        setscore_type, setend_type = setscore
        final_setscore_type, final_setend_type = final_setscore
        
        if final_setend_type==SetEndType.ADVANTAGE:
            raise NotImplementedError('Final set played to AD not implemented')
        
#         import pdb; pdb.set_trace()
#         is_finalset_matchtiebreak=final_setend_type==SetEndType.MATCHTIEBREAK
        
        final_point = setscore_type.value
        final_tiebreak_point = setend_type.value
        final_set_point = final_setscore_type.value
        final_set_tiebreak_point = final_setend_type.value

        sets = [SetTracker(
            final_point=final_point, 
            final_tiebreak_point=final_tiebreak_point,
            game_type=game_type
        ) for i in range(best_of-1)]
        
#         if is_finalset_matchtiebreak:
#             final_set = [MatchTiebreakScorer()]
#         else:
        final_set = [SetTracker(final_point=final_set_point, final_tiebreak_point=final_set_tiebreak_point, game_type=game_type)]
        sets = sets + final_set
        
        if 'home_score' in kwargs and 'away_score' in kwargs:
            current_set = kwargs['home_score'] + kwargs['away_score']
            
            if 'home_set_score' in kwargs and 'away_set_score' in kwargs:
                sets[current_set] = SetTracker(final_point=final_set_point, 
                                               final_tiebreak_point=final_set_tiebreak_point, 
                                               game_type=game_type,
                                               home_score=home_set_score,
                                               away_score=away_set_score
                                              )
                kwargs.pop('home_set_score')
                kwargs.pop('away_set_score')
        
        instance = cls(
            best_of=best_of,
            sets=sets,
            **kwargs
        )

        return instance
    
    @classmethod
    def from_state(cls, state, **kwargs):
        matchtype = getattr(TourMatchType, state['matchtype'])
        name, best_of, setscore, final_setscore, game_type = matchtype.value

        setscore_type, setend_type = setscore
        final_setscore_type, final_setend_type = final_setscore
        
        if final_setend_type==SetEndType.ADVANTAGE:
            raise NotImplementedError('Final set played to AD not implemented')
        
#         import pdb; pdb.set_trace()
        is_finalset_matchtiebreak=final_setend_type==SetEndType.MATCHTIEBREAK
        
        final_point = setscore_type.value
        final_tiebreak_point = setend_type.value
        final_set_point = final_setscore_type.value
        final_set_tiebreak_point = final_setend_type.value
        
        sets = []
        for i in range(best_of-1):
            set_state = next(s for s in state['sets'] if s['setnumber']==i+1)
            # need to find setstate in correct order and apply to set creation w/ tiebreak  etc.
            set_args = dict(final_point=final_point, 
                final_tiebreak_point=final_tiebreak_point,
                game_type=game_type,
                home_score=set_state['setscore_home'],
                away_score=set_state['setscore_away'])
            
            if "gamescore_away" in set_state and "gamescore_home" in set_state:
                set_args.update({"home_pointscore": set_state["gamescore_home"],
                                 "away_pointscore": set_state["gamescore_away"]})
            set_ = SetTracker(
                **set_args
            )
            sets.append(set_)
        
        if is_finalset_matchtiebreak:
            final_set = [MatchTiebreakScorer()]
        else:
            final_set = [SetTracker(final_point=final_set_point, final_tiebreak_point=final_set_tiebreak_point, game_type=game_type)]
        sets = sets + final_set
        
        instance = cls(
            best_of=best_of,
            sets=sets,
            **kwargs
        )

        return instance
    
#     @classmethod
#     def AUSOPEN(cls):
#         BEST_OF=5
#         MATCH_TIEBREAK=True
#         FINAL_POINT=12

#         instance = cls(
#             match_tiebreak=MATCH_TIEBREAK,
#             best_of=BEST_OF)

# #         instance.sets = [SetTracker() for i in range(BEST_OF-1)]
# #         instance.sets = instance.sets + [SetTracker(final_point=FINAL_POINT)]
        
#         instance.sets = [SetTracker(deciding_point=False) for i in range(best_of)]

#         return instance

# MatchTracker.from_matchtype(matchtype=TourMatchType.ATP_SINGLES)

# class MatchTrackerWimbledon(MatchTracker):
#     BEST_OF=5
#     MATCH_TIEBREAK=False
#     FINAL_POINT=12

#     def __init__(self, **kwargs):
#         super().__init__(
#             match_tiebreak=self.MATCH_TIEBREAK,
#             best_of=self.BEST_OF,
#             **kwargs)

#         self.sets = [SetTracker() for i in range(self.BEST_OF-1)]
#         self.sets = self.sets + [SetTracker(final_point=self.FINAL_POINT)]
