# coding: utf-8
"""
@author: d.bozhenko
"""
import string
import random
import os
import json
import urllib2
from xml.dom.minidom import parseString
from security._security_orm import customPermsCursor, rolesCustomPermsCursor
from ru.curs.celesta.syscursors import PermissionsCursor, UserRolesCursor
from ru.curs.celesta.showcase.utils import XMLJSONConverter
from common.dbutils import DataBaseXMLExchange
from java.io import File, FileInputStream, FileOutputStream
from common.grainssettings import SettingsManager

try:
    from ru.curs.showcase.core.jython import JythonDownloadResult
except:
    from ru.curs.celesta.showcase import JythonDownloadResult

class Settings():
    u"""Класс работы с настройками гранулы security из grainSettings.xml    
    
    """
    settings = {}    # Статическая переменная класса, в которую сохраняются настройки гранулы.
                # Данная реализация нужна для того, чтобы не обращаться каждый раз к расположенному на жестком диске файлу настроек
                # rainSettings.xml
    settingsTag = 'securitySettings'    # Наименование тэга настроек гранулы
    grainName = 'security'    # название гранулы

    def __init__(self):
        self.settingsInstance = SettingsManager()
        # Создаём объект класса получения настроек свойств _всех_ гранул.

    def getSettingsJSON(self):
        u""" Функция получения JSON'а всех настроек гранулы
            JSON имеет вид:
            {<Имя параметра>: <значение параметра>}
        """
        if self.settings == {}:
            # Если переменная настроек self.settings пуста, получаем настройки из общего файла настроек grainSettings.xml
            names = self.settingsInstance.getGrainSettings("%s/parameter/@name" % self.settingsTag, self.grainName)
            values = self.settingsInstance.getGrainSettings("%s/parameter/@value" % self.settingsTag, self.grainName)
            settingsJson = {}
            for i in range(len(names)):
                settingsJson[names[i]] = values[i]
            self.settings = settingsJson
        return self.settings

    def isUseAuthServer(self):
        u"""Метод возвращает значение параметра useAuthServer настроечного файла (True/False) 
        """
        settingsJson = self.getSettingsJSON()
        return settingsJson["useAuthServer"] == True or settingsJson["useAuthServer"] == "true"

    def loginIsSubject(self):
        u"""Метод возвращает значение параметра loginEqualSubject настроечного файла (True/False) 
        """
        settingsJson = self.getSettingsJSON()
        return settingsJson["loginEqualSubject"] == True or settingsJson["loginEqualSubject"] == "true"

    def isEmployees(self):
        u"""Метод возвращает True, если в настроечном файле параметры employeesGrain, employeesTable, employeesName
            одновременно принимают значения отличные от null и пустой строки. В противном случае возвращает False 
        """
        settingsJson = self.getSettingsJSON()
        result = settingsJson["employeesGrain"] <> None and settingsJson["employeesTable"] <> None and settingsJson["employeesName"] <> None and\
                settingsJson["employeesGrain"] <> "" and settingsJson["employeesTable"] <> "" and settingsJson["employeesName"] <> "" and\
                settingsJson["employeesGrain"] <> "null" and settingsJson["employeesTable"] <> "null" and settingsJson["employeesName"] <> "null"
        return result

    def isSystemInitialised(self):
        u"""Метод возвращает значение параметра isSystemInitialised настроечного файла (True/False) 
        """
        settingsJson = self.getSettingsJSON()
        return settingsJson["isSystemInitialised"] == True or settingsJson["isSystemInitialised"] == "true"

    def getEmployeesParam(self, param):
        u"""Возвращает значение параметра настроечного файла с именем param 
        """
        settingsJson = self.getSettingsJSON()
        return settingsJson[param]

    def setEmployeesParam(self, param, value):
        u"""Присваивает параметру param из настроечного json'а значение value,
            (!)одновременно сохраняя значение в файл grainsSettings.xml 
        """
        if param in self.settings.keys():
            self.settings[param] = value
            path = "%s/parameter[@name='%s']/@value" % (self.settingsTag, param)
            self.settingsInstance.setGrainSettings(path, value, self.grainName)

    def settingsJSONSave(self):
        u""" deprecated!
        """
        # оставляем на случай, если метод где-то используется.
        # но теперь сохранение будет осуществляться методом setEmployeesParam
        pass



def id_generator(size=8, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits):
    u"""Функция генерирует произвольную строчку заданной длины и из заданного набора символов.
        По умолчанию, длина результирующей строки 8, набор символов - цифры и английские буквы. 
    """
    return ''.join(random.choice(chars) for _ in range(size))


def getUsersFromAuthServer(server, sessionId):
    u"""Функция получает адрес Mellophone и ID сессии и возвращает xml (объект xml.dom.minidom) с пользователями. 
        нужна для работы режима "useAuthServer"=="true"
    """
    req = urllib2.Request(server + '/importusers?sesid=' + sessionId, '<Request></Request>')
    data = urllib2.urlopen(req)
    users = data.read()
    return parseString(users)

def getActionJSON(add="", elementId="", caption="", height="", width=""):
    u"""Вспомогательная функция для упрощения генерации settings
        используется в гридах гранулы.
    """
    action = {"@show_in": "MODAL_WINDOW",
                "#sorted":[{"main_context":"current"},
                           {"modalwindow":{"@caption": caption,
                                           "@height": height,
                                           "@width": width}
                            },
                           {"datapanel":{"@type": "current",
                                         "@tab": "current",
                                         "element": {"@id": elementId,
                                                     "add_context":add}
                                         }
                            }]
            }
    # Использование этой функции приводит к тому, что недоступные кнопки гридов не дизейблятся, а скрываются.
    # Возможно, надо доработать.
    return action

def submissionGenPass(context, main=None, add=None, filterinfo=None, session=None, data=None):
    u"""Функция для submission, генерирующая пароль с помощью вызова id_generator 
        изпользуется в карточке users.xml
    """
    instance = json.loads(data)
    instance["schema"]["user"]["@password"] = id_generator()
    return XMLJSONConverter.jsonToXml(json.dumps(instance))

def generateGridSettings(columns={}, pagesize=25, gridHeight=250, delta=40, \
                        totalCount=0, profile="default.properties", header="", columnsSorted=None, \
                        datapanelWidth=None):
    u"""Вспомогательная функция для упрощения построения настроек грида.
        columns - json с колонками грида
        pagesize - размер страницы
        gridHeight - высота грида
        delta=40 - ширина грида рассчитывается как сумма ширин столбцов + delta (если datapanelWidth == None)
        totalCount - число записей грида
        profile="default.properties" - настройки грида
        header - заголовок
        columnsSorted - если None, сортируется по полям. Если не None, то в переменной передаются названия колонки в нужном порядке.
        datapanelWidth - Если не None, ширина грида будет равна datapanelWidth 
    """
    settings = {"gridsettings":{"columns":{"col":[]}
                                }
                }
    if columnsSorted is None:
        columnsSorted = sorted(columns.keys())
    for key in columnsSorted:
        settings["gridsettings"]["columns"]["col"].append({"@id":key, "@width": str(columns[key]) + "px"})
    settings["gridsettings"]["properties"] = {"@pagesize":pagesize,
                                            "@gridWidth":"100%",
                                            "@gridHeight":gridHeight,
                                            "@totalCount":totalCount,
                                            "@profile":profile}
    settings["gridsettings"]["labels"] = {"header":header}
    return settings

def getPermissionsOfTypeAndUser(context, sid, permissionType=None):
    u"""
        Функция возвращает курсор с разрешениями данного типа,
        которые есть у данного пользователя. Работает для permissions (если permissionType - None или tables)
        и для customPermissions.
        Если разрешений нет, возвращает None
    """
    # Насколько знаю, ни в одном решении функция не используется. Курсор с разрешениями пока никому не пригодился
    # Возможно, стоит выпилить.
    userRoles = UserRolesCursor(context)
    userRoles.setRange("userid", sid)
    filter_string = ""
    if userRoles.tryFindSet():
        filter_string = "'" + userRoles.roleid + "'"
        while True:
            if userRoles.nextInSet():
                filter_string += "|'" + userRoles.roleid + "'"
            else:
                break

    if permissionType is None or permissionType == 'tables':
        # получаем разрешения из таблицы permissions
        permissions = PermissionsCursor(context)
        if filter_string == "":
            return None
        permissions.setFilter("roleid", filter_string)
    else:
        # получаем разрешения из таблицы customPermissions
        permissions = customPermsCursor(context)
        rolePermissions = rolesCustomPermsCursor(context)
        rolePermissions.setFilter("roleid", filter_string)
        filter_string = ""
        if rolePermissions.tryFindSet():
            filter_string = "'" + rolePermissions.permissionId + "'"
            while True:
                if rolePermissions.nextInSet():
                    filter_string += "|'" + rolePermissions.permissionId + "'"
                else:
                    break
        if filter_string <> "":
            permissions.setFilter("name", filter_string)
        else:
            return None
    return permissions

def userHasPermission(context, sid, permission):
    u"""
        Функция возвращает False, если разрешения не существует или
        у данного пользователя нет такого разрешения.
        В случае, если у данного пользователя разрешение есть,
        возвращает True
        sid - sid пользователя
        permission - рарешение из таблицы customPermissions
    """
    userRoles = UserRolesCursor(context)
    if userRoles.tryGet(sid, "editor"):
        # Для роли editor есть все(!) разрешения
        return True
    userRoles.clear()
    userRoles.setRange("userid", sid)    # выбираем разрешения данного пользователя.
    permissions = customPermsCursor(context)
    if not permissions.tryGet(permission):
        # Разрешения не нашли, возвращаем False
        return False
    rolePermissions = rolesCustomPermsCursor(context)
    if userRoles.tryFindSet():
        while True:
            rolePermissions.setRange("roleid", userRoles.roleid)
            rolePermissions.setRange("permissionId", permission)
            if rolePermissions.tryFirst():
                return True
            if not userRoles.nextInSet():
                break
    return False

def tableUpload(cursorInstance, fileData):
    u"""Функция загрузки xml-данных fileData в таблицу с объектом курсора cursorInstance 
    """
    exchange = DataBaseXMLExchange(fileData, cursorInstance)
    exchange.uploadXML()

def tableDownload(cursorInstance, fileName):
    u"""Функция загрузки данных таблицы с объектом курсора cursorInstance в xml-файл
    """
    filePath = os.path.join(os.path.dirname(os.path.abspath(__file__)), fileName + '.xml')
    dataStream = FileOutputStream(filePath)
    exchange = DataBaseXMLExchange(dataStream, cursorInstance)
    exchange.downloadXML()
    dataStream.close()
    report = File(filePath)
    return JythonDownloadResult(FileInputStream(report), fileName + '.xml')