# -*- 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.

import os
import sys

from soaplib.service import rpc
from soaplib.service import DefinitionBase
from soaplib.serializers.primitive import String, Integer

from soaplib.serializers.clazz import Array
from calculate.core.server.core_interfaces import CoreServiceInterface

_ = lambda x: x
from calculate.lib.cl_lang import getLazyLocalTranslate, setLocalTranslate

setLocalTranslate('cl_core3', sys.modules[__name__])

__ = getLazyLocalTranslate(_)

from clean import sid_monitor, monitor
from tasks import restart, dbus_stop
from loaded_methods import LoadedMethods
import post_cert
import post_request
import send_cert


class Basic(DefinitionBase, CoreServiceInterface):
    """ Basic server class """
    SERV_VERS = 0.11
    glob_process_dict = {}
    process_pid = {}
    glob_progress_dict = {}
    glob_table_dict = {}
    glob_frame_list = {}
    manager = None

    data_path = None
    certbase = None
    rights = None
    sids = None
    pids = None
    sids_file = None
    ssl_certificate = None
    ssl_private_key = None
    cachedict = {}

    # function getting object from cache
    def get_cache(self, sid, meth_name, obj_name):
        if sid in self.cachedict:
            if meth_name in self.cachedict[sid]:
                if obj_name in self.cachedict[sid][meth_name]:
                    return self.cachedict[sid][meth_name][obj_name]
        return None

    # function placing object in cache
    def set_cache(self, sid, meth_name, obj_name, obj, smart=True):
        try:
            if sid not in self.cachedict:
                self.cachedict[sid] = {}
            if meth_name not in self.cachedict[sid]:
                self.cachedict[sid][meth_name] = {}
            if obj_name not in self.cachedict[sid][meth_name]:
                self.cachedict[sid][meth_name][obj_name] = obj
                return True
            if smart:
                for var_name, var_value in obj.__dict__.viewitems():
                    if var_value is not None:
                        setattr(self.cachedict[sid][meth_name][obj_name],
                                var_name, var_value)
            else:
                self.cachedict[sid][meth_name][obj_name] = obj
            return True
        except Exception as e:
            if isinstance(e, SyntaxError):
                raise
            return False

    def clear_cache(self, sid, meth_name=None, obj_name=None):
        if sid not in self.cachedict:
            return True
        if meth_name:
            if meth_name not in self.cachedict[sid]:
                return True

            if obj_name:
                if obj_name not in self.cachedict[sid][meth_name]:
                    return True
                else:
                    obj = self.cachedict[sid][meth_name].pop(obj_name, None)
                    if hasattr(obj, 'close'):
                        obj.close()
            else:
                method_dict = self.cachedict[sid].pop(meth_name, None)
                for val_obj in method_dict.values():
                    if hasattr(val_obj, 'close'):
                        val_obj.close()
        else:
            session_dict = self.cachedict.pop(sid, None)
            for method_dict in session_dict.values():
                for val_obj in method_dict.values():
                    if hasattr(val_obj, 'close'):
                        val_obj.close()

    def clear_cache_method(self, method=None):
        for k, v in self.cachedict.items():
            self.clear_cache(k, method)

    @staticmethod
    def 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="server.crt", key="server.key"):
        """ set system path for main class """
        Basic.data_path = data_path
        Basic.certbase = certbase
        Basic.serv_certbase = serv_certbase
        Basic.rights = rights
        Basic.group_rights = group_rights
        Basic.sids = sids
        Basic.pids = pids
        Basic.sids_pids = sids_pids
        Basic.sids_file = sids_file
        Basic.pids_file = pids_file
        Basic.ssl_certificate = cert
        Basic.ssl_private_key = key
        Basic.cert_path = cert_path
        Basic.max_sid = int(max_sid)
        Basic.max_pid = int(max_pid)
        Basic.log_filename = log_filename

    def run_tasks(self):
        """
        Запуситить регулярные задачи
        """
        import threading
        # start monitor and sid_monitor threads
        monitoring = threading.Thread(target=monitor,
                                      args=(Basic.certbase, Basic.sids_file))
        sid_mon = threading.Thread(target=sid_monitor,
                                   args=(Basic.sids_file, Basic.sids, self))
        restart_watchdog = threading.Thread(target=restart,
                                            args=(self.glob_process_dict,))
        dbus_stop_mon = threading.Thread(target=dbus_stop,
                                     args=(self.glob_process_dict, Basic.sids, self))
        threads = []
        for thread, success, failed in (
                (monitoring, _("General monitoring started"),
                 _("Monitoring error")),
                (sid_mon, _("Session monitoring started"),
                 _("Session monitoring failed")),
                (restart_watchdog, _("Restart watchdog started"),
                 _("Restart watchdog failed")),
                (dbus_stop_mon, _("Inactive watchdog started"),
                 _("Inactive watchdog failed"))):
            try:
                threads.append(thread)
                thread.daemon = True
                thread.start()
                print success
            except Exception:
                print failed
        print

    def killall(self):
        sys.stdout.write('\n' + _('Closing all processes') + '...')
        sys.stdout.flush()
        import time
        # Waiting for closing
        for pid in self.process_pid.keys():
            try:
                os.kill(self.process_pid[pid].pid, 2)
            except OSError:
                pass
        while True:
            num_active_process = 0
            for pid in self.process_pid.keys():
                if self.process_pid[pid].is_alive():
                    num_active_process += 1

            if num_active_process:
                sys.stdout.write('.')
                sys.stdout.flush()
            else:
                print '\n' + _('All processes are closed.')
                return 0
            time.sleep(0.5)

    @rpc(_returns=Array(Integer))
    def post_cert(self):
        # import post_cert
        returns = post_cert.serv_post_cert(self)
        return returns

    @rpc(Integer, _returns=Integer)
    def clear_session_cache(self, sid):
        check_sid = self.check_sid_cert(sid)
        if not check_sid:
            return 1
        # clear cache
        self.clear_cache(sid)
        return 0

    @rpc(Integer, String, _returns=Integer)
    def clear_method_cache(self, sid, method_name):
        check_sid = self.check_sid_cert(sid)
        if not check_sid:
            return 1
        # clear cache
        self.clear_cache(sid, method_name)
        return 0

    @rpc(Integer, Integer, _returns=Integer)
    def clear_pid_cache(self, sid, pid):
        if not self.check_sid_cert(sid):
            return 1

        if pid in self.find_sid_pid_file(sid):
            # clear pid cache
            self._delete_pid(sid, pid)
            return 0

        return 2

    @rpc(Integer, Integer, String, _returns=Array(Integer))
    def post_sid(self, sid, cert_id, lang):
        return self.sid_cmp(sid, cert_id, lang)

    @rpc(Integer, String, _returns=(Array(Integer), Array(Integer)))
    def init_session(self, sid, lang):
        return self.serv_init_session(sid, lang)

    @rpc(Integer, _returns=Array(String))
    # @Dec.check_permissions(['del_sid'])
    def del_sid(self, sid):
        flag = self.del_sid_pid(sid)
        self.clear_cache(int(sid))
        if not flag:
            return self.del_sid_from_file(sid)
        else:
            return ['-1']

    @rpc(Integer, Integer, _returns=Integer)
    def pid_kill(self, pid, sid):
        return self.serv_pid_kill(pid, sid)

    @rpc(Integer, _returns=Array(Integer))
    def list_pid(self, sid):
        return self.find_sid_pid_file(sid)

    @rpc(Integer, String, _returns=Array(Array(String)))
    def get_methods(self, sid, client_type):
        return map(lambda x: map(str, x),
                   self.serv_get_methods(client_type))

    @rpc(Integer, _returns=Array(String))
    @LoadedMethods.core_method(rights=["get-sessions"])
    # @Dec.console('list-session')
    def get_sessions(self, sid):
        if not self.check_sid_cert(sid):
            return ['']
        return self.serv_get_sessions()

    @rpc(Integer, Integer, _returns=Array(String))
    # @Dec.check_permissions(["pid_info"])
    def pid_info(self, sid, pid):
        return self.serv_pid_info(sid, pid)

    @rpc(Integer, _returns=Array(String))
    @LoadedMethods.core_method(rights=["session_info"])
    def sid_info(self, sid):
        return self.serv_sid_info(sid)

    @rpc(Integer, String, _returns=Array(String))
    @LoadedMethods.check_permissions(["view_cert_right"])
    # @Dec.console('view-cert-right')
    # @Dec.gui('System')
    def view_cert_right(self, cert_id, client_type):
        return self.serv_view_cert_right(cert_id, self.data_path, client_type)

    @rpc(Integer, _returns=Integer)
    def active_client(self, sid):
        return self.active_clients(sid)

    @rpc(String, String, String, String, _returns=String)
    def post_client_request(self, request, ip, mac, client_type):
        res = post_request.serv_post_client_request(request, self.data_path,
                                                    ip, mac, client_type,
                                                    self.certbase,
                                                    self.cert_path)
        return res

    @rpc(String, String, _returns=Array(String))
    def get_client_cert(self, req_id, request):
        res = post_request.serv_get_client_cert(req_id, request,
                                                self.data_path, self.certbase,
                                                self.cert_path)
        return res

    @rpc(String, String, String, _returns=String)
    def post_server_request(self, request, ip, mac):
        res = post_request.serv_post_server_request(request, self.data_path,
                                                    ip, mac, self.serv_certbase,
                                                    self.cert_path)
        return res

    @rpc(String, String, _returns=Array(String))
    def get_server_cert(self, req_id, request):
        res = post_request.serv_get_server_request(req_id, request,
                                                   self.data_path,
                                                   self.serv_certbase,
                                                   self.cert_path)
        return res

    @rpc(_returns=String)
    def get_crl(self):
        if os.path.exists(self.data_path + '/server_certs/ca.crl'):
            return open(self.data_path + '/server_certs/ca.crl', 'r').read()
        return ' '

    @rpc(_returns=String)
    def get_server_host_name(self):
        import OpenSSL

        cert = open(self.ssl_certificate, 'r').read()
        cert_obj = OpenSSL.crypto.load_certificate(
            OpenSSL.SSL.FILETYPE_PEM, cert)
        subject = cert_obj.get_subject().get_components()
        for subj in subject:
            if subj[0] == 'CN':
                return subj[1]
        return ''

    @rpc(_returns=String)
    def get_ca(self):
        return send_cert.get_ca(self.cert_path)
