# -*- coding: utf-8 -*-

# Copyright 2012-2016 Mir Calculate. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

from __future__ import print_function
from __future__ import absolute_import
import sys
import os
from calculate.lib.utils.tools import unpack_single_opts
from . import cert_cmd
import pwd
from calculate.core.server.func import clearDataVars

from calculate.lib.cl_lang import setLocalTranslate

import calculate.contrib
# from spyne.protocol.http import HttpRpc
# from spyne.protocol.xml import XmlDocument
# from spyne.protocol.json import JsonDocument
from spyne.protocol.soap import Soap11
from spyne import Application
# from spyne.server.wsgi import WsgiApplication

from .spyne_adapter import CoreInnerWsdl, make_service

_ = lambda x: x
setLocalTranslate('cl_core3', sys.modules[__name__])

_("User must be root")
_('Failed to import %s')
_('No module named %s')


@clearDataVars
def main(*args, **keywords):
    _args = list(unpack_single_opts(sys.argv[1:]))
    if os.path.basename(sys.argv[0]) != 'cl-core':
        parser = cert_cmd.parse(full=False)
        args, unknown_args = parser.parse_known_args(_args)
        args.method = '_temp_'
    else:
        parser = cert_cmd.parse(full=True)
        args, unknown_args = parser.parse_known_args(_args)
        if args.method:
            parser = cert_cmd.parse(full=False)
            args, unknown_args = parser.parse_known_args(_args)
    if not args.method:
        if unknown_args:
            args = parser.parse_args(_args)

    if args.help and not args.method:
        parser.print_help()
        return 0

    from calculate.core.datavars import DataVarsCore

    ob = DataVarsCore()
    ob.importCore()

    # set var env
    if not ob.flIniFile():
        sys.exit(1)
    # cl_wsdl = ob.Get('cl_wsdl')
    cl_wsdl = ob.Get('cl_wsdl_available')
    data_path = ob.Get('cl_core_data')
    local_data_path = ob.Get('cl_core_local_data')
    certbase = ob.Get('cl_core_database')
    serv_certbase = ob.Get('cl_core_serv_database')
    rights = ob.Get('cl_core_rights')
    group_rights = ob.Get('cl_core_group_rights_path')
    sids = ob.Get('cl_core_sids_path')
    pids = ob.Get('cl_core_pids_path')
    sids_pids = ob.Get('cl_core_sids_pids')
    sids_file = ob.Get('cl_core_sids_file')
    pids_file = ob.Get('cl_core_pids_file')
    max_sid = ob.Get('cl_core_max_sid')
    max_pid = ob.Get('cl_core_max_pid')
    cert_path = ob.Get('cl_core_cert_path')
    cert = ob.Get('cl_core_cert')
    key = ob.Get('cl_core_key')
    cl_ver = ob.Get('cl_ver')
    log_path_var = ob.Get('cl_log_path')
    cl_core_port = ob.GetInteger('cl_core_port')
    port = cl_core_port
    file_logger = None

    # создать симлинки на команды
    if not args.method and args.create_symlink:
        from .func import create_symlink, initialization

        initialization(cl_wsdl)
        create_symlink(local_data_path, data_path)
        return 0

    if args.version:
        print(cl_ver)
        return 0

    log_filename = None

    if ob.Get('cl_ebuild_phase') == '' and os.getuid() == 0:
        import logging
        import logging.handlers

        # logging.raiseExceptions = 0

        log_path = args.log_path if args.log_path else log_path_var

        if not os.path.exists(log_path):
            os.makedirs(log_path)
        log_filename = os.path.join(log_path, 'logging_cl_core.out')
        file_logger = logging.getLogger('MyLogger')
        file_logger.setLevel(logging.DEBUG)

        # Add the log message handler to the logger
        try:
            handler = logging.handlers.RotatingFileHandler(
                log_filename, maxBytes=10000000, backupCount=3)

            file_logger.addHandler(handler)

            # debug
            if args.debug:
                logging.basicConfig(level=logging.DEBUG)
                logger = logging.getLogger('spyne.server.wsgi')
                logger.setLevel(logging.DEBUG)
        except IOError:
            pass

        from urllib2 import URLError

    from traceback import print_exc

    ob.close()
    if not args.method:
        try:
            port = args.port or cl_core_port
            if args.check:
                from . import bootstrap

                bootstrap.check(cert, key)
                return 0

            if args.bootstrap_user_name:
                from . import bootstrap
                bootstrap.init(cert, key, cert_path, data_path, certbase, args,
                               port)
                if not args.cert_user_name:
                    return 0
            if args.clear_user_cert:
                from . import bootstrap
                bootstrap.clear_localuser_certificates(certbase)
                if not args.cert_user_name:
                    return 0
            if args.cert_user_name:
                cert_user_name = args.cert_user_name
                try:
                    pwd.getpwnam(cert_user_name)
                except KeyError:
                    print(_("User %s does not exist") % cert_user_name)
                    return 1

                from . import bootstrap
                bootstrap.force_user_cert(cert, cert_path, data_path,
                                          certbase, cert_user_name, dv=ob)
                return 0
            if args.revoke_cert_id:
                cert_cmd.revoke_signed_cert(args.revoke_cert_id, data_path,
                                            cert_path)
                return 0
            if (args.host or args.gen_root_cert or args.root_host or
                    args.use_root_cert):
                cert_cmd.check_server_certificate(cert, key, cert_path, args,
                                                  port)
                return 0
            if args.id_client_req:
                cert_cmd.sing_req_by_server(args.id_client_req, cert_path,
                                            data_path)
                return 0
            if args.Id:
                cert_cmd.view_cert(args, certbase, data_path, rights,
                                   group_rights)
                return 0
            if args.cert_id:
                cert_cmd.view_signed_cert(args, serv_certbase, data_path)
                return 0
            if args.req_id:
                cert_cmd.view_client_request(args, certbase, data_path)
                return 0
            # Sign request by root certificate
            if args.id_server_req:
                cert_cmd.sing_req_by_root(args, cert_path, data_path)
                return 0
            if args.id_del_req or args.id_del_client_req:
                cert_cmd.del_request(args.id_del_req, args.id_del_client_req,
                                     serv_certbase, certbase, data_path)
                return 0
        except BaseException as e:
            from urllib2 import URLError

            if isinstance(e, URLError) and log_filename:
                if file_logger:
                    fd = open(log_filename, 'a')
                    file_logger.debug(print_exc(file=fd))
                    fd.close()
                print(e)
            else:
                raise
    params_list = ["start", "create_symlink", "method",
                   "list_methods"]
    for param in params_list:
        if hasattr(args, param) and getattr(args, param):
            break
    else:
        parser.print_help()
        return 0
    #####################
    # importing other modules
    from .func import initialization

    outer_wsdl_classes = initialization(cl_wsdl)
    
    pack = "calculate.core.server"
    import importlib

    func_metaclass = importlib.import_module('%s.func_metaclass' % pack)
    core_wsdl_classes = []
    core_wsdl_classes.append(func_metaclass.Func_MetaClass)

    from calculate.core.server.baseClass import Basic
    # make server metaclass
    if args.method or args.list_methods:
        from .local_call import local_method, LocalCall

        ClService = CoreInnerWsdl("ClService",
                                   tuple([LocalCall] + outer_wsdl_classes + [Basic] + core_wsdl_classes), 
                                   {
                                        "__metaclass__" : CoreInnerWsdl
                                   })
        tc = ClService()
        tc.set_comb_class_ref(tc)
        return local_method(tc, args, unknown_args)

    ClService = make_service(Basic, core_wsdl_classes, outer_wsdl_classes, "ClService")

    from .server_class import ClApplication, OpenSSLAdapter
    #do we even need this anymore?
    tc = ClService() 
    App = Application([ClService], 'tns',
        name="ClApplication",
        in_protocol=Soap11(),
        out_protocol=Soap11(),
        )
    # delete all sid and pid informations file
    wsgi_application = ClApplication(App, log=file_logger)
    cert_cmd.create_path(data_path, certbase, rights, group_rights,
                         local_data_path)

    # set all path
    tc.set_paths(data_path, certbase, serv_certbase,
                 rights, group_rights, sids, pids, sids_pids, 
                 sids_file, pids_file, max_sid,
                 max_pid, cert_path, log_filename, 
                 cert, key)

    tc.set_comb_class_ref(tc)
    tc.run_tasks()

    

    max_num = 99999999
    import calculate.contrib
    from cherrypy.wsgiserver import CherryPyWSGIServer, WSGIPathInfoDispatcher

    dispatcher = WSGIPathInfoDispatcher({'': wsgi_application})
    server = CherryPyWSGIServer(('0.0.0.0', port), dispatcher,
                                numthreads=10, max=max_num,
                                request_queue_size=max_num)

    # logger = logging.getLogger("spyne.application")
    # logger.setLevel(0)
    print(_("listening to https://0.0.0.0:%d") % port)
    print(_("wsdl is located at: https://0.0.0.0:%d/?wsdl") % port)

        


    ca_cert = cert_path + "/ca_root.crt"
    if not os.path.exists(ca_cert):
        ca_cert = None

    ssl_adapter = OpenSSLAdapter(cert, key, ca_cert)

    ssl_adapter.certbase = certbase

    server.ssl_adapter = ssl_adapter
    server.certbase = certbase
    server.serv_certbase = serv_certbase
    server.rights = rights
    server.group_rights = group_rights
    server.sids = sids
    server.pids = pids
    server.sids_file = sids_file
    server.pids_file = pids_file
    server.data_path = data_path
    server.cert_path = cert_path

    server.ssl_certificate = cert
    server.ssl_private_key = key
    from OpenSSL.SSL import Error as SSLError
    import socket

    try:
        if args.pidfile:
            try:
                with open(args.pidfile, "w") as f:
                    f.write(str(os.getpid()))
            except OSError:
                sys.stderr.write(_("failed to create PID file %s")
                                 % args.pidfile + "\n")
                sys.exit(1)
        # For cleaning of sessions at server reboot
        from .clean import clean
        from .gen_pid import clear_finished_pids
        from calculate.lib.utils.files import writeFile

        clean(sids_file, pids_file, sids_pids, sids, pids)
        clear_finished_pids(ob)
        print(_("Server started"))
        dbus_stop_file = ob.Get('cl_core_dbus_stop_path')
        if args.inactiveclose:
            try:
                writeFile(dbus_stop_file).close()
            except (OSError, IOError):
                pass
        else:
            if os.path.exists(dbus_stop_file):
                os.unlink(dbus_stop_file)
        server.start()
    except KeyboardInterrupt:
        try:
            ClService.killall()
        except KeyboardInterrupt:
            pass
        print('\n' + _("Server stopped"))
        server.stop()
        sys.exit(0)
    except socket.error as e:
        if e.message == "No socket could be created":
            print(_("No socket could be created"))
            print(_('Port %d already in use') % port)
        else:
            if file_logger:
                fd = open(log_filename, 'a')
                file_logger.debug(print_exc(file=fd))
                fd.close()
            print(e)
    except SSLError:
        print('\n', _('Server certificate not found'))  # , e
        print(_("use cl-core with option --gen-cert-by HOST "
                "(--get-cert-from HOST) or --use-root-as-server)"))
    except Exception:
        if file_logger:
            fd = open(log_filename, 'a')
            file_logger.debug(print_exc(file=fd))
            fd.close()

    server.stop()
    if args.pidfile:
        if os.path.exists(args.pidfile):
            os.unlink(args.pidfile)
    sys.exit(0)
