#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Copyright (C) 2016 Canonical Ltd.
# Author: Christopher Townsend <christopher.townsend@canonical.com>

# 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; version 3 of the License.
#
# 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, see <http://www.gnu.org/licenses/>.

import dbus
import dbus.service
import libertine.LxcContainer
import libertine.utils
import os
import shlex
import signal
import subprocess

from collections import Counter
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib


LIBERTINE_LXC_MANAGER_NAME = "com.canonical.libertine.LxcManager"
LIBERTINE_LXC_MANAGER_PATH = "/LxcManager"


class Service(dbus.service.Object):

    def __init__(self):
        self.is_pulse_setup = False
        self.app_counter = Counter()

        DBusGMainLoop(set_as_default=True)
        try:
            bus_name = dbus.service.BusName(LIBERTINE_LXC_MANAGER_NAME,
                                            bus=dbus.SessionBus(),
                                            do_not_queue=True)
        except dbus.exceptions.NameExistsException:
            print("service is already running")
            raise
        super().__init__(bus_name, LIBERTINE_LXC_MANAGER_PATH)

    @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME,
                         in_signature='s',
                         out_signature='b')
    def app_start(self, container_id):
        started = self._launch_lxc_container(container_id)

        if started:
            self.app_counter[container_id] += 1

        return started

    @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME,
                         in_signature='s')
    def app_stop(self, container_id):
        self.app_counter[container_id] -= 1

        if self.app_counter[container_id] == 0:
            self._stop_lxc_container(container_id)
            del self.app_counter[container_id]

    def _setup_pulse(self):
        pulse_socket_path = os.path.join(libertine.utils.get_libertine_runtime_dir(), 'pulse_socket')

        lsof_cmd = 'lsof -n %s' % pulse_socket_path
        args = shlex.split(lsof_cmd)
        lsof = subprocess.Popen(args, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
        lsof.wait()

        if not os.path.exists(pulse_socket_path) or lsof.returncode == 1:
            pactl_cmd = (
                'pactl load-module module-native-protocol-unix auth-anonymous=1 socket=%s'
                % pulse_socket_path)
            args = shlex.split(pactl_cmd)
            subprocess.Popen(args).wait()

        self.is_pulse_setup = True

    def _launch_lxc_container(self, container_id):
        container = libertine.LxcContainer.LibertineLXC(container_id)

        if not self.is_pulse_setup:
            self._setup_pulse()

        if not container.is_running():
            try:
                container.start_container(True)
            except RuntimeError as e:
                print("%s" % e)
                return False

        return True

    def _stop_lxc_container(self, container_id):
        container = libertine.LxcContainer.LibertineLXC(container_id)
        if container.is_running():
            container.stop_container()


def sigterm(self):
    shutdown()


def shutdown():
    GLib.MainLoop().quit()


def main():
    service = Service()
    GLib.unix_signal_add(GLib.PRIORITY_HIGH,
                         signal.SIGTERM,
                         sigterm,
                         None)

    try:
        GLib.MainLoop().run()
    except KeyboardInterrupt:
        shutdown()


if __name__ == '__main__':
    main()
