#!/usr/bin/python3

import ctypes.util
import filecmp
import functools
import os
import subprocess
import sys
import tempfile

import xdg.BaseDirectory

PKG_LIB_DIR = "/usr/lib/games/dwarf-fortress"
PKG_DATA_DIR = "/usr/share/games/dwarf-fortress/gamedata"
LOCAL_DATA_DIR = "/usr/local/share/games/dwarf-fortress"

def get_user_run_dir():
    old_run_dir = xdg.BaseDirectory.save_data_path('dwarf-fortress')
    new_run_dir = os.path.join(old_run_dir, 'run')
    if not os.path.exists(new_run_dir):
        to_migrate = os.listdir(old_run_dir)
        os.mkdir(new_run_dir)
        for migratee in to_migrate:
            os.rename(os.path.join(old_run_dir, migratee),
                      os.path.join(new_run_dir, migratee))
    return new_run_dir

def get_user_data_dirs(run_dir):
    for df_dir in xdg.BaseDirectory.load_data_paths('dwarf-fortress'):
        data_dirs = os.listdir(df_dir)
        full_data_dirs = map(functools.partial(os.path.join, df_dir),
                             data_dirs)
        yield from filter(lambda p: not os.path.samefile(run_dir, p),
                          full_data_dirs)

def get_data_dirs(run_dir):
    yield from get_user_data_dirs(run_dir)
    yield PKG_LIB_DIR
    if os.path.isdir(LOCAL_DATA_DIR):
        for dir_entry in os.scandir(LOCAL_DATA_DIR):
            if dir_entry.is_dir():
                yield dir_entry.path
    yield PKG_DATA_DIR

def run_df(tmp_dir, args):
    # libgraphics.so is missing the dependency on libz on i386, so we
    # have to help it a bit.
    df_env = dict(os.environb)
    libz = ctypes.util.find_library('z')
    try:
        df_env['LD_PRELOAD'] += ":{}".format(libz)
    except KeyError:
        df_env['LD_PRELOAD'] = libz

    cmd = [os.path.join(tmp_dir, 'df')] + args
    subprocess.run(cmd, env=df_env).check_returncode()

def run_df_in_unionfs(user_run_dir, data_dirs, args):
    mnt_dirs = user_run_dir + "=rw:" + ':'.join(data_dirs)
    with tempfile.TemporaryDirectory(suffix='run', prefix='dwarf-fortress') as tmp_dir:
        cmd = ['unionfs', '-o', 'cow,relaxed_permissions', mnt_dirs, tmp_dir]
        subprocess.run(cmd).check_returncode()
        try:
            run_df(tmp_dir, args)
        finally:
            subprocess.run(['fusermount', '-u', tmp_dir]).check_returncode()

def run_df_in_unionfs_with_cleanup(user_run_dir, data_dirs, args):
    try:
        run_df_in_unionfs(user_run_dir, data_dirs, args)
    finally:
        cleanup_unionfs(user_run_dir, data_dirs)

def cleanup_unionfs(user_run_dir, data_dirs):
    for (current, _, files) in os.walk(user_run_dir):
        for file in files:
            fullpath = os.path.join(current, file)
            relpath = os.path.relpath(fullpath, user_run_dir)
            upstream_path = find_first_file(relpath, data_dirs)
            if upstream_path and filecmp.cmp(fullpath, upstream_path, False):
                os.unlink(fullpath)

def find_first_file(file, data_dirs):
    for data_dir in data_dirs:
        path = os.path.join(data_dir, file)
        if os.path.exists(path):
            return path

def ensure_font_symlink(user_run_dir):
    font_link_dir = os.path.join(user_run_dir, 'data', 'art')
    font_link = os.path.join(font_link_dir, 'font.ttf')
    if not os.path.lexists(font_link):
        os.makedirs(font_link_dir, exist_ok=True)
        os.symlink('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', font_link)

def main():
    user_run_dir = get_user_run_dir()
    data_dirs = list(get_data_dirs(user_run_dir))
    ensure_font_symlink(user_run_dir)
    run_df_in_unionfs_with_cleanup(user_run_dir, data_dirs, sys.argv)

if __name__ == '__main__':
    main()
