# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4

##
## Copyright (C) 2015 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##

import BaseHTTPServer
import SimpleHTTPServer
import atexit
import base64
import dbus
import logging
import os
import tempfile

import avahi
from stoqlib.api import api
from stoqlib.domain.person import LoginUser
from stoqlib.exceptions import LoginError
from stoqlib.lib.configparser import get_config
from stoqlib.lib.fileutils import md5sum_for_filename

from stoqserver import library
from stoqserver.common import (AVAHI_DOMAIN, AVAHI_HOST, AVAHI_STYPE,
                               SERVER_NAME, SERVER_AVAHI_PORT,
                               SERVER_EGGS, APP_CONF_FILE)

_ = lambda s: s
_eggs_path = library.get_resource_filename('stoqserver', 'eggs')
_md5sum_path = None
logger = logging.getLogger(__name__)


# TODO: This is experimental and not used anywhere in production,
# which means that we can tweak it a lot without having to worry
# to break something.
class _RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

    def do_HEAD(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        auth = self.headers.getheader('Authorization')
        if not auth or not auth.startswith('Basic '):
            self.do_AUTHHEAD()
            self.wfile.write('Missing authentication')
            return

        encoded_auth = auth.replace('Basic ', '')
        username, password = base64.b64decode(encoded_auth).split(':')
        with api.new_store() as store:
            try:
                login_ok = LoginUser.authenticate(
                    store, unicode(username), unicode(password), None)
            except LoginError:
                login_ok = False

        if not login_ok:
            self.send_error(403, "User not found")
            return

        return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)

    def do_AUTHHEAD(self):
        self.send_response(401)
        self.send_header('WWW-Authenticate', 'Basic realm=\"Stoq Login\"')
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def translate_path(self, path):
        # FIXME: Improve this when we really use it
        if path == '/login':
            return APP_CONF_FILE
        elif path.startswith('/eggs'):
            # SimpleHTTPRequestHandler calls this to translate the url path
            # into a filesystem path. It will always start with os.getcwd(),
            # which means we just need to replace it with the _static path
            translated = SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(
                # /eggs is just the endpoing name, the real path doesn't have it
                self, path.replace('/eggs', ''))
            return translated.replace(os.getcwd(), _eggs_path)
        else:
            return path


class StoqServer(object):

    def __init__(self):
        config = get_config()
        self._port = int(config.get('General', 'serveravahiport') or
                         SERVER_AVAHI_PORT)

    #
    #  Public API
    #

    def run(self):
        try:
            self._setup_avahi()
        except dbus.exceptions.DBusException as e:
            logger.warning("Failed to setup avahi: %s", str(e))

        # md5sum
        with tempfile.NamedTemporaryFile(delete=False) as f:
            for egg in SERVER_EGGS:
                egg_path = os.path.join(_eggs_path, egg)
                if not os.path.exists(_eggs_path):
                    continue

                f.write('%s:%s\n' % (egg, md5sum_for_filename(egg_path)))

        global _md5sum_path
        _md5sum_path = f.name
        server = BaseHTTPServer.HTTPServer(('localhost', self._port),
                                           _RequestHandler)
        server.serve_forever()

    #
    #  Private
    #

    def _setup_avahi(self):
        bus = dbus.SystemBus()
        dbus_server = dbus.Interface(
            bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER),
            avahi.DBUS_INTERFACE_SERVER)

        self.group = dbus.Interface(
            bus.get_object(avahi.DBUS_NAME, dbus_server.EntryGroupNew()),
            avahi.DBUS_INTERFACE_ENTRY_GROUP)
        self.group.AddService(
            avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0), SERVER_NAME,
            AVAHI_STYPE, AVAHI_DOMAIN, AVAHI_HOST,
            dbus.UInt16(self._port),
            avahi.string_array_to_txt_array(['foo=bar']))

        self.group.Commit()
        atexit.register(lambda: self.group.Reset())
