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

# Copyright 2012-2013 Calculate Ltd. 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, 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.lib.cl_lang import getLazyLocalTranslate, setLocalTranslate
setLocalTranslate('cl_core3',sys.modules[__name__])

__ = getLazyLocalTranslate(_)

from clean import sid_monitor, monitor
from decorators import Dec
import post_cert, post_request, send_cert

class Basic (DefinitionBase) :
    """ 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 not sid in self.cachedict:
                self.cachedict[sid] = {}
            if not meth_name in self.cachedict[sid]:
                self.cachedict[sid][meth_name] = {}
            if not obj_name 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 != 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:
            return False

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

            if obj_name:
                if not obj_name 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 set_paths (cls, 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

        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))
        threads = []
        try:
            threads.append(monitoring)
            monitoring.daemon = True
            monitoring.start()
            print _("General monitoring started")
        except:
            print _("Monitoring error")

        try:
            threads.append(sid_mon)
            sid_mon.daemon = True
            sid_mon.start()
            print _("Session monitoring started") + '\n'
        except:
            print _("Session monitoring failed") + '\n'

    def killall(self):
        sys.stdout.write ('\n'+_('Closing all processes')+'...')
        sys.stdout.flush()
        import time
        # Waiting for closing
        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)


    ## Cache parameters
    #class cache_old:
        #def __init__(self,func):
            #self.func_name = func.func_name
            #self.func_code = func.func_code
            #self.__doc__ = func.__doc__
            #self.__name__ = func.__name__
            #self.func = func
            #self.args = {}
            ## storage data in ordered dict

        #def __call__(self, *args,**kwargs):
            #sid = str(args[1])
            #if not sid in self.args:
                #self.args[sid] = collections.OrderedDict()

            ## comparison of variable names and their values
                #for varname,varvalue in \
                        #zip(self.func.func_code.co_varnames \
                        #[:self.func.func_code.co_argcount],
                        #[None]*(self.func.func_code.co_argcount-\
                        #len(self.func.func_defaults))\
                        #+list(self.func.func_defaults)):
                    #self.args[sid][varname] = varvalue
            #newargs = []

            ## addition list of None values
            #args = list(args)

            #if len(args) < len(self.args[sid]):
                #args += [None] * (len(self.args[sid])-len(args))       
            ## addition values in dict
            #for key,value in kwargs.items():
                #self.args[sid][key] = value

            ## check values in None and substitute of cache
            #for cachearg,newarg in zip(self.args[sid].items(),args):
                #if not newarg is None:
                    #self.args[sid][cachearg[0]] = newarg
                    #newargs.append(newarg)
                #else:
                    #newargs.append(cachearg[1])

            #return self.func(*newargs)

    @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)
        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, self.certbase)

    @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))
    @Dec.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))
    @Dec.core_method(rights=["session_info"])
    def sid_info (self, sid):
        return self.serv_sid_info (sid)

    @rpc(Integer, String, _returns = Array(String))
    @Dec.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(String, String, _returns = String)
#    def cert_add (self, mac, client_type):
#        return send_cert.add_cert(mac, client_type, self.data_path, \
#                                  self.certbase)

    @rpc( _returns = String)
    def get_server_host_name (self):
        import OpenSSL
        cert = open(self.ssl_certificate, 'r').read()
        certobj = OpenSSL.crypto.load_certificate \
                            (OpenSSL.SSL.FILETYPE_PEM, cert)
        Subject = certobj.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)
