#!/usr/bin/env python3

import os
import sys
import yaml
import time
import glob
import getpass
import fileinput
import subprocess


def get_containers():
    container_list = subprocess.run(['sudo', '-u', user, 'podman', 'ps', '-a', '--no-trunc', '--sort=created', '--format',
                                     '{{.Names}}'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip().split('\n')

    try:
        with open(os.path.expanduser('~/.config/blend/config.yaml')) as config_file:
            data = yaml.safe_load(config_file)
        order = data['container_order'].copy()
        order.reverse()
        container_list.reverse()
        for i in container_list:
            if i.strip() not in order:
                order.insert(0, i)
        for i, o in enumerate(order):
            if o not in container_list:
                del order[i]
        return order
    except:
        return container_list


def list_use_container_bin():
    try:
        with open(os.path.expanduser('~/.config/blend/config.yaml')) as config_file:
            data = yaml.safe_load(config_file)
        return data['use_container_bins']
    except:
        return []


def check_if_present(attr, desktop_str):
    for l in desktop_str:
        if l.startswith(attr + '='):
            return True
    return False


def which(bin):
    results = []
    for dir in os.environ.get('PATH').split(':'):
        if os.path.isdir(dir):
            if os.path.basename(bin) in os.listdir(dir):
                results.append(os.path.join(dir, os.path.basename(bin)))
    if results == []:
        return None
    return results


def create_container_binaries():
    _binaries = []
    remove_binaries = []

    for c in _list:
        c = c.strip()
        for i in con_get_output(c, '''find /usr/bin -type f -printf "%P\n" 2>/dev/null;
                                      find -L /usr/bin -xtype l -type f -printf "%P\n" 2>/dev/null;
                                      find /usr/local/bin -type f -printf "%P\n" 2>/dev/null;
                                      find -L /usr/local/bin -xtype l -type f -printf "%P\n" 2>/dev/null;''').split('\n'):
            i = i.strip()
            os.makedirs(os.path.expanduser(
                f'~/.local/bin/blend_{c}'), exist_ok=True)
            i_present = False
            orig_which_out = which(os.path.basename(i))
            which_out = None
            if orig_which_out != None:
                which_out = orig_which_out.copy()
                try:
                    which_out.remove(os.path.expanduser(
                        f'~/.local/bin/blend_bin/{os.path.basename(i)}'))
                except ValueError:
                    pass
                if which_out == []:
                    which_out = None
            if which_out != None and os.path.basename(i) not in _exceptions:
                i_present = True

            if os.path.basename(i) != 'host-spawn' and i != '' and not i_present:
                with open(os.path.expanduser(f'~/.local/bin/blend_{c}/{os.path.basename(i)}.tmp'), 'w') as f:
                    f.write('#!/bin/bash\n')
                    f.write(f'# blend container: {c};{i}\n')
                    if os.path.basename(i) in _exceptions:
                        f.write(f'# EXCEPTION\n')
                    f.write('[ -f /run/.containerenv ] && { if [[ -e "/usr/bin/' + os.path.basename(i) + '" ]] || [[ -e "/usr/local/bin/' + os.path.basename(i) + '" ]]; then if [[ -e "/usr/bin/' + os.path.basename(i) + '"  ]]; then /usr/bin/' + os.path.basename(
                        i) + ' "$@"; elif [[ -e "/usr/local/bin/' + os.path.basename(i) + '" ]]; then /usr/local/bin/' + os.path.basename(i) + ' "$@"; fi; exit $?; else echo "This command can be accessed from the host, or from the container \'' + c + '\'."; exit 127; fi } || :\n')
                    f.write(
                        f'BLEND_ALLOW_ROOT= BLEND_NO_CHECK= blend enter -cn {c} -- {os.path.basename(i)} "$@"\n')
                # XXX: make this bit fully atomic
                os.chmod(os.path.expanduser(
                    f'~/.local/bin/blend_{c}/{os.path.basename(i)}.tmp'), 0o775)
                subprocess.call(['mv', os.path.expanduser(f'~/.local/bin/blend_{c}/{os.path.basename(i)}.tmp'),
                                 os.path.expanduser(f'~/.local/bin/blend_{c}/{os.path.basename(i)}')])
                _binaries.append((c, os.path.basename(os.path.basename(i))))

    os.makedirs(os.path.expanduser(f'~/.local/bin/blend_bin'), exist_ok=True)

    for c, i in _binaries:
        try:
            os.symlink(os.path.expanduser(
                f'~/.local/bin/blend_{c}/{i}'), os.path.expanduser(f'~/.local/bin/blend_bin/{i}'))
        except FileExistsError:
            if subprocess.call(['grep', '-q', f'^# container: {c};{i}$', os.path.expanduser(f'~/.local/bin/blend_bin/{i}')], shell=False):
                os.remove(os.path.expanduser(f'~/.local/bin/blend_bin/{i}'))
                os.symlink(os.path.expanduser(
                    f'~/.local/bin/blend_{c}/{i}'), os.path.expanduser(f'~/.local/bin/blend_bin/{i}'))

    for i in remove_binaries:
        try:
            os.remove(i)
        except:
            pass

    for b in os.listdir(os.path.expanduser(f'~/.local/bin/blend_bin')):
        if [_b for _b in _binaries if _b[1] == b] == []:
            os.remove(os.path.join(os.path.expanduser(
                f'~/.local/bin/blend_bin'), b))

    for b_dir in glob.glob(os.path.expanduser(f'~/.local/bin/blend_*')):
        if os.path.basename(b_dir) != 'blend_bin' and os.path.basename(b_dir)[6:] not in _list:
            subprocess.call(['rm', '-rf', b_dir], shell=False)


def create_container_applications():
    _apps = []

    os.makedirs(os.path.expanduser(
        f'~/.local/share/applications'), exist_ok=True)

    for c in _list:
        c = c.strip()
        for i in con_get_output(c, 'find /usr/share/applications -type f 2>/dev/null; find /usr/local/share/applications -type f 2>/dev/null').split('\n'):
            orig_path = i.strip()
            i = os.path.basename(orig_path)
            i_present = (os.path.isfile(f'/usr/share/applications/{i}') or os.path.isfile(f'/usr/local/share/applications/{i}')
                         or os.path.isfile(os.path.expanduser(f'~/.local/share/applications/{i}')))
            if i != '' and not i_present:
                with open(os.path.expanduser(f'~/.local/share/applications/blend;{i}'), 'w') as f:
                    _ = con_get_output(
                        c, f"sudo sed -i '/^DBusActivatable=/d' {orig_path}")
                    _ = con_get_output(
                        c, f"sudo sed -i '/^TryExec=/d' {orig_path}")
                    contents = con_get_output(c, f'cat {orig_path}')
                    f.write(contents)
                for line in fileinput.input(os.path.expanduser(f'~/.local/share/applications/blend;{i}'), inplace=True):
                    if line.strip().startswith('Exec='):
                        line = f'Exec=env BLEND_NO_CHECK= blend enter -cn {c} -- {line[5:]}\n'
                    elif line.strip().startswith('Icon='):
                        if '/' in line:
                            line = line.strip()
                            _ = con_get_output(
                                c, f"mkdir -p ~/.local/share/blend/icons/file/\"{c}_{i}\"; cp {line[5:]} ~/.local/share/blend/icons/file/\"{c}_{i}\"")
                            line = f'Icon={os.path.expanduser("~/.local/share/blend/icons/file/" + c + "_" + i + "/" + os.path.basename(line[5:]))}\n'
                        else:
                            line = line.strip()
                            icons = con_get_output(c, f'''find /usr/share/icons /usr/share/pixmaps /var/lib/flatpak/exports/share/icons \\
                                                            -type f -iname "*{line[5:]}*" 2> /dev/null | sort''').split('\r\n')
                            _ = con_get_output(
                                c, f"mkdir -p ~/.local/share/blend/icons/\"{c}_{i}\"; cp {icons[0]} ~/.local/share/blend/icons/\"{c}_{i}\"")
                            line = f'Icon={os.path.expanduser("~/.local/share/blend/icons/" + c + "_" + i + "/" + os.path.basename(icons[0]))}\n'
                    sys.stdout.write(line)
                os.chmod(os.path.expanduser(
                    f'~/.local/share/applications/blend;{i}'), 0o775)
                _apps.append((c, i))
                del _

    for a in os.listdir(os.path.expanduser(f'~/.local/share/applications')):
        if a.startswith('blend;'):
            a = a.removeprefix('blend;')
            if [_a for _a in _apps if _a[1] == a] == []:
                os.remove(os.path.expanduser(
                    f'~/.local/share/applications/blend;{a}'))


def create_container_sessions(type='xsessions'):
    session_dir = f'/usr/share/{type}'

    os.makedirs('/usr/share/xsessions', exist_ok=True)

    for session in os.listdir(session_dir):
        if session.startswith(os.path.join(session_dir, 'blend-')):
            os.remove(os.path.join(session_dir, session))

    for c in _list:
        c = c.strip()
        for i in con_get_output(c, f'find {session_dir} -type f 2>/dev/null').split('\n'):
            orig_path = i.strip()
            i = os.path.basename(orig_path)
            if i != '':
                with open(os.path.expanduser(f'{session_dir}/blend-{c};{i}'), 'w') as f:
                    contents = con_get_output(c, f'cat {orig_path}')
                    f.write(contents)
                for line in fileinput.input(os.path.expanduser(f'/{session_dir}/blend-{c};{i}'), inplace=True):
                    if line.strip().startswith('Name'):
                        name = line.split('=')[1]
                        line = f'Name=Container {c}: {name}'
                    elif line.strip().startswith('Exec='):
                        line = f'Exec=blend enter -cn {c} -- {line[5:]}'
                    elif line.strip().startswith('TryExec='):
                        continue

                    sys.stdout.write(line)
                os.chmod(os.path.expanduser(
                    f'{session_dir}/blend-{c};{i}'), 0o775)


def con_get_output(name, cmd):
    try:
        return subprocess.run(['sudo', '-u', user, 'podman', 'exec', '--user', getpass.getuser(), '-it', name, 'bash', '-c', cmd],
                            stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=5).stdout.decode('UTF-8').strip()
    except subprocess.TimeoutExpired:
        return ''


user = getpass.getuser()

try:
    user = sys.argv[2]
except:
    pass

for c in get_containers():
    c = c.strip()
    subprocess.call(['podman', 'start', c])

while True:
    _list = get_containers()
    _exceptions = list_use_container_bin()

    create_container_binaries()
    create_container_applications()
    time.sleep(1)