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

# Copyright 2010-2012 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 sys

from soaplib.serializers.primitive import String, Integer
from soaplib.serializers.clazz import Array, ClassSerializer
from soaplib.service import rpc
from calculate.core.server.api_types import (Field, GroupField, ViewInfo,
                                             ViewParams, ReturnedMessage)
from calculate.lib.datavars import VariableError
from calculate.lib.utils.files import readLinesFile
from calculate.lib.cl_lang import getLazyLocalTranslate, setLocalTranslate
setLocalTranslate('cl_core',sys.modules[__name__])

from calculate.core import datavars
import traceback

__ = getLazyLocalTranslate(_)

class GroupInfo(ClassSerializer):
    """Parameters for method install"""
    cl_page_count = String
    cl_page_offset = String

class DetailGroupInfo(ClassSerializer):
    """Parameters for method install"""
    cl_group_name = String
    cl_group_rights = Array(String)

class AddGroupInfo(ClassSerializer):
    """Parameters for method install"""
    cl_group_add = String
    cl_group_rights = Array(String)

def catchExcept():
    class wrapper:
        def __init__(self,f):
            self.f = f
            self.func_name = f.func_name
            self.func_code = f.func_code
            self.__doc__ = f.__doc__
            self.__name__ = f.__name__

        def __call__(self,selfobj,*args,**kwargs):
            try:
                return self.f(selfobj,*args,**kwargs)
            except BaseException as e:
                view = ViewInfo(groups=[])
                group = GroupField(name=_("Error"),last=True)
                group.fields = []
                group.fields.append(Field(
                        name = "error",
                        label = str(e),
                        default = 'color:red;',
                        element = "error"))
                view.groups.append(group)
                print "!!!!EXCEPTION!!!!"
                for i in apply(traceback.format_exception, sys.exc_info()):
                    print i
                return view
    return wrapper

class CoreWsdl:
    #def create_req_table(self, )
    def show_groups_meth (self, dv) :
        try:
            page_count = int(dv.Get('cl_page_count'))
            page_offset = int(dv.Get('cl_page_offset'))
            #self.printSUCCESS('Current offset = %d' %page_offset)

            if not page_offset:
                page_offset = 0

            list_group_name = dv.Choice('cl_group_name')

            if not len(list_group_name):
                self.printSUCCESS('No groups')
                return True

            dict_group_name = {}
            list_group_name.sort()
            for i in range(len(list_group_name)):
                dict_group_name[i] = list_group_name[i]
            max_id = len(list_group_name)

            if not page_count:
                page_count = len(list_group_name)

            head = [_('Groups'),_('Permissions')]
            body = []
            fields = ['cl_group_name','']

            num_id = len(list_group_name)
            if page_offset > num_id - 1:
                i = num_id - 1
            else:
                i = page_offset
            while True:
                if len(body) == page_count or i >= max_id:
                    break

                dv.Set('cl_group_name', dict_group_name[i])
                group_rights = ', '.join(dv.Get('cl_group_rights'))
                body.append([dict_group_name[i],group_rights])
                i+=1

            if body:
                self.printTable(_("List of available groups"), head, body, \
                            fields = fields, onClick = 'core_detail_group', \
                            addAction = 'core_add_group')
                if not page_offset:
                    return True
                count_page = len(list_group_name) / page_count + 1
                num_page = page_offset / page_count + 1
                self.printSUCCESS(_('page %d from ')%num_page+str(count_page))
                return True
        except Exception,e:
            for i in apply(traceback.format_exception, sys.exc_info()):
                print i
            print 'Except: ', e
        return True

    def check_group_params (self, dv, info,allvars=False,ordered=None):
        errors = []
        keys = sorted(filter(lambda x:x.lower() == x,
               info._type_info.keys()))
        if ordered:
            keys = ordered + filter(lambda x:not x in ordered,
                             keys)
        for var in keys:
            # get value of variable from info
            val = getattr(info, var)
            # check value if value send of check allvariables
            if val != None or allvars:
                try:
                    # if value not send, then get from datavars
                    if val == None:
                        val = dv.Get(var)
                    else:
                        uncomperr = dv.Uncompatible(var)
                        if uncomperr:
                            raise VariableError(uncomperr)
                    if not dv.Uncompatible(var):
                        dv.Set(var, val)
                except VariableError, e:
                    mess = ''
                    messages = e.message if type(e.message) == list \
                                         else [e.message]
                    for error in messages:
                        mess += str(error) + '\n'
                        errors.append(ReturnedMessage(type = 'error', 
                                                field = var, message = mess))
        return errors

    from calculate.core.server.decorators import Dec

    def groupCommon(self,sid,info,methodname):
        """
        Install common method
        """
        try:
            dv = self.get_cache(sid,methodname,"vars")
            if not dv:
                #reload(cl_install)
                dv = datavars.DataVarsCore()
                dv.importCore()
                dv.flIniFile()
            errors = self.check_group_params(dv, info,
                       ordered=['cl_page_count',
                                'cl_page_offset'],
                     allvars=True)
            if errors:
                return errors 
            group_meth = type("groupCommon",(self.Common,
                                CoreWsdl, object), {})
            pid = self.startprocess(sid, target=group_meth,
                                    method="show_groups_meth",
                                    method_name='core_show_groups',
                                    auto_delete = True,
                                    args_proc = (dv,))
            returnmess = ReturnedMessage(type = 'pid', message = pid, \
                                         expert = True)
            returnmess.type = "pid"
            returnmess.message = pid
            dv = self.clear_cache(sid,methodname)
            return [returnmess]
        finally:
            if dv:
                self.set_cache(sid,methodname,"vars",dv,smart=False)
        return []

    @rpc(Integer, GroupInfo, _returns = Array(ReturnedMessage))
    @Dec.core_method(gui=True, title=__('Show groups'), category = \
              __('Utilities'), image='user-group-properties,view-certificate'\
              '-import,application-certificate',command='cl-core-show-group',
              rights=["edit_group"])
    def core_show_groups(self, sid, info):
        if not info:
            mess = 'Field must be int!'
            errors = []
            for field in ['cl_page_count', 'cl_page_offset']:
                errors.append(ReturnedMessage(type = 'error', field = field,
                                      message = mess, expert = True))
            return errors
        dv = datavars.DataVarsCore()
        dv.importCore()
        dv.flIniFile()
        dv.Set('cl_page_count', info.cl_page_count)
        dv.Set('cl_page_offset', info.cl_page_offset)
        self.set_cache(sid, 'core_show_groups', "vars", dv, smart=False)
        return self.groupCommon(sid,info,'core_show_groups')

    @rpc(Integer, ViewParams,_returns = ViewInfo)
    @catchExcept()
    def core_show_groups_view (self, sid, params):
        dv = datavars.DataVarsCore()
        dv.importCore()
        dv.flIniFile()
        dv.addGroup(_("Groups"),
            normal=('cl_page_count','cl_page_offset'),
            next_label=_("Next"))
        view = ViewInfo(dv)
        self.set_cache(sid, 'core_show_groups', "vars", dv, smart=False)
        return view

##################### BEGIN DETAIL GROUP METHOD ##############################
    @rpc(Integer, DetailGroupInfo, _returns = Array(ReturnedMessage))
    @Dec.core_method(gui=True, title=__('Group details'),rights=["edit_group"])
    def core_detail_group ( self, sid, info):
        if not info:
            mess = 'Field must be in list!'
            errors = []
            errors.append(ReturnedMessage(type = 'error',
                                          field = 'cl_group_name',
                                          message = mess))
            return errors

        dv = datavars.DataVarsCore()
        dv.importCore()
        dv.flIniFile()
        try:
            dv.Set('cl_group_name', info.cl_group_name)
        except VariableError, e:
            return [ReturnedMessage(type = 'error', field = 'cl_group_name',
                                    message = e.message)]
        self.set_cache(sid, 'core_detail_group', 'dv', dv, smart = False)
        return []

    @rpc(Integer, ViewParams, _returns = ViewInfo)
    @catchExcept()
    def core_detail_group_view (self, sid, params):
        dv = self.get_cache(sid, 'core_detail_group', 'dv')
        if not dv:
            dv = datavars.DataVarsCore()
            dv.importCore()
            dv.flIniFile()
            dv.Get('cl_group_name')

        dv.addGroup(None,
            normal=('cl_group_name', 'cl_group_rights'))

        view = ViewInfo(dv)

        group = GroupField(name='',last=True)
        group.fields = []
        group.fields.append(Field(
            name = "but0",
            label = _("Back"),
            value = "core_show_groups",
            element = "button"))
        group.fields.append(Field(
            name = "but1",
            label = _("Change"),
            value = "core_change_group",
            element = "button"))
        group.fields.append(Field(
            name = "but2",
            label = _("Delete"),
            value = "core_del_group",
            element = "button"))
        view.groups.append(group)
        self.set_cache(sid, 'core_show_groups', "vars", dv, smart=False)
        return view

############## END DETAIL GROUP METHOD #######################################
############### BEGIN CONFIRM GROUP METHODS ##################################
    def changeGroupCommon(self,sid,info,methodname):
        try:
            dv = self.get_cache(sid,methodname,"vars")
            errors = self.check_group_params(dv, info,
                       ordered=['cl_group_name','cl_group_rights'],
                       allvars=True)
            if errors:
                return errors
            group_meth = type("changeGroupCommon",(self.Common,
                                CoreWsdl, object), {})
            pid = self.startprocess(sid, target=group_meth,
                                method="change_group_meth",
                                method_name='core_change_group',
                                args_proc = (dv,))
            returnmess = ReturnedMessage(type = 'pid', message = pid)
            returnmess.type = "pid"
            returnmess.message = pid
            dv = self.clear_cache(sid,methodname)
            return [returnmess]
        finally:
            if dv:
                self.set_cache(sid,methodname,"vars",dv,smart=False)
        return []

    def change_group_meth(self, dv):
        try:
            group_rights_file = dv.Get('cl_core_group_rights')

            cl_group_name = str(dv.Get('cl_group_name'))
            cl_group_rights = dv.Get('cl_group_rights')
            #self.startTask('Request confirmation')
            changed_flag = False
            result = []

            for line in readLinesFile(group_rights_file):
                if line.startswith('#') or not line:
                    result.append(line)
                    continue
                words = line.split(' ',1)
                # first word in line equal name input method
                if words[0] == cl_group_name:
                    line = cl_group_name + ' ' + ','.join(cl_group_rights)
                    changed_flag = True
                result.append(line)

            if cl_group_name == 'all' and not changed_flag:
                result.append(cl_group_name + ' ' + ','.join(cl_group_rights))
                changed_flag = True

            fd = open (group_rights_file, 'w')
            for lines in result:
                fd.write(lines + '\n')
            fd.close()

            if changed_flag:
                self.printSUCCESS ('<b>'+_('Group changed')+'</b>')
            else:
                self.printSUCCESS ('<b>'+ _('Group not changed')+'</b>')
            self.printSUCCESS (_('Group name') + " = %s" %cl_group_name)
            self.printSUCCESS (_('Group permissions') + " = %s" 
                               %','.join(cl_group_rights))
            return True
        except (KeyboardInterrupt,):
            self.printERROR('Process is interrupted!')
            return False

    @rpc(Integer, DetailGroupInfo, _returns = Array(ReturnedMessage))
    @Dec.core_method(gui=True, title=__('Change the group'),
                     command='cl-core-change-group',rights=["edit_group"])
    @catchExcept()
    def core_change_group ( self, sid, info):
        dv = self.get_cache(sid,'core_change_group',"vars")
        if not dv:
            dv = datavars.DataVarsCore()
            dv.importCore()
            dv.flIniFile()
        not_group_msg = [ReturnedMessage(type='error', field='cl_group_add',\
                         message = _('use --group-name to select the group'))]
        if not info:
            return not_group_msg
        if not info.cl_group_name:
            return not_group_msg
        try:
            dv.Set('cl_group_name', info.cl_group_name)
        except VariableError, e:
            return [ReturnedMessage(type = 'error', message = e.message, \
                                    field = 'cl_group_name')]
        if info.cl_group_rights == None:
            return [ReturnedMessage(type='error', field='cl_group_rights',\
                    message=_('use --group-rights to change the permissions'))]
        try:
            dv.Set('cl_group_rights', info.cl_group_rights)
        except VariableError, e:
            return [ReturnedMessage(type = 'error', message = e.message, \
                                    field = 'cl_group_rights')]
        self.set_cache(sid, 'core_change_group', "vars", dv, smart=False)
        return self.changeGroupCommon(sid,info,'core_change_group')

    @rpc(Integer, ViewParams, _returns = ViewInfo)
    @catchExcept()
    def core_change_group_view (self, sid, params):
        dv = self.get_cache(sid, 'core_change_group', 'dv')
        if not dv:
            dv = datavars.DataVarsCore()
            dv.importCore()
            dv.flIniFile()
            dv.Get('cl_group_name')

        dv.addGroup(_("Change the group"),
            normal=('cl_group_name', 'cl_group_rights'),
            next_label=_("Done"))

        view = ViewInfo(dv)

        group = GroupField(name=_("Change the group"), nextlabel=_("Done"), \
                           last=True)
        group.fields = []
        group.fields.append(Field(
            name = "but2",
            label = "Confirm",
            value = "core_change_group",
            element = "button"))
        view.groups.append(group)
        self.set_cache(sid, 'core_change_group', "vars", dv, smart=False)
        return view
############### END CONFIRM GROUP METHODS ###################################
############### BEGIN DELETE GROUP METHODS ##################################
    def delGroupCommon(self,sid,info,methodname):
        """
        Install common method
        """
        try:
            dv = self.get_cache(sid,methodname,"vars")
            errors = self.check_group_params(dv, info,
                       ordered=['cl_group_name'],
                       allvars=True)
            if errors:
                return errors
            group_meth = type("delGroupCommon",(self.Common,
                                CoreWsdl, object), {})
            pid = self.startprocess(sid, target=group_meth,
                                method="del_group_meth",
                                method_name='core_del_group',
                                args_proc = (dv,))
            returnmess = ReturnedMessage(type = 'pid', message = pid)
            returnmess.type = "pid"
            returnmess.message = pid
            dv = self.clear_cache(sid,methodname)
            return [returnmess]
        finally:
            if dv:
                self.set_cache(sid,methodname,"vars",dv,smart=False)
        return []

    def del_group_meth(self, dv):
        try:
            group_rights_file = dv.Get('cl_core_group_rights')
            cl_group_name = str(dv.Get('cl_group_name'))

            result = []
            deleted_flag = False
            for line in readLinesFile(group_rights_file):
                if line.startswith('#') or not line:
                    result.append(line)
                    continue
                words = line.split(' ',1)
                # first word in line equal name input method
                if words[0] != cl_group_name:
                    result.append(line)
                else:
                    deleted_flag = True

            fd = open (group_rights_file, 'w')
            for lines in result:
                fd.write(lines + '\n')
            fd.close()

            if deleted_flag:
                self.printSUCCESS ('<b>'+_('Group %s deleted') %cl_group_name\
                                   + '!</b>')
            else:
                self.printSUCCESS ('<b>'+_('Group %s has not been deleted.') \
                                   %cl_group_name + '!</b>')
            return True

        except KeyboardInterrupt:
            self.endTask()
            return False
        except Exception as e:
            msg = e.message
            if not msg:
                msg = e.reason
            self.printERROR (_("Exception: %s") %msg)
            return False


    @rpc(Integer, DetailGroupInfo, _returns = Array(ReturnedMessage))
    @Dec.core_method(gui=True, title=__('Delete the group'),
                     command='cl-core-del-group',rights=["edit_group"])
    @catchExcept()
    def core_del_group(self, sid, info):
        #dv = self.get_cache(sid,'delete_request',"vars")
        #if not dv:
        dv = datavars.DataVarsCore()
        dv.importCore()
        dv.flIniFile()
        if not info:
            return [ReturnedMessage(type = 'error', field = 'cl_group_name', \
                    message = _('use --group-name to select the group'))]
        try:
            dv.Set('cl_group_name', info.cl_group_name)
        except VariableError, e:
            return [ReturnedMessage(type = 'error', message = e.message, \
                                    field = 'cl_group_name')]
        self.set_cache(sid, 'core_del_group', "vars", dv, smart=False)
        return self.delGroupCommon(sid,info,'core_del_group')

    @rpc(Integer, ViewParams, _returns = ViewInfo)
    @catchExcept()
    def core_del_group_view (self, sid, params):
        dv = self.get_cache(sid, 'core_del_group', 'dv')
        if not dv:
            dv = datavars.DataVarsCore()
            dv.importCore()
            dv.flIniFile()
            dv.Get('cl_group_name')

        dv.addGroup(_("Deleting the group"),
            normal=('cl_group_name', 'cl_group_rights'),
            next_label=_("Delete"))

        view = ViewInfo(dv)
        self.set_cache(sid, 'core_del_group', "vars", dv, smart=False)
        return view

############### END DELETE GROUP METHODS ####################################
############### BEGIN ADD GROUP METHODS #####################################
    def addGroupCommon(self,sid,info,methodname):
        """
        Install common method
        """
        try:
            dv = self.get_cache(sid,methodname,"vars")
            errors = self.check_group_params(dv, info,
                       ordered=['cl_group_add', 'cl_group_rights'],
                       allvars=True)
            if errors:
                return errors 
            add_group_meth = type("delGroupCommon",(self.Common,
                                CoreWsdl, object), {})
            pid = self.startprocess(sid, target=add_group_meth,
                                method="add_group_meth",
                                method_name='core_add_group',
                                args_proc = (dv,))
            returnmess = ReturnedMessage(type = 'pid', message = pid)
            returnmess.type = "pid"
            returnmess.message = pid
            dv = self.clear_cache(sid,methodname)
            return [returnmess]
        finally:
            if dv:
                self.set_cache(sid,methodname,"vars",dv,smart=False)
        return []

    def add_group_meth(self, dv):
        try:
            group_rights_file = dv.Get('cl_core_group_rights')
            cl_group_add = str(dv.Get('cl_group_add'))
            cl_group_rights = ', '.join(dv.Get('cl_group_rights'))

            self.printSUCCESS (_('Group name') + " = %s" %cl_group_add)
            self.printSUCCESS (_('Group permissions')+" = %s"%cl_group_rights)

            result = []
            for line in readLinesFile(group_rights_file):
                if line.startswith('#') or not line:
                    result.append(line)
                    continue
                words = line.split(' ',1)
                # first word in line equal name input method
                if words[0] == cl_group_add:
                    self.printSUCCESS (_('Group %s already exists!') \
                                       %cl_group_add)
                    return True
                result.append(line)

            result.append(cl_group_add + ' ' + cl_group_rights)

            fd = open (group_rights_file, 'w')
            for lines in result:
                fd.write(lines + '\n')
            fd.close()
            self.printSUCCESS (_("Group %s added") %cl_group_add)

            return True
        except KeyboardInterrupt:
            self.endTask()
            return False
        except Exception, e:
            msg = e.message
            if not msg:
                msg = e.reason
            self.printERROR (_("Exception: %s") %msg)
            return False

    @rpc(Integer, AddGroupInfo, _returns = Array(ReturnedMessage))
    @Dec.core_method(gui=True, title=__('Add a group'),
                     command='cl-core-add-group',rights=["edit_group"])
    @catchExcept()
    def core_add_group ( self, sid, info):
        #dv = self.get_cache(sid,'delete_request',"vars")
        #if not dv:
        dv = datavars.DataVarsCore()
        dv.importCore()
        dv.flIniFile()
        if not info:
            return [ReturnedMessage(type = 'error', field = 'cl_group_add',\
                          message = _('use --add-group to add a group'))]
        try:
            dv.Set('cl_group_add', info.cl_group_add)
        except VariableError, e:
            return [ReturnedMessage(type = 'error', message = e.message, \
                                    field = 'cl_group_add')]
        if not info.cl_group_rights:
            info.cl_group_rights = []
        try:
            dv.Set('cl_group_rights', info.cl_group_rights)
        except VariableError, e:
            return [ReturnedMessage(type = 'error', message = e.message, \
                                    field = 'cl_group_rights')]
        self.set_cache(sid, 'core_add_group', "vars", dv, smart=False)
        return self.addGroupCommon(sid,info,'core_add_group')

    @rpc(Integer, ViewParams, _returns = ViewInfo)
    @catchExcept()
    def core_add_group_view (self, sid, params):
        dv = self.get_cache(sid, 'core_add_group', 'dv')
        if not dv:
            dv = datavars.DataVarsCore()
            dv.importCore()
            dv.flIniFile()

        dv.addGroup(None,
            normal=('cl_group_add', 'cl_group_rights'),
            next_label=_("Add"))

        view = ViewInfo(dv)
        self.set_cache(sid, 'core_add_group', "vars", dv, smart=False)
        return view
