#!/usr/bin/python -u

# Copyright (C) 2013, Daniel Narvaez
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


import argparse
import os
import subprocess
import tempfile
import time
import signal
import sys

from gi.repository import SugarRunner

helpers_dir = "/usr/lib/sugar-runner"

window_process = None
result = 0


def _usr1_signal_handler(number, frame):
    global result
    result = 1


def _setpgrp_preexec():
    os.setpgrp()


def _get_screen_dpi():
    from gi.repository import Gtk

    settings = Gtk.Settings.get_default()
    return settings.get_property('gtk-xft-dpi') / 1024


def _write_xkb_config():
    f, name = tempfile.mkstemp(prefix="sugar-xkbconfig-")
    subprocess.check_call(["setxkbmap", "-print"], stdout=f)
    os.environ["SUGAR_RUNNER_XKBCONFIG"] = name


def _allow_to_run_x():
    try:
        with open("/etc/X11/Xwrapper.config") as f:
            if "allowed_users=anybody" in f.read():
                return
    except IOError:
        return

    print("We need to allow everybody to run the X server")
    tweak_wrapper = os.path.join(helpers_dir, "tweak-xwrapper")
    subprocess.check_call(["sudo", "-k", tweak_wrapper])


def _run_xephyr_window(resolution):
    args = [os.path.join(helpers_dir, "xephyr-window")]

    if resolution is not None:
        args.extend(["--resolution", resolution])

    process = subprocess.Popen(args, stdout=subprocess.PIPE)
    return process, process.stdout.readline().strip()


def _get_tty_number():
    tty = subprocess.check_output(["tty"])
    head, tail = os.path.split(tty.decode('ascii'))
    return tail.strip().replace("tty", "")


# Setup an unencrypted keyring to avoid password dialogs
def _setup_keyring():
    keyrings_dir = os.path.join(os.environ["XDG_DATA_HOME"], "keyrings")
    if not os.path.exists(keyrings_dir):
        try:
            os.makedirs(keyrings_dir)
        except OSError:
            pass

        with open(os.path.join(keyrings_dir, "default"), "w") as f:
            f.write("default")

        with open(os.path.join(keyrings_dir, "default.keyring"), "w") as f:
            f.write("[keyring]\n"
                    "display-name=default\n"
                    "lock-on-idle=false\n"
                    "lock-timeout=0")


def _ensure_dir(path):
    try:
        os.makedirs(path)
    except OSError:
        pass


def _setup_variables(args):
    to_unset = ["GPG_AGENT_INFO",
                "SSH_AUTH_SOCK",
                "GNOME_KEYRING_CONTROL",
                "GNOME_KEYRING_PID",
                "SESSION_MANAGER"]

    for name in to_unset:
        if name in os.environ:
            del os.environ[name]

    if args.output is not None:
        os.environ["SUGAR_RUNNER_OUTPUT"] = args.output

    base_home_dir = os.path.expanduser("~/.sugar-runner")

    if "XDG_CACHE_HOME" not in os.environ:
        cache_home = os.path.join(base_home_dir, "cache")
        os.environ["XDG_CACHE_HOME"] = cache_home
        _ensure_dir(cache_home)

    if "XDG_CONFIG_HOME" not in os.environ:
        config_home = os.path.join(base_home_dir, "config")
        os.environ["XDG_CONFIG_HOME"] = config_home
        _ensure_dir(config_home)

    if "XDG_DATA_HOME" not in os.environ:
        data_home = os.path.join(base_home_dir, "data")
        os.environ["XDG_DATA_HOME"] = data_home
        _ensure_dir(data_home)

    os.environ["SUGAR_SHOW_RESTART"] = "no"
    os.environ["SUGAR_SHOW_SHUTDOWN"] = "no"
    os.environ["SUGAR_SHOW_LOGOUT"] = "yes"

    # Disable Unity custom gtk scrollbars
    os.environ["LIBOVERLAY_SCROLLBAR"] = "0"

parser = argparse.ArgumentParser(description="Run sugar")
parser.add_argument("--resolution", help="screen resolution")
parser.add_argument("--output", help="name of the output")
args = parser.parse_args()

_setup_variables(args)
_setup_keyring()

xinit_args = ["xinit", os.path.join(helpers_dir, "xinitrc"), "--"]
display = SugarRunner.find_free_display()

if "SUGAR_RUNNER_VIRTUAL" in os.environ:
    xinit_args.append("/usr/bin/Xvfb")
    xinit_args.append(display)
    xinit_args.extend(["-ac", "-noreset", "-shmem",
                       "-screen", "0", "1024x768x16"])
elif "DISPLAY" in os.environ:
    _write_xkb_config()

    window_process, xid = _run_xephyr_window(args.resolution)

    xinit_args.append("/usr/bin/Xephyr")
    xinit_args.append(display)
    xinit_args.extend(["-dpi", str(_get_screen_dpi())])
    xinit_args.extend(["-parent", xid])
else:
    _allow_to_run_x()
    xinit_args.append(display)
    xinit_args.append("vt%s" % _get_tty_number())

os.environ["SUGAR_RUNNER_PID"] = str(os.getpid())

signal.signal(signal.SIGUSR1, _usr1_signal_handler)

try:
    xinit_process = subprocess.Popen(xinit_args, preexec_fn=_setpgrp_preexec)

    result = None
    while result is None:
        result = xinit_process.poll()
        time.sleep(0.1)
except KeyboardInterrupt:
    pass

if window_process:
    window_process.terminate()

sys.exit(result)
