#!/usr/bin/env python3

# SPDX-License-Identifier: GPL-2.0-or-later
#
# setsettings.py:
# Copyright (C) 2021-present konsumschaf
# Copyright (C) 2021-present konsumlamm
#
# based on setsettings.sh:
# Copyright (C) 2019-present Shanti Gilbert (https://github.com/shantigilbert)
# Copyright (C) 2020-present Fewtarius


# Convert ES settings from distribution.conf to RetroArch configuration in raappend.conf.

from argparse import ArgumentParser
from configparser import ConfigParser
from dataclasses import dataclass
from glob import iglob
import os
import random
import re
import shutil

# If we are on Steam Deck we want a prefix
DEVICE = "DECK"
# TODO: put a real check on the .OS_ARCH file, or maybe not as we are obviously running on Deck
INSTALL_DIR = os.path.expanduser("~/retrodeck")

# if DEVICE == "DECK" :
#	
#else:
#	INSTALL_DIR = ""

# Files and Folder
ra_conf = INSTALL_DIR + "/storage/.config/retroarch/retroarch.cfg"
ra_source_conf = INSTALL_DIR + "/usr/config/retroarch/retroarch.cfg"
ra_core_conf = INSTALL_DIR + "/storage/.config/retroarch/retroarch-core-options.cfg"
ra_append_conf = "/tmp/raappend.cfg"
os_arch_conf = INSTALL_DIR + "/storage/.config/.OS_ARCH"
snapshots = INSTALL_DIR + "/storage/roms/savestates"

# Logging
class Logger:
    dir = "/tmp/logs"
    file = "exec.log"
    script_basename = os.path.basename(__file__)

    def __init__(self):
        if not os.path.isdir(self.dir):
            os.makedirs(self.dir)
        self.file_handle = open(f'{self.dir}/{self.file}',"a+")

    def log(self, text: str) -> None:
        self.file_handle.write(f'{self.script_basename}: {text}\n')

    def __del__(self):
        self.file_handle.close()


# No Sections in distribution.conf, we have to fake our own
class MyConfigParser(ConfigParser):
    def read(self, filename: str) -> None:
        text = open(filename, encoding="utf_8").read()
        self.read_string("[351elec]\n" + text, filename)

@dataclass
class Config:
    rom_name: str
    platform: str

    distribution_conf = INSTALL_DIR + "/storage/.config/distribution/configs/distribution.conf"

    def __post_init__(self):
        # Read the distribution.conf to a dictionary
        config_parser = MyConfigParser(strict=False)
        config_parser.read(self.distribution_conf)
        self.config = config_parser['351elec']

    def get_setting(self, setting: str) -> str:
        """Get the settings from distribution.conf"""

        pat_rom = f'{self.platform}["{self.rom_name}"].{setting}'
        pat_platform = f'{self.platform}.{setting}'
        pat_global = f'global.{setting}'

        if pat_rom in self.config:
            value = self.config[pat_rom]
        elif pat_platform in self.config:
            value = self.config[pat_platform]
        elif pat_global in self.config:
            value = self.config[pat_global]
        else:
            value = ""

        if value == "0":
            value = ""
        return value

    def get_bool_string(self, setting: str) -> str:
        """For readability a function that returns "true" and "false" directly."""

        if self.get_setting(setting):
            return "true"
        else:
            return "false"

# Delete lines from a config file
def delete_lines(file_path: str, lines: tuple) -> None:
    with open(file_path, "r") as input:
        tmp_file = f'/tmp/{os.path.basename(file_path)}.tmp'
        with open(tmp_file, "w") as output:
            for line in input:
                write_this_line = True
                for item in lines:
                    if line.strip().startswith(item):
                        write_this_line = False
                        break
                if write_this_line:
                    output.write(line)
    # replace file with original name
    shutil.move(tmp_file, file_path)

# Append dictionary to the end of a file
def write_file(file_path: str, dictionary: dict, separator: str = ' = ', quote_sign: str = '"') -> None:
    os.makedirs(os.path.dirname(file_path), exist_ok=True)
    with open(file_path, "a") as file:
        for (key, value) in dictionary.items():
            file.write(f'{key}{separator}{quote_sign}{value}{quote_sign}\n')

# Generate the config out of the distribution.conf
def set_settings(rom_name: str, core: str, platform: str, controllers: str, autosave: str, snapshot: str) -> str:
    logger = Logger()
    shader_path = ''

    #
    # Do some checks and clean up first
    #

    # Log current call:
    logger.log(f'ROM: {rom_name}')
    logger.log(f'Platform: {platform}')
    logger.log(f'Core: {core}')
    logger.log(f'Controllers: {controllers}')
    logger.log(f'Autosave: {autosave}')
    logger.log(f'Snapshot: {snapshot}')

    # We only need the ROM, but we get the fullpath
    rom_name = os.path.basename(rom_name)
    # Delete any existing raappend.cfg first
    if os.path.isfile(ra_append_conf):
        os.remove(ra_append_conf)
    # Restore retroarch.cfg if it is missing or empty
    if not os.path.isfile(ra_conf) or os.stat(ra_conf).st_size == 0:
        shutil.copy(ra_source_conf,ra_conf)

    # Get the Device Name
    with open(os_arch_conf, encoding="utf-8") as f:
        device_name = f.readline().strip()
    logger.log(f'Device: {device_name}')

    # Is the CORE 32 or 64bit?
    if core in {'pcsx_rearmed', 'parallel_n64'}:
        bits='32bit'
    else:
        bits='64bit'
    logger.log(f'core is {bits}')

    # Dictionary for the raappend.cfg
    ra_append_dict = {}

    # Create config from distribution.conf
    config = Config(rom_name, platform)

    #
    # Global configuration directly in retroarch.cfg
    #

    # RA menu rgui, ozone, glui or xmb (fallback if everthing else fails)
    # if empty (auto in ES) do nothing to enable configuration in RA
    if menu_driver := config.get_setting('retroarch.menu_driver'):
        ra_dict = {}
        # delete setting only if we set new ones
    	# therefore configuring in RA is still possible
        delete_lines(ra_conf, ('menu_driver', 'menu_linear_filter'))
        if menu_driver == 'rgui':
            ra_dict['menu_driver'] = 'rgui'
            ra_dict['menu_linear_filter'] = 'true'
        elif menu_driver == 'ozone':
            ra_dict['menu_driver'] = 'ozone'
        elif menu_driver == 'glui':
            ra_dict['menu_driver'] = 'glui'
        else:
       		# play it save and set xmb if nothing else matches
            ra_dict['menu_driver'] = 'xmb'
        write_file(ra_conf, ra_dict)

    #
    # Convert the settings from distribution.conf to raappend.cfg
    #

    # FPS
    ra_append_dict['fps_show'] = config.get_bool_string("showFPS")

    # Retroachievements / Cheevos
    retro_achievements = {
        'arcade', 'atari2600', 'atari7800', 'atarilynx', 'colecovision',
        'famicom', 'fbn', 'fds', 'gamegear', 'gb', 'gba', 'gbah', 'gbc', 'gbch',
        'gbh', 'genesis', 'genh', 'ggh', 'intellivision', 'mastersystem',
        'megacd', 'megadrive', 'megadrive-japan', 'msx', 'msx2', 'n64',
        'neogeo', 'neogeocd', 'nes', 'nesh', 'ngp', 'ngpc', 'odyssey2',
        'pcengine', 'pcenginecd', 'pcfx', 'pokemini', 'psp', 'psx', 'sega32x',
        'segacd', 'sfc', 'sg-1000', 'snes', 'snesh', 'snesmsu1', 'supergrafx',
        'supervision', 'tg16', 'tg16cd', 'vectrex', 'virtualboy', 'wonderswan',
        'wonderswancolor',
    }
    if platform in retro_achievements:
        if config.get_setting("retroachievements"):
            ra_append_dict['cheevos_enable'] = "true"
            ra_append_dict['cheevos_username'] = config.get_setting("retroachievements.username")
            ra_append_dict['cheevos_password'] = config.get_setting("retroachievements.password")
            ra_append_dict['cheevos_hardcore_mode_enable'] = config.get_bool_string("retroachievements.hardcore")
            value = config.get_setting("retroachievements.leaderboards")
            if value == "enabled":
                ra_append_dict['cheevos_leaderboards_enable'] = "true"
            elif value == "trackers only":
                ra_append_dict['cheevos_leaderboards_enable'] = "trackers"
            elif value == "notifications only":
                ra_append_dict['cheevos_leaderboards_enable'] = "notifications"
            else:
                ra_append_dict['cheevos_leaderboards_enable'] = "false"
            ra_append_dict['cheevos_verbose_enable'] = config.get_bool_string("retroachievements.verbose")
            ra_append_dict['cheevos_auto_screenshot'] = config.get_bool_string("retroachievements.screenshot")
            ra_append_dict['cheevos_richpresence_enable'] = config.get_bool_string("retroachievements.richpresence")
            ra_append_dict['cheevos_challenge_indicators'] = config.get_bool_string("retroachievements.challengeindicators")
            ra_append_dict['cheevos_test_unofficial'] = config.get_bool_string("retroachievements.testunofficial")
            ra_append_dict['cheevos_badges_enable'] = config.get_bool_string("retroachievements.badges")
            ra_append_dict['cheevos_start_active'] = config.get_bool_string("retroachievements.active")
            ra_append_dict['cheevos_unlock_sound_enable'] = config.get_bool_string("retroachievements.soundenable")
        else:
            ra_append_dict['cheevos_enable'] = "false"
            ra_append_dict['cheevos_username'] = ""
            ra_append_dict['cheevos_password'] = ""
            ra_append_dict['cheevos_hardcore_mode_enable'] = "false"
            ra_append_dict['cheevos_leaderboards_enable'] = "false"
            ra_append_dict['cheevos_verbose_enable'] = "false"
            ra_append_dict['cheevos_test_unofficial'] = "false"
            ra_append_dict['cheevos_unlock_sound_enable'] = "false"
            ra_append_dict['cheevos_auto_screenshot'] = "false"
            ra_append_dict['cheevos_badges_enable'] = "false"
            ra_append_dict['cheevos_start_active'] = "false"
            ra_append_dict['cheevos_richpresence_enable'] = "false"
            ra_append_dict['cheevos_challenge_indicators'] = "false"

    # Netplay
    if config.get_setting("netplay"):
        ra_append_dict['netplay'] = "true"
        # Not needed any more?
        ## Disable Cheevos Hardcore Mode to allow savestates
        #if 'cheevos_hardcore_mode_enable' in ra_append_dict:
        #    ra_append_dict['cheevos_hardcore_mode_enable'] = "false"
        # Host or Client
        value = config.get_setting('netplay.mode')
        if value == 'host':
            ra_append_dict['netplay_mode'] = "false"
            ra_append_dict['netplay_client_swap_input'] = "false"
            ra_append_dict['netplay_ip_port'] = config.get_setting('netplay.port')
        elif value == 'client':
            ra_append_dict['netplay_mode'] = "true"
            ra_append_dict['netplay_ip_address'] = config.get_setting('netplay.client.ip')
            ra_append_dict['netplay_ip_port'] = config.get_setting('netplay.client.port')
            ra_append_dict['netplay_client_swap_input'] = "true"
        # Relay
        if value := config.get_setting('netplay.relay'):
            ra_append_dict['netplay_use_mitm_server'] = "true"
            ra_append_dict['netplay_mitm_server'] = value
        else:
            ra_append_dict['netplay_use_mitm_server'] = "false"
        ra_append_dict['netplay_delay_frames'] = config.get_setting('netplay.frames')
        ra_append_dict['netplay_nickname'] = config.get_setting('netplay.nickname')
        # spectator mode
        ra_append_dict['netplay_spectator_mode_enable'] = config.get_bool_string("netplay.spectator")
        ra_append_dict['netplay_public_announce'] = config.get_bool_string("netplay_public_announce")
    else:
        ra_append_dict['netplay'] = "false"

    # AI Translation Service
    if config.get_setting('ai_service_enabled'):
        ra_append_dict['ai_service_enable'] = "true"
        LangCodes = {
            "false": "0", "En": "1", "Fr": "3", "Pt": "49", "De": "5",
            "El": "30", "Es": "2", "Cs": "8", "Da": "9", "Hr": "11", "Hu": "35",
            "It": "4", "Ja": "6", "Ko": "12", "Nl": "7", "Nn": "46", "Po": "48",
            "Ro": "50", "Ru": "51", "Sv": "10", "Tr": "59", "Zh": "13",
        }
        ai_lang = config.get_setting('ai_target_lang')
        if ai_lang in LangCodes:
            ra_append_dict['ai_service_target_lang'] = f'{LangCodes[ai_lang]}'
        else:
            # use English as default
            ra_append_dict['ai_service_target_lang'] = "1"
        if ai_url := config.get_setting('ai_service_url'):
            ra_append_dict['ai_service_url'] = f'{ai_url}&mode=Fast&output=png&target_lang={ai_lang}'
        else:
            ra_append_dict['ai_service_url'] = f'http://ztranslate.net/service?api_key=BATOCERA&mode=Fast&output=png&target_lang={ai_lang}'
    else:
        ra_append_dict['ai_service_enable'] = "false"

    #
    # Global/System/Game specific settings
    #

    # Ratio
    # default to 22 (core provided) if case anything goes wrong
    ra_append_dict['aspect_ratio_index'] = "22"
    if ratio := config.get_setting('ratio'):
        index_rations = {
            '4/3': '0', '16/9': '1', '16/10': '2', '16/15': '3', '21/9': '4',
            '1/1': '5', '2/1': '6', '3/2': '7', '3/4': '8', '4/1': '9',
            '9/16': '10', '5/4': '11', '6/5': '12', '7/9': '13', '8/3': '14',
            '8/7': '15', '19/12': '16', '19/14': '17', '30/17': '18',
            '32/9': '19', 'config': '20', 'squarepixel': '21', 'core': '22',
            'custom': '23', 'full' : '24',
        }
        if ratio in index_rations:
            ra_append_dict['aspect_ratio_index'] = index_rations[ratio]

    # Bilinear filtering
    ra_append_dict['video_smooth'] = config.get_bool_string("smooth")

    # Video Integer Scale
    ra_append_dict['video_scale_integer'] = config.get_bool_string("integerscale")

    # Video Integer Scale Overscale
    ra_append_dict['video_scale_integer_overscale'] = config.get_bool_string("integerscaleoverscale")

    # RGA Scaling / CTX Scaling
    ra_append_dict['video_ctx_scaling'] = config.get_bool_string("rgascale")

    # Shaderset
    if shaderset := config.get_setting('shaderset'):
        ra_append_dict['video_shader_enable'] = "true"
        ra_append_dict['video_shader'] = shaderset
        # We need to print the shader folder for runemu.sh to use it
        shader_path = f'--set-shader /tmp/shaders/{shaderset}'
    else:
        ra_append_dict['video_shader_enable'] = "false"
        ra_append_dict['video_shader'] = ""

    # Filters
    # Set correct path for video- and audio-filters depending on 32/64bit
    ra_append_dict['audio_filter_dir'] = f'/usr/share/retroarch/filters/{bits}/audio'
    ra_append_dict['video_filter_dir'] = f'/usr/share/retroarch/filters/{bits}/video'
    # Filterset
    if filterset := config.get_setting('filterset'):
        # Filter do not work with RGA/CTX enabled
        ra_append_dict['video_ctx_scaling'] = "false"
        ra_append_dict['video_filter'] = f'/usr/share/retroarch/filters/{bits}/video/{filterset}'
    else:
        ra_append_dict['video_filter'] = ""

    # Rewind
    no_rewind = {'sega32x', 'psx', 'zxspectrum', 'odyssey2', 'mame', 'n64', 'dreamcast', 'atomiswave', 'naomi', 'neogeocd', 'saturn', 'psp', 'pspminis'}
    if config.get_setting('rewind') and platform not in no_rewind:
        ra_append_dict['rewind_enable'] = "true"
    else:
        ra_append_dict['rewind_enable'] = "false"

    # Saves
    # Incrementalsavestates
    if config.get_setting('incrementalsavestates'):
        ra_append_dict['savestate_auto_index'] = "true"
        ra_append_dict['savestate_max_keep'] = "0"
    else:
        ra_append_dict['savestate_auto_index'] = "false"
        ra_append_dict['savestate_max_keep'] = "50"
    # Autosave
    if config.get_setting('autosave'):
        ra_append_dict['savestate_auto_save'] = "true"
        ra_append_dict['savestate_auto_load'] = "true"
    else:
        ra_append_dict['savestate_auto_save'] = "false"
        ra_append_dict['savestate_auto_load'] = "false"
    # Snapshots
    ra_append_dict['savestate_directory'] = f'{snapshots}/{platform}'
    if snapshot:
        if autosave == "1":
            ra_append_dict['savestate_auto_load'] = "true"
            ra_append_dict['savestate_auto_save'] = "true"
        else:
            ra_append_dict['savestate_auto_load'] = "false"
            ra_append_dict['savestate_auto_save'] = "false"
        ra_append_dict['state_slot'] = f'{snapshot}'

    # Runahead
    # Runahead 1st Instance
    no_run_ahead = {'psp', 'sega32x', 'n64', 'dreamcast', 'atomiswave', 'naomi', 'neogeocd', 'saturn'}
    if (runahead := config.get_setting('runahead')) and platform not in no_run_ahead:
        ra_append_dict['run_ahead_enabled'] = "true"
        ra_append_dict['run_ahead_frames'] = runahead
    else:
        ra_append_dict['run_ahead_enabled'] = "false"
        ra_append_dict['run_ahead_frames'] = "1"
    # Runahead 2nd Instance
    if config.get_setting('secondinstance') and platform not in no_run_ahead:
        ra_append_dict['run_ahead_secondary_instance'] = "true"
    else:
        ra_append_dict['run_ahead_secondary_instance'] = "false"

    # Auto Frame Relay
    ra_append_dict['video_frame_delay_auto'] = config.get_bool_string("video_frame_delay_auto")

    # maxperf / CPU Governor
    if config.get_setting('maxperf'):
        ra_append_dict['cpu_scaling_mode'] = '2'
    else:
        ra_append_dict['cpu_scaling_mode'] = '4'

    #
    # Settings for special cores
    #

    ## atari800 core needs other settings when emulation atari5200
    if core == 'atari800':
        logger.log('Atari 800 section')
        retrocore_dict = {}
        atari800_dict = {}
        atari_dict = {}
        atari_conf = INSTALL_DIR + '/storage/.config/distribution/configs/atari800.cfg'
        atari800_conf = INSTALL_DIR + '/storage/.config/retroarch/config/Atari800/Atari800.opt'
        delete_lines(ra_core_conf, ('atari800_system =',))
        delete_lines(atari_conf, ('RAM_SIZE', 'STEREO_POKEY', 'BUILTIN_BASIC'))
        if os.path.isfile(atari800_conf):
            delete_lines(atari800_conf, ('atari800_system',))
        if platform == 'atari5200':
            retrocore_dict['atari800_system'] = '5200'
            atari800_dict['atari800_system'] = '5200'
            atari_dict['RAM_SIZE'] = '16'
            atari_dict['STEREO_POKEY'] = '0'
            atari_dict['BUILTIN_BASIC'] = '0'
        else:
            retrocore_dict['atari800_system'] = '800XL (64K)'
            atari800_dict['atari800_system'] = '800XL (64K)'
            atari_dict['RAM_SIZE'] = '64'
            atari_dict['STEREO_POKEY'] = '1'
            atari_dict['BUILTIN_BASIC'] = '1'
        write_file(ra_core_conf, retrocore_dict)
        # The format of the atari800.cfg is different
        # ('key=value' instead of 'key = "value"')
        write_file(atari_conf, atari_dict, separator='=', quote_sign='')
        write_file(atari800_conf, atari800_dict)

    # Gambatte
    if core == 'gambatte':
        logger.log('Gambatte section')
        gambatte_dict = {}
        gambatte_conf = INSTALL_DIR + "/storage/.config/retroarch/config/Gambatte/Gambatte.opt"

        if os.path.isfile(gambatte_conf):
            delete_lines(gambatte_conf, ('gambatte_gb_colorization', 'gambatte_gb_internal_palette'))
        else:
            gambatte_dict['gambatte_gbc_color_correction'] = 'disabled'

        colorization = config.get_setting('renderer.colorization')
        logger.log(f'gambatte colorization: {colorization}')
        if not colorization or colorization == 'auto':
            gambatte_dict['gambatte_gb_colorization'] = 'disabled'
        elif colorization == 'Best Guess':
            gambatte_dict['gambatte_gb_colorization'] = 'auto'
        elif colorization == 'GBC' or colorization == 'SGB':
            gambatte_dict['gambatte_gb_colorization'] = colorization
        else:
            gambatte_dict['gambatte_gb_colorization'] = 'internal'
            gambatte_dict['gambatte_gb_internal_palette'] = colorization

        write_file(gambatte_conf, gambatte_dict)

    #
    # Controllers
    #
    # We set up the controller index
    if controllers:
        for player in range(1,6):
            logger.log(f'Controller section {player}')
            if pindex := re.search(fr'p{player}index\s+([0-9]+)', controllers):
                ra_append_dict['input_player{player}_joypad_index'] = pindex.group(1)
            # Setting controller type for different cores
            if platform == "atari5200":
                ra_append_dict['input_libretro_device_p{player}'] = '513'

    #
    # Bezels / Decorations
    #
    # List of possible Bezel Folders
    bezel_dir = ('/tmp/overlays/bezels', INSTALL_DIR + '/storage/roms/bezels')
    # Define the resolutions of the different systems (0:x 1:y 2:width 3:height) as seen in Scaling -> Aspect Ration -> Custom
    # Devices (width x hight)
    #   RG351P/M = 480x320
    #   RG351V/MP = 640x480
    #   RG552 = 1920x1152
    #   DECK = 1280x800
    # Consoles (width x hight)
    #   GB/GBC/GG = 160x144
    #   supervision = 160x160
    #   Pokemini = 96x64
    #   ngp/ngpc = 160x152
    #   wonderswan/wonderswancolor = 224×144
    if device_name == "RG351P":
        system_viewport = {
            'standard': (1, 1, 479, 319),          # max-1
            'gb': (80, 16, 320, 288),              # x2
            'gbh': (80, 16, 320, 288),             # x2
            'gbc': (80, 16, 320, 288),             # x2
            'gbch': (80, 16, 320, 288),            # x2
            'supervision': (80, 0, 320, 320),      # x2
            'gamegear': (80, 16, 320, 288),        # x2
            'ggh': (80, 16, 320, 288),             # x2
            'pokemini': (96, 64, 288, 192),        # x3
            'ngp': (80, 8, 320, 304),              # x2
            'ngpc': (80, 8, 320, 304),             # x2
            'wonderswan': (16, 16, 448, 288),      # x2
            'wonderswancolor': (16, 16, 448, 288), # x2
        }
    elif device_name == "RG351V" or device_name == "RG351MP":
        system_viewport = {
            'standard': (1, 1, 639, 479),          # max-1
            'gb': (80, 24, 480, 432),              # x3
            'gbh': (80, 24, 480, 432),             # x3
            'gbc': (80, 24, 480, 432),             # x3
            'gbch': (80, 24, 480, 432),            # x3
            'supervision': (80, 0, 480, 480),      # x3
            'gamegear': (80, 24, 480, 432),        # x3
            'ggh': (80, 24, 480, 432),             # x3
            'pokemini': (128, 112, 384, 256),      # x4
            'ngp': (80, 12, 480, 456),             # x3
            'ngpc': (80, 12, 480, 456),            # x3
            'wonderswan': (96, 96, 448, 288),      # x2
            'wonderswancolor': (96, 96, 448, 288), # x2
        }
    elif device_name == "RG552":
        system_viewport = {
            'standard': (1, 1, 1919, 1151),         # max-1
            'gb': (320, 0, 1280, 1152),             # x8
            'gbh': (320, 0, 1280, 1152),            # x8
            'gbc': (320, 0, 1280, 1152),            # x8
            'gbch': (320, 0, 1280, 1152),           # x8
            'supervision': (400, 16, 1120, 1120),   # x7
            'gamegear': (320, 0, 1280, 1152),       # x8
            'ggh': (320, 0, 1280, 1152),            # x8
            'pokemini': (384, 192, 1152, 768),      # x12
            'ngp': (400, 44, 1120, 1064),           # x7
            'ngpc': (400, 44, 1120, 1064),          # x7
            'wonderswan': (64, 0, 1792, 1152),      # x8
            'wonderswancolor': (64, 0, 1792, 1152), # x8
        }
    elif device_name == "DECK":
        #just copied from 552, I have to edit this
        system_viewport = {
            'standard': (1, 1, 1279, 799),         # max-1
            'gb': (320, 0, 1280, 1152),             # x8
            'gbh': (320, 0, 1280, 1152),            # x8
            'gbc': (320, 0, 1280, 1152),            # x8
            'gbch': (320, 0, 1280, 1152),           # x8
            'supervision': (400, 16, 1120, 1120),   # x7
            'gamegear': (320, 0, 1280, 1152),       # x8
            'ggh': (320, 0, 1280, 1152),            # x8
            'pokemini': (384, 192, 1152, 768),      # x12
            'ngp': (400, 44, 1120, 1064),           # x7
            'ngpc': (400, 44, 1120, 1064),          # x7
            'wonderswan': (64, 0, 1792, 1152),      # x8
            'wonderswancolor': (64, 0, 1792, 1152), # x8
        }

    bezel_cfg = None

    if (bezel := config.get_setting('bezel')) and platform in system_viewport:
        logger.log(f'bezel: {bezel} platform: {platform} rom: {rom_name}')
        tmp_bezel = "/tmp/351elec-bezel.cfg"
        game_cfg = ''
        # set path
        path = ''
        for searchpath in bezel_dir:
            if os.path.isdir(f'{searchpath}/{bezel}'):
                path = f'{searchpath}/{bezel}'

        bezel_system = config.get_setting('bezel.system.override')
        if not bezel_system or bezel_system == "AUTO":
            bezel_system = platform

        bezel_system_png = f'{path}/systems/{bezel_system}.png'
        logger.log(f'Bezel system png: {bezel_system_png}')

        game_bezel_override = config.get_setting('bezel.game.override')
        logger.log(f'Game bezel override: {game_bezel_override}')
        if not game_bezel_override or game_bezel_override == "AUTO":
            logger.log('No game specific override found.  Looking for games')
            # is there a $ROMNAME.cfg?
            # exactly the same / just the name / default
            # Random bezels have to match $ROMNAME./d+.cfg
            romdir = f'{path}/systems/{bezel_system}/games'
            full_name = os.path.splitext(rom_name)[0]
            short_name = full_name.split(' (')[0]
            if os.path.isdir(romdir):
                with os.scandir(romdir) as it:
                    full_list = [entry.name for entry in it if entry.is_file() and entry.name.endswith('.cfg')]
                for romname in (full_name, short_name , 'default'):
                    logger.log(f'Looking at: {romdir}/{romname}')
                    file_list = [file for file in full_list if re.fullmatch(fr'{re.escape(romname)}\.[0-9]+\.cfg', file)]
                    if len(file_list) > 0:
                        game_cfg = f'{romdir}/{random.choice(file_list)}'
                        logger.log(f'Using random config: {game_cfg}')
                        break
                    elif os.path.isfile(f'{romdir}/{romname}.cfg'):
                        game_cfg = f'{romdir}/{romname}.cfg'
                        logger.log(f'Using ROM config: {game_cfg}')
                        break
        else:
            game_cfg = f'{path}/systems/{bezel_system}/games/{game_bezel_override}.cfg'

        if os.path.isfile(game_cfg):
            logger.log(f'game config file exists: {game_cfg}')
            with open(game_cfg) as f:
                contents = f.read().strip()
            bezel_system_png = f'{path}/systems/{bezel_system}/games/{contents}'
        logger.log(f'bezel png: {bezel_system_png}')
        if os.path.isfile(bezel_system_png):
            tmp_bezel_dict = {}
            tmp_bezel_dict['overlays'] = '1'
            tmp_bezel_dict['overlay0_full_screen'] = 'true'
            tmp_bezel_dict['overlay0_normalized'] = 'true'
            tmp_bezel_dict['overlay0_overlay'] = bezel_system_png
            overlays_dir = f'{path}/systems/{bezel_system}/overlays/'
            count = 0
            if os.path.isdir(overlays_dir):
                for overlay_png in iglob(f'{overlays_dir}/*.png'):
                    overlay_name = os.path.splitext(os.path.basename(overlay_png))[0]
                    overlay_setting = config.get_setting(f'bezel.overlay.{overlay_name}')
                    if not overlay_setting:
                        continue
                    logger.log(f'Adding overlay. name: {overlay_name} overlay setting: {overlay_setting}')
                    tmp_bezel_dict[f'overlay0_desc{count}_overlay'] = overlay_png
                    tmp_bezel_dict[f'overlay0_desc{count}'] = 'nul,0.5,0.5,rect,0.5,0.5'
                    count += 1
            tmp_bezel_dict['overlay0_descs'] = count
            if os.path.isfile(tmp_bezel):
                os.remove(tmp_bezel)
            write_file(tmp_bezel, tmp_bezel_dict)
            bezel_cfg = tmp_bezel

    if bezel_cfg is not None:
        logger.log('using bezel')
        # configure bezel
        ra_append_dict['input_overlay_enable'] = 'true'
        ra_append_dict['input_overlay'] = bezel_cfg
        ra_append_dict['input_overlay_hide_in_menu'] = 'true'
        ra_append_dict['input_overlay_opacity'] = '1.000000'
        ra_append_dict['input_overlay_show_inputs'] = '2'
        ra_append_dict['video_scale_integer'] = 'false'
        ra_append_dict['aspect_ratio_index'] = '23'
        # configure custom scaling
        # needs some grouping to reflect the hack systems as well (i. e. gb=gb, gbh, gbc and gbch)
        ra_append_dict['custom_viewport_x'] = system_viewport[platform][0]
        ra_append_dict['custom_viewport_y'] = system_viewport[platform][1]
        ra_append_dict['custom_viewport_width'] = system_viewport[platform][2]
        ra_append_dict['custom_viewport_height'] = system_viewport[platform][3]
    else:
        logger.log('not using bezel')
        # disable decorations
        ra_append_dict['input_overlay_enable'] = 'false'
        # set standard resolution for custom scaling
        ra_append_dict['custom_viewport_x'] = system_viewport['standard'][0]
        ra_append_dict['custom_viewport_y'] = system_viewport['standard'][1]
        ra_append_dict['custom_viewport_width'] = system_viewport['standard'][2]
        ra_append_dict['custom_viewport_height'] = system_viewport['standard'][3]

    # DECK specific options TODO: integrate better in the code
    # TODO: not working
    #ra_append_dict['video_windowed_fullscreen'] = "true"

    # Write the raappend.cfg
    logger.log('Write raappend.cfg')
    write_file(ra_append_conf, ra_append_dict)

    logger.log('done ...')
    return shader_path

# Main (if we are run as a script and not as a module)
if __name__ == '__main__':
    # Arguments
    parser = ArgumentParser(description="Convert ES settings from distribution.conf to RetroArch configuration in raappend.conf.")
    parser.add_argument("--core", help="core", required=True)
    parser.add_argument("--platform", help="platform", required=True)
    parser.add_argument("--rom", help="ROM file name", required=True)
    parser.add_argument("--controllers", help="controller config", default="-p1index 0")
    parser.add_argument("--autosave", help="autosave", default="0")
    parser.add_argument("--snapshot", help="snapshot", default="")
    args = parser.parse_args()
    shader_path = set_settings(rom_name=args.rom, core=args.core, platform=args.platform, controllers=args.controllers, autosave=args.autosave, snapshot=args.snapshot)
    if shader_path:
        print(shader_path)