# coding: UTF-8
'''@package common.api.events.activities Модуль содержит классы конткретных
действий

Created on 16 июля 2015 г.

@author tugushev.rr
'''

from common.api.core import IJSONSerializable
from common.api.events.core import _BaseActivityElement
from common.api.utils.tools import procname


class ActionActivityType(object):
    """Описывает группы конкретных действий"""
    
    ## Обновление элементов информационной панели
    DATAPANEL='datapanel'
    ## Серверное действие
    SERVER='server'
    ## Клиентское действие
    CLIENT='client'
    ## Обновление навигатора
    NAVIGATOR='navigator'
    

class ActivityElementType(object):
    """Описывает элементы, внутри конкретных действий"""
    
    ## Элемент информационной панели; используется с типом @c ActionActivityType.DATAPANEL
    DATAPANEL='element'
    ## Клиентская или серверная функция; используется с типами @c ActionActivityType.SERVER и @c ActionActivityType.CLIENT
    CLIENT_SERVER='activity'


class DatapanelElement(_BaseActivityElement):
    """Описывает обновляемый элемент информационной панели"""
    
    def __init__(self, inId, inAddContext=None, inKeepUserSettings=None, inIsPartialUpdate=None):
        """
        @param inId, inAddContext, inKeepUserSettings, inIsPartialUpdate см. 
        common.api.events.common._BaseActivityElement
        """
        super(DatapanelElement, self).__init__(inId, inAddContext, inKeepUserSettings, inIsPartialUpdate)
    
    
    def hide(self):
        """Скрывает элемент
        @warning Для скрытия элемента в add_context записывается *hide*
        @return ссылка на себя
        """
        self._addContext = 'hide'
        return self


class ActivityElement(_BaseActivityElement):
    """Непосредственное действие, выполняемое в server/client action"""
    
    def __init__(self, inId, inProcName, inAddContext=None):
        """
        @param inId, inAddContext см. 
        common.api.events.common._BaseActivityElement
        @param inProcName (<em>string or function object</em>) функция-обработчик
        загрузки тулбара
        @return ссылка на себя) 
        """
        super(ActivityElement, self).__init__(inId, inAddContext)
        self.setProc(inProcName)
    
    
    def proc(self):
        """Возвращает функцию-обработчик
        @return (@c string) полное имя функции (qualified name)
        """
        return self.__procName
    
    
    @procname
    def setProc(self, value):
        """Устанавливает функцию-обработчик
        @param value (<em>string or function object</em>) фунция-обработчик
        @return ссылка на себя
        """
        self.__procName = value
        return self
        
        
    def toJSONDict(self):
        d = super(ActivityElement, self).toJSONDict()
        d['@name'] = self.proc()

        return d

    
class ServerElement(ActivityElement):
    """Класс непосредственного серверного действия.
    
    Полностью повторяет @c common.api.events.activiies.ActivityElement. 
    Служит для конкретной идентификации типа при использовании в 
    common.api.events.action.Action.add
    """
    def __init__(self, inId, inProcName, inAddContext=None):
        super(ServerElement, self).__init__(inId, inProcName, inAddContext)


class ClientElement(ActivityElement):
    """Класс непосредственного клиентского действия.
    
    Полностью повторяет @c common.api.events.activiies.ActivityElement. 
    Служит для конкретной идентификации типа при использовании в 
    common.api.events.action.Action.add
    """
    def __init__(self, inId, inProcName, inAddContext=None):
        super(ClientElement, self).__init__(inId, inProcName, inAddContext)


class ActionActivity(IJSONSerializable):
    """Базовый класс для определённых типов действий - обновления навигатора,
    обновления информационной панели, server/client activity
    
    Каждый такой тип представляет собой набор конкретных действий.
    """
    
    def __init__(self, inActionActivityType):
        """
        @param inActionActivityType 
        (@c common.api.events.activities.ActionActivityType)
        """
        self.__elements = []
        self._type = inActionActivityType
    
    
    def type(self):
        """Возвращает тип действия
        @return @c common.api.events.activities.ActionActivityType
        """
        return self._type
    
    
    def addElement(self, inActivityElement):
        """Добавляет конкретное действие в группу действий
        @param inActivityElement (@c common.api.events.activities.ActivityElement)
        @return ссылка на себя
        """
        self.__elements.append(inActivityElement)
        
        return self
        
        
    def toJSONDict(self):
        elList = [el.toJSONDict() for el in self.__elements]
            
        return elList


class NavigatorActivity(ActionActivity):
    """Группа действий обновления навигатора.
    
    На самом деле эта группа не является группой в полном смысле, т.к. не
    имеет множества конкретных действий.
    """
    
    def __init__(self, inRefresh, inNodeId):
        """
        @param inRefresh (@c bool) флаг, обновлять навигатор или нет
        @param inNodeId (@c string) узел навигатора, который нужно обновить
        """
        super(NavigatorActivity, self).__init__(ActionActivityType.NAVIGATOR)
        
        self.__refresh = inRefresh
        self.__nodeId = inNodeId
        
        
    def refresh(self):
        """Возвращает флаг обновления навигатора
        @return @c bool
        """
        return self.__refresh
    
    
    def setRefresh(self, value):
        """Устанавливает флаг обновления навигатора
        @param value (@c bool)
        @return ссылка на себя 
        """
        self.__refresh = value
        return self
    
    
    def nodeId(self):
        """Возвращает ИД узла навигатора
        @return @c string
        """
        return self.__nodeId
    
    
    def setNodeId(self, value):
        """Устанавливает ИД узла навигатора для обновления
        @param value (@c string)
        @return ссылка на себя 
        """
        self.__nodeId = value
        return self
    
    
    def addElement(self, inActivityElement):
        """**Не использовать**. NavigatorActivity не может содержать действий."""
        raise NotImplementedError('NavigatorActivity activity cannot have subelements!')
    
    
    def toJSONDict(self):
        return {
            '@refresh': unicode(self.__refresh).lower(),
            '@element': self.__nodeId 
        }
    
    
class DatapanelActivity(ActionActivity):
    """Группа действий обновления элементов информационной панели.
    
    При использовании DatapanelActivity в дейсвтии (*action*) навигатора,
    в качестве параметра @a inPanel конструктора #__init__ должен передаваться
    шаблон информационной панели или функция, возвращающая этот шаблон.
    
    При иcпользовании DatapanelActivity для обновления элемента информационной
    панели в действии (*action*) на какое-либо событие (*event*), в качестве 
    параметров @a inPanel и @a inTab конструктора #__init__ должны передаваться
    идентификаторы информационной панели и вкладки с обновляемым элементом 
    (или *'current'*)
    """
    
    def __init__(self, inPanel="current", inTab="current"):
        """
        @param inPanel (<tt>string or function</tt>) ИД информационной панели, 
        или шаблон, или функция, возвращающая шаблон
        @param inTab (@c string) ИД вкладки на информационной панели @a inPanel
        """
        super(DatapanelActivity, self).__init__(ActionActivityType.DATAPANEL)
        
        self.setPanel(inPanel)
        self.setTab(inTab)
    
    
    def panel(self):
        """Возвращает ИД или шаблон информационной панели, в зависимости
        от варианта использования DatapanelActivity.
        """
        return self.__panel 
    
    
    @procname
    def setPanel(self, value):
        """Устанавливает ИД или шаблон информационной панели, в зависимости
        от варианта использования DatapanelActivity.
        @param value (<tt>string or function</tt>) ИД информационной панели, 
        или шаблон, или функция, возвращающая шаблон
        @return ссылка на себя
        """
        self.__panel = value
        return self
    
    
    def tab(self):
        """Возвращает ИД вкладки в текущем действии
        @return @c string
        """
        return self.__tab
    
    
    def setTab(self, value):
        """Устанавливает ИД вкладки в текущем действии
        @return value (@c string)
        @return ссылка на себя
        """
        self.__tab = value
        return self
    
        
    def add(self, inId, inAddContext="current", inKeepUserSettings=None, inIsPartialUpdate=None):
        """Добавляет элемент, который нужно обновить
        @param inId (@c string) ИД обновляемого элемента
        @param inAddContext (<tt>любой тип</tt>) add_context элемента
        @param inKeepUserSettings, inIsPartialUpdate см. 
        @c common.api.events.common._BaseActivityElement
        @return ссылка на себя
        
        @see #addElement
        """
        return self.addElement(DatapanelElement(inId, inAddContext, inKeepUserSettings, inIsPartialUpdate))
    
    
    def addElement(self, inActivityElement):
        """Добавляет элемент, который нужно обновить
        @param inActivityElement (@c common.api.events.activities.DatapanelElement)
        @return ссылка на себя
        @throw TypeError если @a inActivityElement некорректного типа
        """
        
        if not isinstance(inActivityElement, DatapanelElement):
            raise TypeError(inActivityElement.__class__.__name__ + 'given when ' + DatapanelElement.__class__.__name__ + ' expected!')
        
        return super(DatapanelActivity, self).addElement(inActivityElement)
        
        
    def toJSONDict(self):
        d = {"@type": self.panel(), "@tab": self.tab()}
        d[ActivityElementType.DATAPANEL] = super(DatapanelActivity, self).toJSONDict()
        
        return d


class ServerActivity(ActionActivity):
    """Описывает серверное действие"""
    
    def __init__(self):
        super(ServerActivity, self).__init__(ActionActivityType.SERVER)
    
    def add(self, inId, inProcName, inAddContext="current"):
        """
        @param inId (@c string) ИД обновляемого элемента
        @param inProcName (<tt>string or function object</tt>) функция-обработчик
        действия
        @return ссылка на себя
        """
        return self.addElement(ActivityElement(inId, inProcName, inAddContext))
    
    
    def addElement(self, inActivityElement):
        """Добавляет действие, которое нужно выполнить
        @param inActivityElement (@c common.api.events.activities.ActivityElement)
        @return ссылка на себя
        @throw TypeError если @a inActivityElement некорректного типа
        """
        
        if not isinstance(inActivityElement, ActivityElement):
            raise TypeError(inActivityElement.__class__.__name__ + 'given when ' + ActivityElement.__class__.__name__ + ' expected!')
        
        return super(ServerActivity, self).addElement(inActivityElement)
        
        
    def toJSONDict(self):
        d = { ActivityElementType.CLIENT_SERVER: super(ServerActivity, self).toJSONDict()}
        return d


class ClientActivity(ServerActivity):
    """Описывает клиентское действие.
    
    Поведение этого класса полностью совпадает с поведением
    @c common.api.events.activities.ServerActivity. При добавлении 
    пользовательского действия важно помнить, что функция-обработчик действия 
    должна быть **js-функцией** 
    """
    
    def __init__(self):
        super(ClientActivity, self).__init__()
        self._type = ActionActivityType.CLIENT
        
    
if __name__ == '__main__':
    
    de = DatapanelElement('3').hide()
    
    d = DatapanelActivity() \
            .add('1', {'org': 5}) \
            .add('2', {'org': 5}, True) \
            .addElement(de)
    
    s = ServerActivity() \
            .add('p1', 'calc') \
            .add('p2', 'calc2', {'org': 5})
    
    c = ClientActivity() \
            .add('p1', 'calc') \
            .add('p2', 'calc2', {'org': 5})
            
    n = NavigatorActivity(True, '2')
    
    print d.toJSONDict()
    print s.toJSONDict()
    print c.toJSONDict()
    print n.toJSONDict()