# -*- coding: utf-8 -*-
# (c) 2003 Centre de Recherche en Informatique ENSMP Fontainebleau <http://cri.ensmp.fr>
# (c) 2003 Benoît PIN <mailto:pin@cri.ensmp.fr>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
#


from OFS.ObjectManager import ObjectManager
from OFS.SimpleItem import SimpleItem
from OFS.PropertyManager import PropertyManager
from Globals import InitializeClass, DTMLFile
from Products.CMFCore.TypesTool import FactoryTypeInformation
from Products.CMFCore.utils import getToolByName
from types import InstanceType
import config

from AccessControl import ClassSecurityInfo, Unauthorized
from Products.CMFCore.permissions import View, ModifyPortalContent, \
     ManagePortal


class RuleError(Exception) :
    def __init__(self, errorMessage) :
        self.errorMessage = errorMessage

    def __str__(self) :
        return self.errorMessage

addMosaicBlockInformationForm = DTMLFile('dtml/addMosaicBlockForm', globals())

def addMosaicBlockInformation(self, id=None, blockType = '', metaFti = None, REQUEST=None) :
    """Add a MosaicBlock"""
    if blockType :
        metaFtis = config.getDefaultBlockMetaFtis()
        if not metaFtis.has_key(blockType) :
            raise ValueError, "Unknown block type : %s" % blockType
        else :
            blockFti = metaFtis[blockType]
    elif metaFti :
        blockFti = metaFti.copy()
    else :
        raise AttributeError, """
        You must specify a default block type or a meta factory type information
        """
    if not id :
        id = blockFti['id']
    else :
        blockFti['id'] = id

    mb = MosaicBlockInformation(blockFti)
    self._setObject(id, mb)

    if REQUEST is not None:
        REQUEST.RESPONSE.redirect('%s/manage_main' % self.absolute_url())
    return id


class MosaicBlockInformation(ObjectManager, FactoryTypeInformation) :
    """Base Block"""
    
    meta_type = "Mosaic Block Information"
    
    isPrincipiaFolderish = 0

    security = ClassSecurityInfo()

    _fti_properties = FactoryTypeInformation._properties

    _properties = (_fti_properties[:5] +
                   ({'id':'template', 'type': 'string', 'mode':'w',
                     'label':'Template'},
                    {'id':'notify_wf', 'type': 'boolean', 'mode':'w',
                     'label':'Notify Workflow Created'},) +
                   _fti_properties[5:]
                   )


    manage_slotsForm = DTMLFile('dtml/slotsForm', globals())
    manage_rulesForm = DTMLFile('dtml/rulesForm', globals())
    manage_options = (FactoryTypeInformation.manage_options[:1] +
                      ({'label'    : 'Slots', 'action'   : 'manage_slotsForm'},) +
                      ({'label'    : 'Rules', 'action'   : 'manage_rulesForm'},) +
                      FactoryTypeInformation.manage_options[1:]
                      )
    
        
    def __init__(self, blockFti) :
        FactoryTypeInformation.__init__(self, **blockFti)

        # init Slots informations
        for slot in blockFti['slots'] :
            slotInfo = SlotInfo(slot['id'], slot['type'], slotArgs = slot['args'])
            self._setObject(slot['id'], slotInfo)

        # init Rules informations
        for rule in blockFti['rules'] :
            ruleInfo = RuleInfo(rule['blockType'], rule['maxNbInstance'], rule['allowMove'], rule['mode'])
            self._setObject(rule['blockType'], ruleInfo)
                        

    security.declareProtected(ManagePortal, 'manage_addSlot')
    def manage_addSlot(self, id, type, REQUEST=None) :
        """Add a slot contruction information"""
        slotInfo = SlotInfo(id, type)
        self._setObject(id, slotInfo)
        if REQUEST :
            return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])

    security.declareProtected(ManagePortal, 'saveSlots')
    def saveSlots(self, REQUEST=None, **kw) :
        """Save slots from ZMI form"""
        # actualy the ':record' casting in an input form create an InstanceType instead of DictType
        kw.update(REQUEST.form)
        dicArgsListItems = [ (argKey, kw[argKey]) for argKey in kw.keys() if type(kw[argKey]) == InstanceType ]
        for key, args in dicArgsListItems :
            slotToEdit = getattr(self, key)
            if key != args['id'] :
                self.manage_renameObject(key, args['id'])
                slotToEdit.id = args['id']
            slotToEdit.type = args['type']
        if REQUEST is not None:
            return REQUEST.RESPONSE.redirect(REQUEST['URL1'] + '/manage_slotsForm?manage_tabs_message=Saved changes.')

    security.declareProtected(ManagePortal, 'deleteSlots')
    def deleteSlots(self, slotSelection=None, REQUEST=None) :
        """Delete slots"""
        if slotSelection :
            self.manage_delObjects(ids=slotSelection)
        if REQUEST :
            return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])



    security.declareProtected(ManagePortal, 'manage_addSlot')
    def manage_addRule(self, id, maxNbInstance, allowMove, mode, REQUEST=None) :
        """Add a rule information"""
        ruleInfo = RuleInfo(id, maxNbInstance, allowMove, mode)
        self._setObject(id, ruleInfo)
        if REQUEST :
            return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])

    security.declareProtected(ManagePortal, 'saveSlots')
    def saveRules(self, REQUEST=None, **kw) :
        """Save rules from ZMI form"""
        # actualy the ':record' casting in an input form create an InstanceType instead of DictType
        kw.update(REQUEST.form)
        dicArgsListItems = [ (argKey, kw[argKey]) for argKey in kw.keys() if type(kw[argKey]) == InstanceType ]
        for key, kwords in dicArgsListItems :
            ruleToEdit = getattr(self, key)
            if key != kwords['id'] :
                self.manage_renameObject(key, kwords['id'])
                #slotToEdit.id = args['id']
            kwArgs = {}
            
            for k in kwords.keys() : #kwords is an InstanceType not a DictType...
                kwArgs[k] = kwords[k]
                
            ruleToEdit.edit(**kwArgs)
            
        if REQUEST is not None:
            return REQUEST.RESPONSE.redirect(REQUEST['URL1'] + '/manage_rulesForm?manage_tabs_message=Saved changes.')

    security.declareProtected(ManagePortal, 'deleteSlots')
    def deleteRules(self, ruleSelection=None, REQUEST=None) :
        """Delete rules"""
        if ruleSelection :
            self.manage_delObjects(ids=ruleSelection)
        if REQUEST :
            return REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])


    def _checkConstructionRules(self, container) :
        """Check """        
        typesTool = getToolByName(self, 'mosaic_tool')
        container_ti = typesTool.getTypeInfo(container)
        rules = container_ti.objectValues(['Rule Information',])
        container_subObjs = container.objectValues()

        if self.getId() in map(lambda x : x.getId(), rules) :
            maxNbInstance = container_ti._getOb(self.getId()).maxNbInstance
            if maxNbInstance :
                if maxNbInstance <= len( [sob for sob in container_subObjs if sob.portal_type == self.getId()])  :
                    raise RuleError, "It's not allowed to add a new '%s', the quota is exceeded in this block" % \
                          self.getId()
                else :
                    return 1
            else :
                return 1 # pas de limitation du nombre d'instance
        else :
            raise RuleError, "'%s' block type construction is not authorized in '%s'" % \
                  (self.getId(), container_ti.getId())
        

    security.declarePublic('isConstructionAllowed')
    def isConstructionAllowed ( self, container ):
        """
        check factory, permissions and rules
        """
        if FactoryTypeInformation.isConstructionAllowed ( self, container ) :
            typesTool = getToolByName(self, 'mosaic_tool')
            container_ti = typesTool.getTypeInfo(container)
            if hasattr(container_ti, '_checkConstructionRules') :
                try :
                    return self._checkConstructionRules(container)
                except RuleError :
                    return 0
            return 1
        else :
            return 0

        
    def _getFactoryMethod(self, container, check_security=1):
        # pas très beau cette surcharge mais
        # FactoryTypeInformation ne vérifie pas la
        # possibilité de création par un appel à isConstructionAllowed.
        
        if not check_security : return FactoryTypeInformation._getFactoryMethod(self, container)
        
        if self.isConstructionAllowed(container) :
            return FactoryTypeInformation._getFactoryMethod(self, container)

        raise Unauthorized, ('Cannot create %s' % self.getId())        
    
    
    security.declarePrivate('_constructInstance')
    def _constructInstance(self, container, id, *args, **kw):
        ob = super(MosaicBlockInformation, self)._constructInstance(container, id, *args, **kw)
        typesTool = getToolByName(self, 'portal_types')
        
        # Add and init slots in block
        for si in self.objectValues(['Slot Information',]) :
            kw = {}
            for key, value in si.propertyItems() :
                kw[key] = value

            typesTool.constructContent(si.type, ob, si.id, **kw)
        return ob


InitializeClass(MosaicBlockInformation)

def addSlotInfo(dispatcher, id, type, slotArgs) :
    pass

class SlotInfo(SimpleItem, PropertyManager) :
    """Encapsulate slot informations"""
    meta_type="Slot Information"
    _properties = ()
    property_extensible_schema__ = 1

    manage_propertiesForm = DTMLFile('dtml/properties', globals())
    manage_options = PropertyManager.manage_options + SimpleItem.manage_options
    
    def __init__(self, id, type, slotArgs={}) :
        self.id = id
        self.type = type
        for argId in slotArgs.keys() :
            self.manage_addProperty(argId, slotArgs[argId]['value'], slotArgs[argId]['type'])
            
InitializeClass(PropertyManager)


def addRuleInfo(dispatcher, id, **kw) :
    pass

class RuleInfo(SimpleItem):
    """Encapsulate block rule informations"""
    meta_type = 'Rule Information'

    def __init__(self, id, maxNbInstance,
                 allowMove, mode,
                 allowMoveUpAndDown=0,
                 allowMoveRightAndLeft=0) :
        self.id = id # actualy Block Type name info
        self.maxNbInstance = maxNbInstance
        if allowMove :
            self.allowMoveUpAndDown = 1
            self.allowMoveRightAndLeft = 1
        else :
            self.allowMoveUpAndDown = allowMoveUpAndDown
            self.allowMoveRightAndLeft = allowMoveRightAndLeft
        self.allowMove = allowMove
        self.mode = mode

    security = ClassSecurityInfo()

    security.declareProtected(ManagePortal, 'edit')
    def edit(self, **kw) :
        for key in kw.keys() :
            if hasattr(self, key) :
                setattr(self, key, kw[key])

InitializeClass(RuleInfo)
