#!/usr/bin/env python3
# encoding=utf-8
# Copyright © 2017 Dylan Baker

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

import enum
import json
import os
import signal
import subprocess
import textwrap
import uuid

from gi.repository import GLib
import appdirs
import attr
import pydbus


_DIRS = appdirs.AppDirs('msmtpqd')


class NMState(enum.Enum):
    """Enum representing NeteworkManagerConnection states."""
    unknown = 0
    alseep = 10
    disconnected = 20
    disconnecting = 30
    connecting = 40
    connected_local = 50
    connected_site = 60
    connected_global = 70


@attr.s(cmp=False, hash=False)
class Manager(object):

    state = attr.ib(default=NMState.unknown,
                    validator=attr.validators.instance_of(NMState))

    def __attrs_post_init__(self):
        self.flush_queue()

    @property
    def queueing(self):
        return self.state is not NMState.connected_global

    def handle_state_change(self, state):
        new_state = NMState(state)
        if new_state is NMState.connected_global:
            print('Network online: Now sending messages')
        else:
            print('Network offline: Now queueing messages')
        if self.queueing and new_state is NMState.connected_global:
            self.flush_queue()
        self.state = new_state

    def flush_queue(self):
        for message in os.listdir(_DIRS.user_data_dir):
            loaded = json.load(message)
            try:
                subprocess.run(' '.split(loaded['command']),
                               input=loaded['message'].encode('utf-8'))
            except subprocess.CalledProcessError:
                print('Failed to send message')
            else:
                os.unlink(message)

    def send_or_queue(self, command, message):
        if self.queueing:
            name = os.path.join(_DIRS.user_data_dir,
                                str(uuid.uuid4()) + '.json')
            with open(name, 'w') as f:
                json.dump({'command': command, 'message': message}, f)
        else:
            # TODO: Handle sending failure
            subprocess.run(command.split(' '), input=message.encode('utf-8'))


@attr.s(cmp=False, hash=False)
class Listener(object):

    dbus = textwrap.dedent("""\
        <node>
          <interface name="com.github.dcbaker.msmtpq.Message">
            <method name="Send">
              <arg type="s" name="command" direction="in"/>
              <arg type="s" name="message" direction="in"/>
            </method>
          </interface>
        </node>""")

    manager = attr.ib()

    def Send(self, command, message):
        self.manager.send_or_queue(command, message)


def main():
    if not os.path.exists(_DIRS.user_data_dir):
        os.makedirs(_DIRS.user_data_dir)

    loop = GLib.MainLoop()

    bus = pydbus.SystemBus()
    nm = bus.get('.NetworkManager')
    manager = Manager(NMState(nm.state()))
    nm.StateChanged.connect(manager.handle_state_change)

    bus = pydbus.SessionBus()
    pub = bus.publish("com.github.dcbaker.msmtpq.Message", Listener(manager))

    def handle_term(*_):
        pub.unpublish()
        loop.quit()

    signal.signal(signal.SIGTERM, handle_term)

    try:
        loop.run()
    except KeyboardInterrupt:
        handle_term()


if __name__ == '__main__':
    main()
