from datetime import timedelta, datetime
from multiprocessing import Pool
from os import listdir, makedirs, mkdir
from os.path import isdir, isfile, exists, abspath, join
from sys import exc_info
from traceback import format_exception
from xml.etree.ElementTree import fromstring

from dateutil.parser import parse
from pytz import timezone
from requests import get

import baseball.process_game_json
from baseball.process_game_xml import MLB_TEAM_CODE_DICT

NUM_PROCESS_SUBLISTS = 3
BOXSCORE_SUFFIX = 'boxscore.xml'
PLAYERS_SUFFIX = 'players.xml'
INNING_SUFFIX = 'inning/inning_all.xml'

ALL_GAMES_URL = ('http://gdx.mlb.com/components/game/mlb/year_{year:04d}/'
                 'month_{month:02d}/day_{day:02d}/miniscoreboard.json')

GAME_URL_TEMPLATE = 'http://statsapi.mlb.com/api/v1.1/game/{game_pk}/feed/live'

MLB_URL_PATTERN = ('http://gd2.mlb.com/components/game/mlb/year_{year}/'
                   'month_{month}/day_{day}/gid_{year}_{month}_{day}_'
                   '{away_mlb_code}mlb_{home_mlb_code}mlb_{game_number}/')

HTML_INDEX_PAGE = (
    '<html>'
    '<head>'
    '<style>'
    '.clickme'
    '{{'
    'cursor: pointer;'
    'display: inline; padding-left: 10px; padding-right: 10px;'
    '}}'
    '.arrow {{'
    'border: solid white;'
    'border-width: 0 3px 3px 0;'
    'display: inline-block;'
    'padding: 3px;'
    '}}'
    '.right {{'
    'transform: rotate(-45deg);'
    '-webkit-transform: rotate(-45deg);'
    '}}'
    '.left {{'
    'transform: rotate(135deg);'
    '-webkit-transform: rotate(135deg);'
    '}}'
    '.up {{'
    'transform: rotate(-135deg);'
    '-webkit-transform: rotate(-135deg);'
    '}}'
    '.down {{'
    'transform: rotate(45deg);'
    '-webkit-transform: rotate(45deg);'
    '}}'
    '</style>'
    '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/'
    'jquery.min.js"></script>'
    '<link rel="icon" type="image/png" href="baseball-fairy-161.png" />'
    '<meta name="viewport" content="width=device-width, initial-scale=0.35">'
    '<meta http-equiv="cache-control" content="no-cache">'
    '<!-- Global site tag (gtag.js) - Google Analytics -->'
    '<script async src="https://www.googletagmanager.com/gtag/js'
    '?id=UA-108577160-1"></script>'
    '<script>'
    'window.dataLayer = window.dataLayer || [];'
    'function gtag(){{dataLayer.push(arguments);}}'
    'gtag("js", new Date());'
    'gtag("config", "UA-108577160-1");'
    '</script>'
    '<script>'
    'function gobackone() {{'
    'window.location = "./{yesterday_html}";'
    '}}'
    '</script>'
    '<script>'
    'function goforwardone() {{'
    'window.location = "./{tomorrow_html}";'
    '}}'
    '</script>'
    '<script>'
    '(function(h,o,u,n,d) {{ '
    'h=h[d]=h[d]||{{q:[],onReady:function(c){{h.q.push(c)}}}} \n'
    'd=o.createElement(u);d.async=1;d.src=n \n'
    'n=o.getElementsByTagName(u)[0];n.parentNode.insertBefore(d,n) '
    '}})(window,document,\'script\',\'https://www.datadoghq-browser-agent.com/'
    'datadog-rum.js\',\'DD_RUM\')\n'
    'DD_RUM.onReady(function() {{ '
    'DD_RUM.init({{ '
    'clientToken: \'pubddae1190ff21ff570dd36eaffc141c4d\', '
    'applicationId: \'8d72601e-b047-42da-af09-1ab422e756fa\', '
    'site: \'datadoghq.com\', '
    'service:\'get-todays-games\', '
    'env:\'none\', '
    'sampleRate: 100, '
    'trackInteractions: true, '
    '}})'
    '}})'
    '</script>'
    '<script>'
    'function gotogame() {{'
    'window.location = "./" + document.getElementById("year").value +'
    ' "-" + document.getElementById("month").value + "-" +'
    ' document.getElementById("day").value + ".html";'
    '}}'
    '</script>'
    '<title>Live Baseball Scorecards</title>'
    '</head>'
    '<body style="background-color:black;">'
    '<div id="header" style="width:1160px; margin:0 auto; '
    'text-align: center;">'
    '<img src="baseball-fairy-bat-250.png" height="250"><br />'
    '<font size="7" color="white">'
    'LiveBaseballScorecards.com'
    '</font><br />'
    '<font size="5" color="white">'
    'Contact us at '
    '<a href="mailto:livebaseballscorecards@gmail.com" '
    'style="color:lightblue">livebaseballscorecards@gmail.com</a>'
    '<br />'
    'For abbreviation definitions, hover your mouse over the scorecard '
    'text or just click <a style="color:lightblue" href="abbreviations.html">'
    'here</a>.'
    '<br /><br />'
    '<font size="6" color="white">Select a date</font>'
    '<br />'
    '<div class="clickme" onclick="gobackone()"><i class="arrow left"></i>'
    '</div>'
    '<select name="month" id="month">'
    '<option {month_list[0]} value="01">January</option>'
    '<option {month_list[1]} value="02">February</option>'
    '<option {month_list[2]} value="03">March</option>'
    '<option {month_list[3]} value="04">April</option>'
    '<option {month_list[4]} value="05">May</option>'
    '<option {month_list[5]} value="06">June</option>'
    '<option {month_list[6]} value="07">July</option>'
    '<option {month_list[7]} value="08">August</option>'
    '<option {month_list[8]} value="09">September</option>'
    '<option {month_list[9]} value="10">October</option>'
    '<option {month_list[10]} value="11">November</option>'
    '<option {month_list[11]} value="12">December</option>'
    '</select>'
    '<select name="day" id="day">'
    '<option {day_list[0]} value="01">1</option>'
    '<option {day_list[1]} value="02">2</option>'
    '<option {day_list[2]} value="03">3</option>'
    '<option {day_list[3]} value="04">4</option>'
    '<option {day_list[4]} value="05">5</option>'
    '<option {day_list[5]} value="06">6</option>'
    '<option {day_list[6]} value="07">7</option>'
    '<option {day_list[7]} value="08">8</option>'
    '<option {day_list[8]} value="09">9</option>'
    '<option {day_list[9]} value="10">10</option>'
    '<option {day_list[10]} value="11">11</option>'
    '<option {day_list[11]} value="12">12</option>'
    '<option {day_list[12]} value="13">13</option>'
    '<option {day_list[13]} value="14">14</option>'
    '<option {day_list[14]} value="15">15</option>'
    '<option {day_list[15]} value="16">16</option>'
    '<option {day_list[16]} value="17">17</option>'
    '<option {day_list[17]} value="18">18</option>'
    '<option {day_list[18]} value="19">19</option>'
    '<option {day_list[19]} value="20">20</option>'
    '<option {day_list[20]} value="21">21</option>'
    '<option {day_list[21]} value="22">22</option>'
    '<option {day_list[22]} value="23">23</option>'
    '<option {day_list[23]} value="24">24</option>'
    '<option {day_list[24]} value="25">25</option>'
    '<option {day_list[25]} value="26">26</option>'
    '<option {day_list[26]} value="27">27</option>'
    '<option {day_list[27]} value="28">28</option>'
    '<option {day_list[28]} value="29">29</option>'
    '<option {day_list[29]} value="30">30</option>'
    '<option {day_list[30]} value="31">31</option>'
    '</select>'
    '<select name="year" id="year">'
    '<option {year_list[2021]} value="2021">2021</option>'
    '<option {year_list[2020]} value="2020">2020</option>'
    '<option {year_list[2019]} value="2019">2019</option>'
    '<option {year_list[2018]} value="2018">2018</option>'
    '<option {year_list[2017]} value="2017">2017</option>'
    '<option {year_list[2016]} value="2016">2016</option>'
    '<option {year_list[2015]} value="2015">2015</option>'
    '<option {year_list[2014]} value="2014">2014</option>'
    '<option {year_list[2013]} value="2013">2013</option>'
    '<option {year_list[2012]} value="2012">2012</option>'
    '<option {year_list[2011]} value="2011">2011</option>'
    '<option {year_list[2010]} value="2010">2010</option>'
    '</select>'
    '<div class="clickme" onclick="goforwardone()"><i class="arrow right"></i>'
    '</div>'
    '<br />'
    '<button onclick="gotogame()">Select Date</button>'
    '<br /><br />'
    '<font size="6" color="white">{today_str}</font>'
    '</div>'
    '<br />'
    '<table cellpadding="10" style="width:1160px" align="center">'
    '{result_object_list_str}'
    '</table>'
    '</body>'
    '</html>'
)

HTML_WRAPPER = (
    '<html>'
    '<head>'
    '<title>{title}</title>'
    '<link rel="icon" type="image/png" href="baseball-fairy-161.png" />'
    '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/'
    'jquery.min.js"></script>'
    '<!-- Global site tag (gtag.js) - Google Analytics -->'
    '<script async src="https://www.googletagmanager.com/gtag/js?'
    'id=UA-108577160-1"></script>'
    '<script>'
    'window.dataLayer = window.dataLayer || [];function gtag()'
    '{{dataLayer.push(arguments);}}gtag(\'js\', new Date());gtag'
    '(\'config\', \'UA-108577160-1\');'
    '</script>'
    '<script>'
    '$(document).ready(function() {{'
    '$.get(\'{filename}\', function (data) {{'
    'document.getElementById("{title}").innerHTML = '
    'new XMLSerializer().serializeToString(data.documentElement);'
    '}});'
    'setInterval(function() {{'
    '$.get(\'{filename}\', function (data) {{'
    'document.getElementById("{title}").innerHTML = '
    'new XMLSerializer().serializeToString(data.documentElement);'
    '}});'
    '}}, 3000); '
    '}});'
    '</script>'
    '<script>'
    '(function(h,o,u,n,d) {{ '
    'h=h[d]=h[d]||{{q:[],onReady:function(c){{h.q.push(c)}}}} \n'
    'd=o.createElement(u);d.async=1;d.src=n \n'
    'n=o.getElementsByTagName(u)[0];n.parentNode.insertBefore(d,n) '
    '}})(window,document,\'script\',\'https://www.datadoghq-browser-agent.com/'
    'datadog-rum.js\',\'DD_RUM\')\n'
    'DD_RUM.onReady(function() {{ '
    'DD_RUM.init({{ '
    'clientToken: \'pubddae1190ff21ff570dd36eaffc141c4d\', '
    'applicationId: \'8d72601e-b047-42da-af09-1ab422e756fa\', '
    'site: \'datadoghq.com\', '
    'service:\'get-todays-games\', '
    'env:\'none\', '
    'sampleRate: 100, '
    'trackInteractions: true, '
    '}})'
    '}})'
    '</script>'
    '</head>'
    '<body style="background-color:black;">'
    '<div align="center">'
    '<object width="1160px" id="{title}"></object>'
    '</div>'
    '</body>'
    '</html>'
)

OBJECT_ENTRY_TEMPLATE = (
    '<script>'
    '$(document).ready(function() {{'
    '$.get(\'{game_id_str}.svg\', function (data) {{'
    'document.getElementById("{game_id_str}").innerHTML = new XMLSerializer().'
    'serializeToString(data.documentElement).replace(\'height="2256" \','
    ' \'height="735" \');'
    '}});'
    'setInterval(function() {{'
    '$.get(\'{game_id_str}.svg\', function (data) {{'
    'document.getElementById("{game_id_str}").innerHTML = new XMLSerializer().'
    'serializeToString(data.documentElement).replace(\'height="2256" \','
    ' \'height="735" \');'
    '}});'
    '}}, 10000);'
    '}});'
    '</script>'
    '<td valign="top"><div align="center">'
    '<a>'
    '<font size="5"><a style="color:lightblue; text-decoration: none;" '
    'href="{game_id_str}.html">{title_str}</a></font>'
    '</div>'
    '<br />'
    '<div align="center">'
    '<div align="center">'
    '<a href="./{game_id_str}.html" style="text-decoration:none">'
    '<object id="{game_id_str}"></object>'
    '</a>'
    '</div>'
    '</div>'
    '</td>'
)

GET_TODAY_GAMES_USAGE_STR = (
    'Usage:\n'
    '  - ./get_today_games.py [OUTPUT DIRECTORY]\n'
)

def get_today_date_str(this_datetime):
    today_date_str = '{}-{}-{}'.format(this_datetime.year,
                                       str(this_datetime.month).zfill(2),
                                       str(this_datetime.day).zfill(2))

    return today_date_str

def get_generated_html_id_list(game_id_list, today_date_str, output_dir,
                               write_game_html):
    if not exists(output_dir):
        makedirs(output_dir)

    output_path = abspath(output_dir)
    game_html_id_list = []

    for game_id in game_id_list:
        away_mlb_code = game_id.split('_')[-3][:3]
        home_mlb_code = game_id.split('_')[-2][:3]
        game_num_str = game_id.split('_')[-1]

        if (away_mlb_code in MLB_TEAM_CODE_DICT.values() and
                home_mlb_code in MLB_TEAM_CODE_DICT.values()):
            away_code = [key for key, val in MLB_TEAM_CODE_DICT.items()
                         if val == away_mlb_code][0]
            home_code = [key for key, val in MLB_TEAM_CODE_DICT.items()
                         if val == home_mlb_code][0]

            game_id, game = get_game_from_url(today_date_str,
                                              away_code,
                                              home_code,
                                              game_num_str)

            if game:
                write_game_svg_and_html(game_id, game, output_path,
                                        write_game_html)

                game_html_id_list.append(
                    '{}-{}-{}-{}'.format(today_date_str,
                                         away_code,
                                         home_code,
                                         game_num_str)
                )

    return game_html_id_list

def get_date_lists(this_datetime):
    year = this_datetime.year
    year_list = []
    for index in range(2500):
        if index == year:
            year_list.append('selected')
        else:
            year_list.append('')

    month = this_datetime.month
    month_list = []
    for index in range(12):
        if index == month - 1:
            month_list.append('selected')
        else:
            month_list.append('')

    day = this_datetime.day
    day_list = []
    for index in range(32):
        if index == day - 1:
            day_list.append('selected')
        else:
            day_list.append('')

    return year_list, month_list, day_list

def generate_game_svgs_for_2019_datetime(this_datetime, output_dir,
                                         write_game_html=False,
                                         write_date_html=False,
                                         write_index_html=False):
    if not exists(output_dir):
        mkdir(output_dir)

    today_date_str = get_today_date_str(this_datetime)
    month = int(this_datetime.month)
    day = int(this_datetime.day)
    year = int(this_datetime.year)
    all_games_dict = get(
        ALL_GAMES_URL.format(month=month, day=day, year=year)
    ).json()

    game_id_list = [
        game_dict['id'].replace('-', '_').replace('/', '_')
        for game_dict in all_games_dict['data']['games'].get('game', [])
    ]

    game_html_id_list = get_generated_html_id_list(game_id_list,
                                                   today_date_str,
                                                   output_dir,
                                                   write_game_html)



    object_html_str = get_object_html_str(game_html_id_list)

    write_game_index(object_html_str, this_datetime, output_dir,
                     write_date_html, write_index_html)

def get_object_html_str(game_html_id_list):
    object_html_str = ''
    for i, game_html_id in enumerate(game_html_id_list):
        game_id_element_list = game_html_id.split('-')
        title_str = '{} @ {} ({:04d}-{:02d}-{:02d}, {})'.format(
            game_id_element_list[3],
            game_id_element_list[4],
            int(game_id_element_list[0]),
            int(game_id_element_list[1]),
            int(game_id_element_list[2]),
            game_id_element_list[5]
        )

        if i % 2 == 0:
            object_html_str += '<tr>'

        object_html_str += OBJECT_ENTRY_TEMPLATE.format(
            title_str=title_str,
            game_id_str=game_html_id
        )

        if i % 2 == 1:
            object_html_str += '</tr>'

    return object_html_str

def write_games_for_date(this_datetime, output_dir, write_game_html=False,
                         write_date_html=False, write_index_html=False):
    if this_datetime.year >= 2020:
        generate_game_svgs_for_2020_datetime(this_datetime, output_dir,
                                             write_game_html, write_date_html,
                                             write_index_html)
    else:
        generate_game_svgs_for_2019_datetime(this_datetime, output_dir,
                                             write_game_html, write_date_html,
                                             write_index_html)

def generate_game_svgs_for_2020_datetime(this_datetime, output_dir,
                                         write_game_html, write_date_html,
                                         write_index_html):
    if not exists(output_dir):
        mkdir(output_dir)

    month = this_datetime.month
    day = this_datetime.day
    year = this_datetime.year
    all_games_dict = get(
        ALL_GAMES_URL.format(month=month, day=day, year=year)
    ).json()

    game_tuple_list = [(x['id'], x['game_pk'])
                       for x in all_games_dict['data']['games'].get('game', [])]

    game_dict_list = [get(GAME_URL_TEMPLATE.format(game_pk=game_pk)).json()
                      for _, game_pk in game_tuple_list]

    game_html_id_list = []
    for game_dict in game_dict_list:
        try:
            game = baseball.process_game_json.get_game_obj(game_dict)
            if len(game.game_date_str.split('-')) == 6:
                game_html_id_list.append(game.game_date_str)
            else:
                game.game_date_str = '{:04d}-{:02d}-{:02d}-{}'.format(
                    year, month, day, game.game_date_str
                )

                if len(game.game_date_str.split('-')) == 6:
                    game_html_id_list.append(game.game_date_str)

            write_game_svg_and_html(game.game_date_str, game, output_dir,
                                    write_game_html)
        except:
            exc_type, exc_value, exc_traceback = exc_info()
            lines = format_exception(exc_type, exc_value, exc_traceback)
            exception_str = ' '.join(lines)
            print('{} {} {}'.format(datetime.utcnow(),
                                    game_dict['gameData']['game']['id'],
                                    exception_str))

    object_html_str = get_object_html_str(game_html_id_list)

    write_game_index(object_html_str, this_datetime, output_dir,
                     write_date_html, write_index_html)

def write_game_index(object_html_str, this_datetime, output_dir,
                     write_date_html, write_index_html):
    year_list, month_list, day_list = get_date_lists(this_datetime)
    today_str = '{} {}, {}'.format(this_datetime.strftime("%B"),
                                   this_datetime.day,
                                   this_datetime.year)

    yesterday = this_datetime - timedelta(days=1)
    tomorrow = this_datetime + timedelta(days=1)
    yesterday_html = '{:04d}-{:02d}-{:02d}.html'.format(int(yesterday.year),
                                                        int(yesterday.month),
                                                        int(yesterday.day))

    tomorrow_html = '{:04d}-{:02d}-{:02d}.html'.format(int(tomorrow.year),
                                                       int(tomorrow.month),
                                                       int(tomorrow.day))

    output_filename = '/{:04d}-{:02d}-{:02d}.html'.format(
        int(this_datetime.year),
        int(this_datetime.month),
        int(this_datetime.day)
    )

    output_html = HTML_INDEX_PAGE.format(result_object_list_str=object_html_str,
                                         month_list=month_list,
                                         day_list=day_list,
                                         year_list=year_list,
                                         yesterday_html=yesterday_html,
                                         tomorrow_html=tomorrow_html,
                                         today_str=today_str,
                                         output_filename=output_filename)

    if object_html_str or not exists(output_dir + output_filename):
        if write_date_html:
            write_location = output_dir + output_filename
            with open(write_location, 'w', encoding='utf-8') as fh:
                fh.write(output_html)

        if write_index_html:
            write_location = output_dir + '/index.html'
            with open(write_location, 'w', encoding='utf-8') as fh:
                fh.write(output_html)

def get_game_from_date(this_datetime, this_away_code, this_home_code,
                       this_game_number):
    month = this_datetime.month
    day = this_datetime.day
    year = this_datetime.year
    all_games_dict = get(
        ALL_GAMES_URL.format(month=month, day=day, year=year)
    ).json()

    game_tuple_list = [
        (x['id'], x['game_pk'])
        for x in all_games_dict['data']['games'].get('game', [])
    ]

    game_dict_list = [get(GAME_URL_TEMPLATE.format(game_pk=game_pk)).json()
                      for _, game_pk in game_tuple_list]

    game = None
    for game_dict in game_dict_list:
        away_code = (
            game_dict.get('gameData', {}).get('teams', {}).get(
                'away', {}).get('abbreviation', {})
        )

        home_code = (
            game_dict.get('gameData', {}).get('teams', {}).get(
                'home', {}).get('abbreviation', {})
        )

        game_number_is_match = (
            this_game_number == game_dict.get('gameData', {}).get(
                'game', {}).get('gameNumber', {})
        )

        if ((away_code and home_code) and (away_code == this_away_code) and
                (home_code == this_home_code) and game_number_is_match):
            game = baseball.process_game_json.get_game_obj(game_dict)

    return game

def get_formatted_date_str(input_date_str):
    this_date = parse(input_date_str)
    this_date_str = '{}-{}-{}'.format(str(this_date.year),
                                      str(this_date.month).zfill(2),
                                      str(this_date.day).zfill(2))

    return this_date_str

def write_game_svg_and_html(game_id, game, output_path, write_html=False):
    svg_filename = game_id + '.svg'
    html_filename = game_id + '.html'

    svg_text = game.get_svg_str()
    html_text = HTML_WRAPPER.format(title=game_id, filename=svg_filename)

    output_svg_path = join(output_path, svg_filename)
    output_html_path = join(output_path, html_filename)

    with open(output_svg_path, 'w') as filehandle:
        filehandle.write(svg_text)

    if write_html:
        with open(output_html_path, 'w') as filehandle:
            filehandle.write(html_text)

def get_game_from_files(boxscore_file, player_file, inning_file):
    this_game = None

    try:
        if (isfile(boxscore_file) and isfile(player_file) and
                isfile(inning_file)):
            boxscore_raw = open(boxscore_file, 'r', encoding='utf-8').read()
            boxscore_xml = fromstring(boxscore_raw)
            player_raw = open(player_file, 'r', encoding='utf-8').read()
            player_xml = fromstring(player_raw)
            inning_raw = open(inning_file, 'r', encoding='utf-8').read()
            inning_xml = fromstring(inning_raw)
            this_game = baseball.process_game_xml.get_game_obj(boxscore_xml,
                                                               player_xml,
                                                               inning_xml)
    except:
        exc_type, exc_value, exc_traceback = exc_info()
        lines = format_exception(exc_type, exc_value, exc_traceback)
        exception_str = ' '.join(lines)
        print('{} ({} {} {}) {}'.format(datetime.utcnow(),
                                        boxscore_file,
                                        player_file,
                                        inning_file,
                                        exception_str))

    return this_game

def get_game_from_filename_tuple(filename_tuple):
    game_id, boxscore_file, player_file, inning_file = filename_tuple
    game = get_game_from_files(boxscore_file, player_file, inning_file)

    return game_id, game

def get_game_generator(filename_list):
    for filename_tuple in filename_list:
        game_id, this_game = get_game_from_filename_tuple(filename_tuple)
        if this_game:
            yield game_id, this_game

def write_game_svg_html_from_filename_tuple(filename_output_path_tuple):
    filename_tuple, output_path = filename_output_path_tuple
    game_id, game = get_game_from_filename_tuple(filename_tuple)
    if game:
        print(game_id)
        write_game_svg_and_html(game_id, game, output_path, True)

def get_game_from_xml_strings(boxscore_raw_xml, players_raw_xml,
                              inning_raw_xml):
    this_game = None
    if (boxscore_raw_xml and players_raw_xml and inning_raw_xml and
            boxscore_raw_xml != '{"message": "Internal server error"}' and
            players_raw_xml != '{"message": "Internal server error"}' and
            inning_raw_xml != '{"message": "Internal server error"}'):
        boxscore_xml_obj = fromstring(boxscore_raw_xml)
        players_xml_obj = fromstring(players_raw_xml)
        inning_xml_obj = fromstring(inning_raw_xml)
        this_game = baseball.process_game_xml.get_game_obj(boxscore_xml_obj,
                                                           players_xml_obj,
                                                           inning_xml_obj)

    return this_game

def write_svg_from_file_range(start_date_str, end_date_str, input_dir,
                              output_dir):
    if not exists(output_dir):
        makedirs(output_dir)

    output_path = abspath(output_dir)
    filename_output_path_tuple_list = [
        (filename_tuple, output_path)
        for filename_tuple in get_filename_list(start_date_str,
                                                end_date_str,
                                                input_dir)
    ]

    for tup in filename_output_path_tuple_list:
        write_game_svg_html_from_filename_tuple(tup)

def get_filename_list(start_date_str, end_date_str, input_dir):
    filename_list = []
    input_path = abspath(input_dir)
    start_date = parse(start_date_str)
    end_date = parse(end_date_str)
    day_delta = timedelta(days=1)
    this_date = start_date
    while this_date < end_date + day_delta:
        year = str(this_date.year)
        month = str(this_date.month).zfill(2)
        day = str(this_date.day).zfill(2)
        filename = '{}/{}/month_{}/day_{}/'.format(input_path, year, month, day)
        if isdir(filename):
            file_list = listdir(filename)
            if file_list:
                for subfile in file_list:
                    if subfile.startswith('gid_'):
                        away_code, home_code, game_num = subfile.split('_')[-3:]
                        away_code = away_code[:-3]
                        home_code = home_code[:-3]
                        away_team, home_team = None, None
                        for key, value in MLB_TEAM_CODE_DICT.items():
                            if value == away_code:
                                away_team = key

                            if value == home_code:
                                home_team = key

                        if away_team and home_team:
                            output_name = '-'.join([year, month, day, away_team,
                                                    home_team, game_num])

                            subfolder_name = filename + subfile + '/'
                            if listdir(subfolder_name):
                                player_filename = subfolder_name + 'players.xml'
                                boxscore_filename = (subfolder_name +
                                                     'boxscore.xml')

                                inning_filename = (subfolder_name +
                                                   'inning/inning_all.xml')

                                filename_list.append((output_name,
                                                      boxscore_filename,
                                                      player_filename,
                                                      inning_filename))

        this_date += day_delta

    return filename_list

def get_game_list_from_file_range(start_date_str, end_date_str, input_dir):
    filename_list = get_filename_list(start_date_str, end_date_str, input_dir)
    process_pool = Pool(NUM_PROCESS_SUBLISTS)
    #game_tuple_list = process_pool.map(get_game_from_filename_tuple,
    #                                   filename_list)
    game_tuple_list = [get_game_from_filename_tuple(filename_tuple)
                       for filename_tuple in filename_list]

    return game_tuple_list

def get_game_generator_from_file_range(start_date_str, end_date_str, input_dir):
    filename_list = get_filename_list(start_date_str, end_date_str, input_dir)

    return get_game_generator(filename_list)

def write_svg_from_url(date_str, away_code, home_code, game_number, output_dir):
    if not exists(output_dir):
        makedirs(output_dir)

    output_path = abspath(output_dir)
    game_id, game = get_game_from_url(date_str, away_code, home_code,
                                      game_number)

    write_game_svg_and_html(game_id, game, output_path, False)

def get_game_dict_from_url(date_str, away_code, home_code, game_number):
    formatted_date_str = get_formatted_date_str(date_str)
    this_datetime = parse(formatted_date_str)
    month = this_datetime.month
    day = this_datetime.day
    year = this_datetime.year

    all_games_dict = get(
        ALL_GAMES_URL.format(month=month, day=day, year=year)
    ).json()

    game_tuple_list = [(x['id'], x['game_pk'])
                       for x in all_games_dict['data']['games'].get('game', [])]

    game_dict_list = [get(GAME_URL_TEMPLATE.format(game_pk=game_pk)).json()
                      for _, game_pk in game_tuple_list]

    return_dict = {}
    game_id = None
    for game_dict in game_dict_list:
        this_away_code = (
            game_dict.get('gameData', {}).get('teams', {}).get(
                'away', {}).get('abbreviation', {})
        )

        this_home_code = (
            game_dict.get('gameData', {}).get('teams', {}).get(
                'home', {}).get('abbreviation', {})
        )

        game_number_is_match = (
            game_number == game_dict.get('gameData', {}).get(
                'game', {}).get('gameNumber', {})
        )

        if ((this_away_code and this_home_code) and
                (away_code == this_away_code) and
                (home_code == this_home_code) and
                game_number_is_match):

            game_id = '-'.join(
                [formatted_date_str, away_code, home_code, str(game_number)]
            )

            return_dict = game_dict
            break

    return game_id, return_dict

def get_game_xml_from_url(date_str, away_code, home_code, game_number):
    formatted_date_str = get_formatted_date_str(date_str)
    date = parse(formatted_date_str)

    game_id = '-'.join(
        [formatted_date_str, away_code, home_code, str(game_number)]
    )

    request_url_base = MLB_URL_PATTERN.format(
        year=date.year,
        month=str(date.month).zfill(2),
        day=str(date.day).zfill(2),
        away_mlb_code=MLB_TEAM_CODE_DICT[away_code],
        home_mlb_code=MLB_TEAM_CODE_DICT[home_code],
        game_number=game_number
    )

    boxscore_request_text = get(request_url_base + BOXSCORE_SUFFIX).text
    if boxscore_request_text == 'GameDay - 404 Not Found':
        boxscore_raw_xml, players_raw_xml, inning_raw_xml = None, None, None
    else:
        boxscore_raw_xml = boxscore_request_text
        players_raw_xml = get(request_url_base + PLAYERS_SUFFIX).text
        inning_raw_xml = get(request_url_base + INNING_SUFFIX).text

    return game_id, boxscore_raw_xml, players_raw_xml, inning_raw_xml

def get_game_from_url_2020(date_str, this_date, away_code, home_code,
                           game_number):
    this_game = get_game_from_date(this_date, away_code, home_code,
                                   game_number)

    formatted_date_str = get_formatted_date_str(date_str)
    game_id = '-'.join(
        [formatted_date_str, away_code, home_code, str(game_number)]
    )

    if not this_game:
        print('No data found for {} {} {} {}'.format(date_str,
                                                     away_code,
                                                     home_code,
                                                     game_number))

    return game_id, this_game

def get_game_from_url_2019(date_str, away_code, home_code, game_number):
    (game_id,
     boxscore_raw_xml,
     players_raw_xml,
     inning_raw_xml) = get_game_xml_from_url(date_str,
                                             away_code,
                                             home_code,
                                             game_number)

    this_game = get_game_from_xml_strings(boxscore_raw_xml,
                                          players_raw_xml,
                                          inning_raw_xml)

    if not this_game:
        print('No data found for {} {} {} {}'.format(date_str,
                                                     away_code,
                                                     home_code,
                                                     game_number))

        prefix = '/var/log/baseball/no-data-{}-'.format(
            '-'.join(str(datetime.now()).split())
        )
        with open(prefix + 'boxscore.xml', 'w') as fh:
            fh.write(boxscore_raw_xml)
        with open(prefix + 'players.xml', 'w') as fh:
            fh.write(players_raw_xml)
        with open(prefix + 'inning_all.xml', 'w') as fh:
            fh.write(inning_raw_xml)

    return game_id, this_game

def get_game_from_url(date_str, away_code, home_code, game_number):
    this_game = None
    game_id = None
    try:
        this_date = parse(date_str)
        if int(this_date.year) >= 2020:
            game_id, this_game = get_game_from_url_2020(
                date_str, this_date, away_code, home_code, game_number
            )
        else:
            game_id, this_game = get_game_from_url_2019(
                date_str, away_code, home_code, game_number
            )
    except:
        exc_type, exc_value, exc_traceback = exc_info()
        lines = format_exception(exc_type, exc_value, exc_traceback)
        exception_str = ' '.join(lines)
        print('{} ({}-{}-{}-{}) {}'.format(datetime.utcnow(),
                                           date_str,
                                           away_code,
                                           home_code,
                                           game_number,
                                           exception_str))

    return game_id, this_game

def generate_today_game_svgs(output_dir, write_game_html=False,
                             write_date_html=False, write_index_html=False):
    time_shift = timedelta(hours=7)
    today_datetime = datetime.utcnow() - time_shift
    try:
        write_games_for_date(
            today_datetime.astimezone(timezone('America/New_York')),
            output_dir,
            write_game_html,
            write_date_html,
            write_index_html
        )
    except:
        exc_type, exc_value, exc_traceback = exc_info()
        lines = format_exception(exc_type, exc_value, exc_traceback)
        exception_str = ' '.join(lines)
        print('{} ({}) {}'.format(datetime.utcnow(),
                                  str(today_datetime),
                                  exception_str))
