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

# Copyright 2016 Canonical Ltd.
#
# 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 argparse
import signal
import sys
import os
from gi.repository import GLib, GObject
from libertine import utils
from libertine.service import manager


class OutputRedirector(object):
    def __init__(self, config):
        self.config = config
        self.cache_path = '%s/.cache/libertined' % os.environ['HOME']
        self.cache_file = '%s/libertined.log' % self.cache_path
        self.output_file = None
        self.copied_stdout = None
        self.copied_stderr = None

    def _fileno(self, file_or_fd):
        return getattr(file_or_fd, 'fileno', lambda: file_or_fd)()

    def _do_redirect(self, stream):
        fd = self._fileno(stream)
        copied_stream = os.fdopen(os.dup(fd), 'wb')
        stream.flush()
        os.dup2(self._fileno(self.output_file), fd)
        return copied_stream, fd

    def _undo_redirect(self, stream, stream_fd, copied_stream):
        stream.flush()
        os.dup2(copied_stream.fileno(), stream_fd)
        copied_stream.close()

    def _rotate_logs(self):
        os.makedirs(self.cache_path, exist_ok=True)
        num_backups = 3
        for i in range(num_backups, 0, -1):
            filename = '%s.%i' % (self.cache_file, i)
            if os.path.exists(filename):
                if i == num_backups:
                    os.remove(filename)
                else:
                    os.rename(filename, '%s.%i' % (self.cache_file, i+1))
        if os.path.exists(self.cache_file):
            os.rename(self.cache_file, '%s.1' % self.cache_file)

    def __enter__(self):
        if self.config.debug:
            os.environ['LIBERTINE_DEBUG'] = '1'
        else:
            if self.config.cache_output:
                os.environ['LIBERTINE_DEBUG'] = '1'
                self._rotate_logs()
                self.output_file = open(self.cache_file, 'w')
            else:
                self.output_file = open(os.devnull, 'w')

            self.copied_stdout, self.stdout_fd = self._do_redirect(sys.stdout)
            self.copied_stderr, self.stderr_fd = self._do_redirect(sys.stderr)

    def __exit__(self, type, value, tb):
        if self.copied_stdout and self.stdout_fd:
            self._undo_redirect(sys.stdout, self.stdout_fd, self.copied_stdout)
        if self.copied_stderr and self.stderr_fd:
            self._undo_redirect(sys.stderr, self.stderr_fd, self.copied_stderr)
        if self.output_file:
            self.output_file.close()


class Config(object):
  def __init__(self):
    self._arg_parser = argparse.ArgumentParser(description=u'Libertine Store service')
    self._arg_parser.add_argument(u'-l', u"--use-local-cache",
                                  action='store_true',
                                  default=False,
                                  help=u"use local cache instead of system cache")
    self._arg_parser.add_argument(u'-d', u"--debug",
                                  action='store_true',
                                  default=False,
                                  help=u"allow all output on stdout")
    self._arg_parser.add_argument(u'-c', u"--cache-output",
                                  action='store_true',
                                  default=False,
                                  help=u"Log to $HOME/.cache/libertined/ instead of stdout")
    args = self._arg_parser.parse_args(namespace=Config)


class Loop(object):
    def __init__(self):
        GLib.unix_signal_add(GLib.PRIORITY_HIGH,
                         signal.SIGTERM,
                         self.sigterm,
                         None)
        self.loop = GLib.MainLoop()

    def sigterm(self, code):
        utils.get_logger().info("terminate ('%s') signal received" % code)
        self.shutdown()

    def shutdown(self):
        utils.get_logger().info("shutting service down")
        self.loop.quit()

    def run(self):
        utils.get_logger().debug("entering main loop")
        self.loop.run()


def main():
    config = Config()

    with OutputRedirector(config):
        utils.get_logger().info("Starting libertine service")
        service = manager.Manager()
        loop = Loop()
        try:
            loop.run()
        except KeyboardInterrupt:
            utils.get_logger().debug("keyboard interrupt received")
            loop.shutdown()


if __name__ == '__main__':
    main()
