#!/usr/bin/python

# pylint: disable=import-outside-toplevel

import os
import sys
import shlex
from typing import NoReturn
from shutil import which


DEFAULT_XPRA_ARGS = [
    "--attach=yes",
    "--exit-with-children=yes",
    # "--exit-with-client=yes",
    "--encodings=rgb",
    "--compress=0",
    "--audio=no",
    "--video=no",
    "--mdns=no",
    "--start-new-commands=no",
    "--systemd-run=no",
]


def usage(msg: str = "", exit_code=1) -> NoReturn:
    if msg:
        sys.stderr.write("%s\n" % msg)
    sys.stderr.write("usage:   run_scaled --scale=VALUE application [optionalarguments]\n")
    sys.stderr.write("   ie:   run_scaled --scale=2     xterm +ls -fg blue\n")
    sys.stderr.write("or also using `--` for clarity\n")
    sys.stderr.write("usage:   run_scaled --scale=VALUE [extra xpra arguments] -- application [optionalarguments]")
    sys.stderr.write("   ie:   run_scaled --scale=3     --mousewheel=invert-y  -- xterm -n foo -fg red\n")
    sys.exit(exit_code)


def parse_scale(scale: str) -> float:
    try:
        try:
            if scale.endswith("%"):
                value = float(scale[:-1]) / 100
            else:
                value = float(scale)
        except ValueError:
            # try parsing it as a fraction:
            from fractions import Fraction
            value = float(Fraction(scale))
    except ValueError:
        usage(f"invalid scale value {scale!r}")
    if value < 0.1 or value > 10:
        usage(f"scale value {value} is out of range")
    return value


def dpiv(size: int, size_mm: int) -> int:
    """ convert a screen size ratio into a DPI value """
    return round(size * 25.4 / size_mm)


def get_screen_spec() -> tuple[int, int, int]:
    # figure out the dpi and vfb size to use,
    dpi = width = height = 0
    try:
        from xpra.util.env import OSEnvContext
        context = OSEnvContext
    except ImportError:
        from contextlib import nullcontext
        context = nullcontext
    with context():
        os.environ["GDK_SCALE"] = "1"
        os.environ["GDK_DPI_SCALE"] = "1"
        try:
            import gi
            gi.require_version('Gdk', '3.0')
            from gi.repository import Gdk
            screen = Gdk.Screen.get_default()
        except ImportError:
            screen = None
        if screen:
            import warnings
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                width = screen.get_width()
                height = screen.get_height()
                if 0 < width < 2 ** 16 and 0 < height < 2 ** 16:
                    dpi = (dpiv(width, screen.get_width_mm()) + dpiv(height, screen.get_height_mm())) // 2
    return width, height, dpi


def parse_argv(argv: list[str]) -> tuple[int, list[str], list[str]]:
    # default scaling value is 2:
    scale = 2
    command_argv: list[str] = []
    extra_xpra_args: list[str] = []

    # If "--" is present, the command is after that
    if "--" in argv:
        split_idx = argv.index("--")
    else:
        split_idx = -1

    for i, x in enumerate(argv):
        if x in ("--help", "-h"):
            usage("", 0)
        elif x.startswith("--scale="):
            scale = parse_scale(x[len("--scale="):])
        elif i == 0 or i == split_idx:
            # x == "run_scaled" or x == "--"
            continue
        else:
            if i >= split_idx:
                command_argv.append(x)
            else:
                extra_xpra_args.append(x)
    if not command_argv:
        usage("missing command argument")
    return scale, command_argv, extra_xpra_args


def find_xpra() -> str:
    xpra = which("xpra") or ""
    if not xpra and sys.argv[0].find("run_scaled") >= 0:
        xpra = sys.argv[0].replace("run_scaled", "xpra")
    if not xpra or not os.path.exists(xpra):
        usage("cannot find 'xpra' on $PATH")
    return xpra


xpra_cmd = find_xpra()


def get_argv() -> list[str]:
    scale, command_argv, extra_xpra_args = parse_argv(sys.argv)
    argv = [xpra_cmd, "start",
            "--start-child=%s" % shlex.join(command_argv),
            "--desktop-scaling=%s" % scale] + DEFAULT_XPRA_ARGS + extra_xpra_args
    width, height, dpi = get_screen_spec()
    # if found, prefer Xvfb, as it is faster to startup:
    xvfb = which("Xvfb") or ""
    if xvfb:
        from xpra.scripts.config import get_Xvfb_command
        argv.append("--xvfb=%s" % shlex.join(get_Xvfb_command(width, height, dpi * scale)))
    if width > 0 and height > 0:
        argv.append("--resize-display=%ix%i" % (width // scale, height // scale))
    if 10 < dpi < 1000:
        argv.append("--dpi=%i" % (dpi * scale))
    return argv


os.execv(xpra_cmd, get_argv())
