#!/usr/bin/env python3

import sys
import os
import logging
import argparse
import enum
import subprocess
import shutil
import pathlib

logging.basicConfig(format='%(message)s', level=logging.DEBUG)
logger = logging.getLogger(__name__)

DEVKIT_TOOL_FOLDER = os.path.expanduser('~/devkit-game')
# NOTE: only relevant to Frame + OS client with extra arguments
# sideloaded client on Frame supports full command line edit instead 
STEAM_EXTRA_ARGS_FILE = os.path.expanduser('~/.config/systemd/user/steam.service.d/extra_args.conf')

class SteamStatus(enum.Enum):
    # Supported values from steamos-get-status
    OS = 2
    OS_DEV = 3
    SIDELOADED = 4

STATUS_STRINGS = [
    ( SteamStatus.OS, 'SteamStatus.OS' ),
    ( SteamStatus.OS_DEV, 'SteamStatus.OS_DEV' ),
    ( SteamStatus.SIDELOADED, 'SteamStatus.SIDELOADED' ),
]

# gamescope-session passes the execution to this script if it exists rather than start steam itself
DEVKIT_STEAM_TRAMPOLINE = os.path.expanduser('~/devkit-game/devkit-steam')

# this script executes the sideloaded Steam client (part of the steamos-devkit-service package)
SIDE_LOADED_STEAM_CLIENT = '/usr/share/steamos-devkit/bin/devkit-standalone.py'

def write_trampoline(text):
    with open(DEVKIT_STEAM_TRAMPOLINE, 'w') as devkit_steam:
        devkit_steam.write(text)
        devkit_steam.flush()
    os.chmod(DEVKIT_STEAM_TRAMPOLINE, 0o770)
    # trying really hard to avoid leaving a zero sized trampoline if the deck is about to hang on the session restart coming next
    subprocess.run(['/usr/bin/sync', DEVKIT_TOOL_FOLDER])

def get_os_info():
    os_info = {}
    try:
        for k, v in [ s.split('=') for s in open('/etc/os-release').read().split('\n') if len(s) > 0 ]:
            os_info[k] = v.strip('"')
    except Exception as e:
        logger.error(e)
        logger.error('Failed to parse OS release file')
    return os_info

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--verbose', required=False, action='store_true')
    parser.add_argument('--client', action='store', required=True, choices=[ v[1] for v in STATUS_STRINGS ])
    parser.add_argument('--args', action='store', required=False, help='steam client command line arguments')
    parser.add_argument('--gameid', required=True, action='store')
    parser.add_argument('--gdbserver', action='store_true', required=False)
    conf = parser.parse_args()

    if conf.verbose:
        logger.setLevel(logging.DEBUG)
    else:
        logger.setLevel(logging.INFO)

    target = [ v for v in STATUS_STRINGS if v[1] == conf.client ][0][0]
    logging.info(f'Set steam client on device to {target}')

    if os.path.exists(DEVKIT_STEAM_TRAMPOLINE):
        os.unlink(DEVKIT_STEAM_TRAMPOLINE)

    os_info = get_os_info()
    assert os_info is not None
    is_deckard = os_info.get('VARIANT_ID', None) == 'vr'

    if target == SteamStatus.OS:
        if is_deckard:
            # conf.args is the extra arguments for the normal Steam 'OS client', update it now
            if conf.args is None or conf.args == '':
                logger.info('Clearning extra arguments for normal Steam client')
                if os.path.exists(STEAM_EXTRA_ARGS_FILE):
                    os.unlink(STEAM_EXTRA_ARGS_FILE)
                    subprocess.run(['systemctl', '--user', 'daemon-reload'], check=True)
            else:
                logger.info(f'Setting extra arguments for normal Steam client: {conf.args}')
                os.makedirs(os.path.dirname(STEAM_EXTRA_ARGS_FILE), exist_ok=True)
                with open(STEAM_EXTRA_ARGS_FILE, 'wt') as extra_args_file:
                    escaped_args = conf.args.replace('"', '\\"')
                    extra_args_file.write(f'[Service]\nEnvironment="STEAM_EXTRA_ARGS={escaped_args}"')
                subprocess.run(['systemctl', '--user', 'daemon-reload'], check=True)

            # When disabling a sideloaded client, also delete the ~/.steam symlinks:
            # They will be re-created by the OS client when starting,
            # this prevents SteamVR trying to use the sideloaded binaries that are still there for the steam API.
            # (this may happen because SteamVR starts before Steam starts and has a chance to set those symlinks correctly)
            for path in pathlib.Path(os.path.expanduser('~/.steam')).glob('*'):
                if path.is_symlink():
                    try:
                        lnk = path.resolve()
                        if 'devkit-game' in str(lnk):
                            path.unlink()
                            print(f'Deleted: {path} -> {lnk}')
                    except Exception as e:
                        print(f'Error processing {path}: {e}')
        logger.info('Devkit Steam client override is disabled - default OS client execution will resume.')
        sys.exit(0)

    os.makedirs(os.path.dirname(DEVKIT_STEAM_TRAMPOLINE), exist_ok=True)

    # OS client
    steam_client = '$HOME/.local/share/Steam/steam.sh'
    if target == SteamStatus.SIDELOADED:
        steam_client = '$HOME/devkit-game/steam/steam.sh'

    if is_deckard:
        # RUNSTEAM.sh checks for SIDELOADED_STEAMROOT="${HOME}/devkit-game/steam"
        # this is consistent with sideload on Steam Deck, but we use a different name 'steamdeckard'
        # will be addressed when reworking the sideload and debug strategy, for now just drop in a symlink
        steam_symlink = os.path.expanduser('~/devkit-game/steam')
        if os.path.lexists(steam_symlink):
            if os.path.islink(steam_symlink):
                os.unlink(steam_symlink)
            else:
                # this happens if an upload in Steam Deck mode was attempted against a Steam Frame for instance
                # was an easy mistake to make before recent changes
                logger.warning('warning: ~/devkit-game/steam exists but is not a symlink. Removing anyway.')
                shutil.rmtree(steam_symlink)
        os.symlink(
            os.path.expanduser('~/devkit-game/steamdeckard'),
            steam_symlink,
        )

    args = '"$@"'
    if conf.args is not None:
        args = conf.args

    gdbserver = ''
    if conf.gdbserver:
        logger.info('Configuring for remote debugging via gdbserver')
        gdbserver = 'export DEBUGGER="gdbserver 0.0.0.0:2345"'

    write_trampoline('''#!/bin/bash
# Generated by steamos-set-steam-client, do not edit!
# configuration tag (do not delete): {}
{}
mkdir -p $HOME/.steam/steam/logs
exec {} {}
'''.format(
    conf.client,
    gdbserver,
    steam_client,
    args
))
