#!/usr/bin/python
# Copyright 2009 Canonical Ltd.
#
# This file is part of desktopcouch.
#
#  desktopcouch is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# desktopcouch 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with desktopcouch.  If not, see <http://www.gnu.org/licenses/>.
#
# Authors: Stuart Langridge <stuart.langridge@canonical.com>
#          Tim Cole <tim.cole@canonical.com>

"""CouchDB port advertiser.

A command-line utility which exports a
desktopCouch.getPort method on the bus which returns
that port, so other apps (specifically, the contacts API) can work out
where CouchDB is running so it can be talked to.

Calculates the port number by looking in the CouchDB log.

If CouchDB is not running, then run the script to start it and then
start advertising the port.

This file should be started by D-Bus activation.

"""

import logging
import logging.handlers
from errno import ENOENT

from twisted.internet import glib2reactor
glib2reactor.install()
from twisted.internet import reactor as mainloop
import dbus.service, gobject, os, errno, time
from dbus import DBusException

import desktopcouch
from desktopcouch import local_files
from desktopcouch import replication
from desktopcouch import stop_local_couchdb


class PortAdvertiser(dbus.service.Object):
    "Advertise the discovered port number on the D-Bus Session bus"
    def __init__(self, death):
        self.conn = dbus.SessionBus()
        self.death = death
        super(PortAdvertiser, self).__init__(object_path="/", conn=self.conn)

        # Here we commit to being ready to answer function calls.
        self.bus_name = dbus.service.BusName("org.desktopcouch.CouchDB",
                bus=self.conn)

    @dbus.service.method(dbus_interface='org.desktopcouch.CouchDB',
                         in_signature='', out_signature='i')
    def getPort(self):
        "Exported method to return the port"
        port = int(desktopcouch._direct_access_find_port())
        return port

    @dbus.service.method(dbus_interface='org.desktopcouch.CouchDB',
                         in_signature='', out_signature='')
    def quit(self):
        "Exported method to quit the program"
        self.death()


def main(ctx=local_files.DEFAULT_CONTEXT):
    pid = desktopcouch.find_pid(start_if_not_running=False, ctx=ctx)

    should_shut_down = False
    if pid is None:
        logging.warn("Starting up personal couchdb.")
        pid = desktopcouch.find_pid(start_if_not_running=True, ctx=ctx)
        should_shut_down = True
    else:
        logging.warn("Personal couchdb is already running at PID#%d.", pid)

    try:
        port = desktopcouch._direct_access_find_port(pid=pid, ctx=ctx)

        # Advertise the port
        replication_runtime = replication.set_up(lambda: port)
        portAdvertiser = PortAdvertiser(mainloop.stop)  # TODO: send port.
        try:
            logging.debug("starting main loop")
            mainloop.run()
            logging.debug("ending main loop")

        finally:
            if replication_runtime:
                replication.tear_down(*replication_runtime)

    except:
        logging.exception("uncaught exception makes us shut down.")
    finally:
        logging.info("exiting.")
        if should_shut_down:
            logging.warn("shutting down personal couchdb.")
            stop_local_couchdb.stop_couchdb(ctx=ctx)


if __name__ == "__main__":
    import xdg.BaseDirectory
    import gobject
    gobject.set_application_name("desktopcouch service")

    log_directory = os.path.join(xdg.BaseDirectory.xdg_cache_home,
            "desktop-couch/log")
    try:
        os.makedirs(log_directory)
    except:
        pass
    rotating_log = logging.handlers.TimedRotatingFileHandler(
            os.path.join(log_directory, "desktop-couch-replication.log"),
            "midnight", 1, 14)
    rotating_log.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
    rotating_log.setFormatter(formatter)
    logging.getLogger('').addHandler(rotating_log)
    console_log = logging.StreamHandler()
    console_log.setLevel(logging.WARNING)
    console_log.setFormatter(logging.Formatter("%(asctime)s - %(message)s"))
    logging.getLogger('').addHandler(console_log)
    logging.getLogger('').setLevel(logging.DEBUG)

    main()
