"""Base class and utilities for JSON based runners"""

import json
import os
import shlex

from lutris import settings
from lutris.exceptions import MissingGameExecutableError
from lutris.runners.runner import Runner
from lutris.util import datapath, system

JSON_RUNNER_DIRS = [
    os.path.join(datapath.get(), "json"),
    os.path.join(settings.RUNNER_DIR, "json"),
]


class JsonRunner(Runner):
    json_path = None

    def __init__(self, config=None):
        super().__init__(config)
        if not self.json_path:
            raise RuntimeError("Create subclasses of JsonRunner with the json_path attribute set")
        with open(self.json_path, encoding="utf-8") as json_file:
            self._json_data = json.load(json_file)

        self.game_options = self._json_data["game_options"]
        self.runner_options = self._json_data.get("runner_options", [])
        self.runner_name = self._json_data.get("name", "")
        self.human_name = self._json_data["human_name"]
        self.description = self._json_data["description"]
        platforms = self._json_data.get("platforms", {})
        self.platform_dict = (
            self._json_data["platforms"]
            if isinstance(platforms, dict)
            else {platform: platform for platform in platforms}
        )
        self.runner_executable = self._json_data["runner_executable"]
        self.system_options_override = self._json_data.get("system_options_override", [])
        self.entry_point_option = self._json_data.get("entry_point_option", "main_file")
        self.download_url = self._json_data.get("download_url")
        self.runnable_alone = self._json_data.get("runnable_alone")
        self.flatpak_id = self._json_data.get("flatpak_id")

    def play(self):
        """Return a launchable command constructed from the options"""
        arguments = self.get_command()
        for option in self.runner_options:
            if option["option"] not in self.runner_config:
                continue
            if option["type"] == "bool":
                if self.runner_config.get(option["option"]):
                    arguments.append(option["argument"])
            elif option["type"] == "choice":
                if self.runner_config.get(option["option"]) != "off":
                    arguments.append(option["argument"])
                    arguments.append(self.runner_config.get(option["option"]))
            elif option["type"] == "string":
                arguments.append(option["argument"])
                arguments.append(self.runner_config.get(option["option"]))
            elif option["type"] == "command_line":
                arg = option.get("argument")
                if arg:
                    arguments.append(arg)
                arguments += shlex.split(self.runner_config.get(option["option"]))
            else:
                raise RuntimeError("Unhandled type %s" % option["type"])

        # Prepend the option flag before for entry_point_option value
        for option in self.game_options:
            if self.entry_point_option != option["option"]:
                continue
            if "argument" in option:
                arguments.append(option["argument"])

        main_file = self.game_config.get(self.entry_point_option)
        if not system.path_exists(main_file):
            raise MissingGameExecutableError(filename=main_file)
        arguments.append(main_file)
        result = {"command": arguments}
        if self._json_data.get("env"):
            result["env"] = self._json_data["env"]
        if self._json_data.get("working_dir") == "runner":
            result["working_dir"] = os.path.dirname(os.path.join(settings.RUNNER_DIR, self.runner_executable_path))
        return result


def load_json_runners():
    json_runners = {}
    for json_dir in JSON_RUNNER_DIRS:
        if not os.path.exists(json_dir):
            continue
        for json_path in os.listdir(json_dir):
            if not json_path.endswith(".json"):
                continue
            json_full_path = os.path.join(json_dir, json_path)
            json_data = {}
            with open(json_full_path, encoding="utf-8") as json_file:
                json_data = json.load(json_file)
            runner_name = json_data.get("name") or json_path[:-5]
            runner_class = type(runner_name, (JsonRunner,), {"json_path": json_full_path})
            json_runners[runner_name] = runner_class
    return json_runners
