#!/usr/bin/python3
# encoding=utf-8
#
# Copyright © 2015-2016 Simon McVittie <smcv@debian.org>
# Copyright © 2015-2016 Alexandre Detiste <alexandre@detiste.be>
# SPDX-License-Identifier: GPL-2.0-or-later

from __future__ import annotations

import configparser
import logging
import os
import subprocess
from collections.abc import (Iterable, Iterator)
from typing import (Any, TYPE_CHECKING)

from ..build import (PackagingTask)
from ..data import (Package)
from ..game import (GameData)
from ..paths import DATADIR
from ..util import (mkdir_p)

logger = logging.getLogger(__name__)

if TYPE_CHECKING:
    from ..packaging import (PerPackageState)


def install_data(from_: str, to: str) -> None:
    subprocess.check_call(['cp', '--reflink=auto', from_, to])


class ScummvmPackage(Package):
    gameid: str


class ScummvmGameData(GameData):
    def __init__(self, shortname: str, data: dict[str, Any]) -> None:
        super(ScummvmGameData, self).__init__(shortname, data)

        self.wikibase = 'http://wiki.scummvm.org/index.php/'
        assert self.wiki

        if 'gameid' in self.data:
            self.gameid: str = self.data['gameid']
            assert self.gameid != shortname, \
                'extraneous gameid for ' + shortname
            self.aliases.add(self.gameid)
        else:
            self.gameid = shortname

        if self.engine is None:
            self.engine = 'scummvm'
        if self.genre is None:
            self.genre = 'Adventure'

    def construct_package(self, binary, data) -> Package:
        return ScummvmPackage(binary, data)

    def _populate_package(
        self,
        package,
        d: dict[str, Any]
    ) -> None:
        super(ScummvmGameData, self)._populate_package(package, d)
        package.gameid = d.get('gameid')

    def construct_task(self, **kwargs) -> ScummvmTask:
        return ScummvmTask(self, **kwargs)


class ScummvmTask(PackagingTask):
    def iter_extra_paths(
        self,
        packages: Iterable[Package]
    ) -> Iterator[str]:
        super(ScummvmTask, self).iter_extra_paths(packages)

        gameids = set()
        for p in packages:
            assert isinstance(self.game, ScummvmGameData), self.game
            assert isinstance(p, ScummvmPackage), p
            gameid = p.gameid or self.game.gameid
            if gameid == 'agi-fanmade':
                continue
            gameids.add(gameid.split('-')[0])
        if not gameids:
            return

        # http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM
        # https://github.com/scummvm/scummvm/pull/656
        config_home = os.environ.get(
            'XDG_CONFIG_HOME',
            os.path.expanduser('~/.config'),
        )
        for rcfile in (
            os.path.join(config_home, 'scummvm/scummvm.ini'),
            os.path.expanduser('~/.scummvmrc'),
            os.path.join(config_home, 'residualvm/residualvm.ini'),
            os.path.expanduser('~/.residualvmrc'),
        ):
            if os.path.isfile(rcfile):
                config = configparser.ConfigParser(strict=False)
                config.read(rcfile, encoding='utf-8')
                for section in config.sections():
                    for gameid in gameids:
                        if section.split('-')[0] == gameid:
                            if 'path' not in config[section]:
                                # invalid .scummvmrc
                                continue
                            path = config[section]['path']
                            if os.path.isdir(path):
                                yield path

    def fill_extra_files(
        self,
        per_package_state: PerPackageState
    ) -> None:
        super().fill_extra_files(per_package_state)
        package = per_package_state.package
        destdir = per_package_state.destdir
        assert isinstance(self.game, ScummvmGameData), self.game
        assert isinstance(package, ScummvmPackage), package

        if package.type not in ('demo', 'full') and not package.gameid:
            return

        icon = package.name
        for from_ in (self.locate_steam_icon(package),
                      os.path.join(DATADIR, package.name + '.png'),
                      os.path.join(DATADIR, self.game.shortname + '.png'),
                      os.path.join('/usr/share/pixmaps', icon + '.png'),
                      os.path.join(
                          DATADIR,
                          self.game.shortname.strip('1234567890') + '.png'),
                      ):
            if from_ and os.path.exists(from_):
                pixdir = os.path.join(destdir, 'usr/share/pixmaps')
                mkdir_p(pixdir)
                install_data(from_, os.path.join(pixdir, '%s.png' % icon))
                break
        else:
            icon = 'scummvm'

        assert from_ is not None
        from_ = os.path.splitext(from_)[0] + '.svgz'
        if os.path.exists(from_):
            svgdir = os.path.join(
                destdir, 'usr/share/icons/hicolor/scalable/apps',
            )
            mkdir_p(svgdir)
            install_data(from_, os.path.join(svgdir, '%s.svgz' % icon))

        appdir = os.path.join(destdir, 'usr/share/applications')
        mkdir_p(appdir)

        desktop = configparser.RawConfigParser()
        desktop.optionxform = lambda option: option     # type: ignore
        desktop['Desktop Entry'] = {}
        entry = desktop['Desktop Entry']
        entry['Name'] = package.longname or self.game.longname
        entry['GenericName'] = self.game.genre + ' Game'
        entry['TryExec'] = 'scummvm'
        entry['Icon'] = icon
        entry['Terminal'] = 'false'
        entry['Type'] = 'Application'
        entry['Categories'] = (
            'Game;%sGame;' % self.game.genre.replace(' ', '')
        )
        gameid = package.gameid or self.game.gameid
        install_to = self.packaging.substitute(package.install_to,
                                               package.name)
        if len(package.langs) == 1:
            entry['Exec'] = 'scummvm -p %s %s' % (
                    os.path.join('/', install_to), gameid)
            per_package_state.lintian_overrides.add(
                'desktop-command-not-in-package {} '
                '[usr/share/applications/{}.desktop]'.format(
                    'scummvm', package.name,
                )
            )
        else:
            pgm = package.name[0:len(package.name)-len('-data')]
            entry['Exec'] = pgm
            bindir = self.packaging.substitute(
                self.packaging.BINDIR, package.name,
            )
            bindir = os.path.join(destdir, bindir.strip('/'))
            assert bindir.startswith(destdir + '/'), (bindir, destdir)
            mkdir_p(bindir)
            path = os.path.join(bindir, pgm)
            if 'en' not in package.langs:
                package.langs.append('en')
            with open(path, 'w') as f:
                f.write('#!/bin/sh\n')
                f.write('GAME_LANG=$(\n')
                f.write(
                    "echo $LANGUAGE $LANG en | tr ': ' '\\n' | cut -c1-2 | "
                    "while read lang\n"
                )
                f.write('do\n')
                for lang in package.langs:
                    f.write(
                        '[ "$lang" = "%s" ] && echo $lang && break\n' % lang
                    )
                f.write('done\n')
                f.write(')\n')
                f.write('if [ "$GAME_LANG" = "en" ]; then\n')
                f.write('  exec scummvm -p %s %s\n' % (
                    os.path.join('/', install_to), gameid))
                f.write('else\n')
                f.write('  exec scummvm -q $GAME_LANG -p %s %s\n' % (
                    os.path.join('/', install_to), gameid))
                f.write('fi\n')
            os.chmod(path, 0o755)

        with open(
            os.path.join(appdir, '%s.desktop' % package.name),
            'w', encoding='utf-8',
        ) as output:
            desktop.write(output, space_around_delimiters=False)


GAME_DATA_SUBCLASS = ScummvmGameData
