import sys
import os
import json
import asyncio

from js import document

def _get_base_url():
    try:
        from js import globalThis
        loc = globalThis.location
        return loc.origin + "/"
    except Exception:
        pass
    try:
        from js import document
        return document.location.origin + "/"
    except Exception:
        return ""

_BASE_URL = _get_base_url()

def _abs_url(path):
    if path.startswith("http://") or path.startswith("https://"):
        return path
    return _BASE_URL + path

def _update_progress(label, pct):
    try:
        status_el = document.getElementById("load-status")
        bar_el = document.getElementById("load-bar")
        if status_el:
            status_el.textContent = label
        if bar_el:
            bar_el.style.width = f"{int(pct)}%"
    except Exception:
        pass

def _hide_loading():
    try:
        el = document.getElementById("loading")
        if el:
            el.style.display = "none"
    except Exception:
        pass

async def _fetch_to_vfs(server_path, vfs_path):
    abs_url = _abs_url(server_path)
    try:
        from js import globalThis
        resp = await globalThis.fetch(abs_url)
    except Exception:
        try:
            from js import fetch as js_fetch
            resp = await js_fetch(abs_url)
        except Exception:
            from js import window
            resp = await window.fetch(abs_url)
    if not resp.ok:
        raise RuntimeError(f"HTTP {resp.status} for {abs_url}")
    blob = await resp.blob()
    ab = await blob.arrayBuffer()
    import js as _js
    arr = _js.Uint8Array.new(ab)
    data = bytes(arr.to_py())
    d = os.path.dirname(vfs_path)
    if d:
        os.makedirs(d, exist_ok=True)
    with open(vfs_path, "wb") as f:
        f.write(data)

async def _fetch_batch(files, batch_size, label):
    sem = asyncio.Lock()
    done = [0]
    total = len(files)
    async def _one(vfs_path, server_path):
        try:
            await _fetch_to_vfs(server_path, vfs_path)
        except Exception as e:
            print(f"Warning: failed to load {server_path}: {e}")
        async with sem:
            done[0] += 1
            if done[0] % 5 == 0 or done[0] == total:
                _update_progress(f"{label} ({done[0]}/{total})", int(done[0] * 100 / total))
    chunks = [files[i:i+batch_size] for i in range(0, len(files), batch_size)]
    for chunk in chunks:
        await asyncio.gather(*[_one(v, s) for v, s in chunk])

async def _preload_essential():
    _update_progress("Loading game data...", 0)
    await _fetch_to_vfs("rounds.json", "rounds.json")

    essential = []
    map_names = [
        "cap_kingdom.jpg", "cascade_kingdom.jpg", "sand_kingdom.jpg",
        "lake_kingdom.jpg", "wooded_kingdom.jpg", "cloud_kingdom.jpg",
        "lost_kingdom.jpg", "metro_kingdom.jpg", "seaside_kingdom.jpg",
        "snow_kingdom.jpg", "luncheon_kingdom.jpg", "ruined_kingdom.jpg",
        "bowsers_kingdom.jpg", "moon_kingdom.jpg", "mushroom_kingdom.jpg",
        "dark_side.jpg", "darker_side.jpg", "empty_map.jpg",
    ]
    for m in map_names:
        essential.append((f"maps/{m}", f"maps/{m}"))

    misc_files = [
        "Fredoka-Bold.ttf", "mario_face.png",
        "sound_button_down.mp3", "sound_button_up.mp3",
        "place_pin_sound.mp3", "slider_sound.mp3", "view_changer_sound.mp3",
        "menu_music.mp3", "gameplay_music.mp3",
        "minimap_button.png", "highlighted_minimap_button.png",
        "world_view_button.png", "highlighted_world_view_button.png",
        "minimap_pin.png", "flag_pin.png",
        "default_submit_button.png", "highlighted_default_submit_button.png",
        "submit_button.png", "highlighted_submit_button.png",
        "next_round_button.png", "highlighted_next_round_button.png",
        "play_button.png", "highlighted_play_button.png",
        "how_to_play_button.png", "highlighted_how_to_play_button.png",
        "quit_button.png", "highlighted_quit_button.png",
        "singleplayer_button.png", "highlighted_singleplayer_button.png", "selected_singleplayer_button.png",
        "local_multiplayer_button.png", "highlighted_local_multiplayer_button.png", "selected_local_multplayer_button.png",
        "back_button.png", "highlighted_back_button.png",
        "hint_button.png", "highlighted_hint_button.png",
        "main_menu_button.png", "highlighted_main_menu_button.png",
        "disclaimer_button.png", "highlighted_disclaimer_button.png",
        "sound_icon.png", "mute_icon.png",
        "player_two_pin.png", "player_three_pin.png", "player_four_pin.png",
        "player_five_pin.png", "player_six_pin.png", "player_seven_pin.png",
        "player_eight_pin.png", "player_nine_pin.png", "player_ten_pin.png",
        "cap.png", "selected_cap.png",
        "cascade.png", "selected_cascade.png",
        "sand.png", "selected_sand.png",
        "lake.png", "selected_lake.png",
        "wooded.png", "selected_wooded.png",
        "cloud.png", "selected_cloud.png",
        "lost.png", "selected_lost.png",
        "metro.png", "selected_metro.png",
        "seaside.png", "selected_seaside.png",
        "snow.png", "selected_snow.png",
        "luncheon.png", "selected_luncheon.png",
        "ruined.png", "selected_ruined.png",
        "bowsers.png", "selected_bowsers.png",
        "moon.png", "selected_moon.png",
        "mushroom.png", "selected_mushroom.png",
        "dark_side.png", "selected_dark_side.png",
        "darker_side.png", "selected_darker_side.png",
    ]
    for m in misc_files:
        essential.append((f"misc/{m}", f"misc/{m}"))

    await _fetch_batch(essential, 10, "Loading maps and UI")

async def _preload_screenshots():
    with open("rounds.json", "r") as f:
        rounds_data = json.load(f)
    seen = set()
    screenshots = []
    for entry in rounds_data:
        s = entry["screenshot"]
        if s not in seen:
            seen.add(s)
            screenshots.append((s, s))
    if screenshots:
        await _fetch_batch(screenshots, 20, "Loading screenshots")
    try:
        from game import game
        game.set_menu_background()
    except Exception:
        pass

async def _preload_first_batch():
    with open("rounds.json", "r") as f:
        rounds_data = json.load(f)
    seen = set()
    screenshots = []
    for entry in rounds_data:
        s = entry["screenshot"]
        if s not in seen:
            seen.add(s)
            screenshots.append((s, s))
    first_batch = screenshots[:20]
    if first_batch:
        await _fetch_batch(first_batch, 10, "Loading screenshots")
    return screenshots[20:]

async def _main():
    try:
        from js import window as _w
        _ver = "?v=" + str(int(_w._BUILD_TS))
    except Exception:
        _ver = "?v=4"
    await _preload_essential()
    py_files = ["network_client.py", "ui_widgets.py", "game.py"]
    for pf in py_files:
        await _fetch_to_vfs(pf + _ver, pf)
    _update_progress("Loading screenshots...", 90)
    remaining = await _preload_first_batch()
    _update_progress("Initializing game...", 98)
    from game import game
    game.set_menu_background()
    _hide_loading()
    if remaining:
        asyncio.ensure_future(_fetch_batch(remaining, 20, "Loading remaining screenshots"))
    await game.run()

try:
    loop = asyncio.get_event_loop()
    if loop.is_running():
        loop.create_task(_main())
    else:
        loop.run_until_complete(_main())
except RuntimeError:
    asyncio.ensure_future(_main())
