import os
import re
from Acquisition import aq_base

from Products.GenericSetup.tool import SetupTool

from Products.CMFPlone.migrations.migration_util import installOrReinstallProduct
from Products.CMFPlone.migrations.v2_1.alphas import reindexCatalog, indexMembersFolder
from Products.CMFPlone.migrations.v2_1.two12_two13 import normalizeNavtreeProperties
from Products.CMFPlone.migrations.v2_1.two12_two13 import removeVcXMLRPC
from Products.CMFPlone.migrations.v2_1.two12_two13 import addActionDropDownMenuIcons
from Products.CMFPlone.migrations.v2_5.alphas import installDeprecated
from Products.CMFPlone.factory import _TOOL_ID as SETUP_TOOL_ID

from Products.CMFCore.utils import getToolByName
from Products.CMFCore.DirectoryView import createDirectoryView
from Products.CMFCore.Expression import Expression

def alpha2_beta1(portal):
    """2.5-alpha2 -> 2.5-beta1
    """
    out = []

    # Add dragdropreorder.js to ResourceRegistries.
    addDragDropReorderJS(portal, out)

    # Add getEventTypes KeywordIndex to portal_catalog
    addGetEventTypeIndex(portal, out)

    # Fix 'home' portal action
    fixHomeAction(portal, out)

    # Fixup the navtree properties (this was already done in 2.1.3, but may
    # need to be done again for those migrating from alphas)
    normalizeNavtreeProperties(portal, out)


    return out

def beta1_beta2(portal):
    """2.5-beta1 - > 2.5-beta2
    """
    out = []

    # The migration done during the alpha screwed things up, so we do it again
    # and fix the mistake while we're at it
    installDeprecated(portal, out)
    removeBogusSkin(portal, out)

    # add a property indicating if this is a big or small site, so the UI can
    # change depending on it
    propTool = getToolByName(portal, 'portal_properties', None)
    propSheet = getattr(propTool, 'site_properties', None)
    if not propSheet.hasProperty('large_site'):
        propSheet.manage_addProperty('large_site', 0, 'boolean')
        out.append("Added 'large_site' property to site_properties.")

    # Remove vcXMLRPC.js from ResourceRegistries (this was already done in
    # 2.1.3, but may need to be done again for those migrating from alphas)
    removeVcXMLRPC(portal, out)

    # Required due to a fix in PortalTransforms...
    reindexCatalog(portal, out)

    # FIXME: *Must* be called after reindexCatalog.
    # In tests, reindexing loses the folders for some reason...

    # Make sure the Members folder is cataloged
    indexMembersFolder(portal, out)

    # add icons for copy, cut, paste and delete
    addActionDropDownMenuIcons(portal, out)

    # add any appropriate plone skin layers to custom skins
    addPloneSkinLayers(portal, out)

    # Install portal_setup
    installPortalSetup(portal, out)

    # Simplify actions using the @@plone view
    simplifyActions(portal, out)

    # Use the @@plone view for the RTL.css expression entry
    migrateCSSRegExpression(portal, out)

    return out

def addDragDropReorderJS(portal, out):
    """Add dragdropreorder.js to ResourceRegistries.
    """
    jsreg = getToolByName(portal, 'portal_javascripts', None)
    script = 'dragdropreorder.js'
    if jsreg is not None:
        script_ids = jsreg.getResourceIds()
        # Failsafe: first make sure the stylesheet doesn't exist in the list
        if script not in script_ids:
            jsreg.registerScript(script)
            try:
                jsreg.moveResourceAfter(script, 'dropdown.js')
            except ValueError:
                # put it at the bottom of the stack
                jsreg.moveResourceToBottom(script)
            out.append("Added " + script + " to portal_javascipt")

def addGetEventTypeIndex(portal, out):
    """Adds the getEventType KeywordIndex."""
    catalog = getToolByName(portal, 'portal_catalog', None)
    if catalog is not None:
        try:
            index = catalog._catalog.getIndex('getEventType')
        except KeyError:
            pass
        else:
            indextype = index.__class__.__name__
            if indextype == 'KeywordIndex':
                return 0
            catalog.delIndex('getEventType')
            out.append("Deleted %s 'getEventType' from portal_catalog." % indextype)

        catalog.addIndex('getEventType', 'KeywordIndex')
        out.append("Added KeywordIndex 'getEventType' to portal_catalog.")
        return 1 # Ask for reindexing
    return 0

def fixHomeAction(portal, out):
    """Make the 'home' action use the @@plone view to get a properly rooted
    navtree.
    """
    newaction = { 'id'         : 'index_html',
                  'name'       : 'Home',
                  'action'     : 'string:${here/@@plone/navigationRootUrl}',
                  'condition'  : '',
                  'permission' : 'View',
                  'category'   : 'portal_tabs',
                }
    exists = False
    actionsTool = getToolByName(portal, 'portal_actions', None)
    if actionsTool is not None:
        new_actions = actionsTool._cloneActions()
        for action in new_actions:
            if action.getId() == newaction['id'] and action.category == newaction['category']:
                exists = True
                action.condition = Expression(text=newaction['condition']) or ''
                out.append('Modified existing home/index_html action')
        if exists:
            actionsTool._actions = new_actions
        else:
            actionsTool.addAction(newaction['id'],
                    name=newaction['name'],
                    action=newaction['action'],
                    condition=newaction['condition'],
                    permission=newaction['permission'],
                    category=newaction['category'],
                    visible=1)
            out.append("Added missing home/index_html action")

def removeBogusSkin(portal, out):
    skins = getToolByName(portal, 'portal_skins', None)
    if skins is not None:
        if skins._getSelections().has_key('cmf_legacy'):
            skins.manage_skinLayers(('cmf_legacy',), del_skin=True)
            out.append("Deleted incorrectly added 'cmf_legacy' skin")

def addPloneSkinLayers(portal, out):
    st = getToolByName(portal, 'portal_skins', None)
    if st is None:
        out.append('No portal_skins tool')
        return

    for skin in st._getSelections().keys():
        path = st.getSkinPath(skin)
        path = [p.strip() for p in path.split(',')]
        if not 'plone_deprecated' in path:
            path.append('plone_deprecated')
            st.addSkinSelection(skin, ','.join(path))
            out.append('Added plone_deprecated to %s' % skin)

def installPortalSetup(portal, out):
    """Adds portal_setup if not installed yet."""
    if SETUP_TOOL_ID not in portal.objectIds():
        portal._setObject(SETUP_TOOL_ID, SetupTool(SETUP_TOOL_ID))
        out.append('Added setup_tool.')

# A set of regexes and substitution strings for cleaning up the current
# actions, in particular to make optimal use of the methods provided by
# @@plone and remove deprecation warnings.
action_replacements = [
# Remove leading space from string and python expressions, it is annoying
(re.compile(r"^string: "),
 r"string:"),
(re.compile(r"^python: "),
 r"python:"),
(re.compile(r"portal\.portal_membership\.checkPermission"),
 r"checkPermission"),
(re.compile(r"^python:\(\(object\.isDefaultPageInFolder\(\) and object.getParentNode\(\)\.absolute_url\(\)\) or folder_url\)\+(?:\"|')/(.+)(?:\"|')$"),
 r"string:${globals_view/getCurrentFolderUrl}/\1"),
(re.compile(r"python:(?:\"|')%s/(.+)(?:\"|')%\(\(object\.isDefaultPageInFolder\(\) or not object\.is_folderish\(\)\) and object\.getParentNode\(\)\.absolute_url\(\) or object_url\)$"),
 r"string:${globals_view/getCurrentFolderUrl}/\1"),
(re.compile(r"^python:(?:\"|')%s/(.+)(?:\"|')%\(object\.isDefaultPageInFolder\(\) and object.getParentNode\(\)\.absolute_url\(\) or object_url\)$"),
 r"string:${globals_view/getCurrentObjectUrl}/\1"),
(re.compile(r"object is not portal and not \(object\.isDefaultPageInFolder\(\) and object\.getParentNode\(\) is portal\)"),
 r"not globals_view.isPortalOrPortalDefaultPage()"),
(re.compile(r"object\.aq_inner\.getParentNode\(\)"),
 r"globals_view.getParentObject()"),
(re.compile("here/@@plone"),
 r"globals_view"),
(re.compile(r"^python:portal\.portal_membership\.getHomeUrl\(\)\+(?:\"|')/(.+)(?:\"|')$"),
 r"string:${portal/portal_membership/getHomeUrl}/\1"),
]

def simplifyActions(portal, out):
    from Products.CMFCore.ActionInformation import ActionInformation
    action_tool = getToolByName(portal, 'portal_actions', None)
    if action_tool is not None:
        providers = action_tool.listActionProviders()
        # Iterate ofer all action providers
        for provider in providers:
            tool = getToolByName(portal, provider, None)
            # If this is not a provider with persistent Action objects skip it
            if not getattr(tool, '_actions', None) or \
               not isinstance(tool._actions[0], ActionInformation):
                continue
            actions = tool.listActions()
            # iterate through the actions and for each action check if it
            # matches any of the patterns we want to replace
            for action in actions:
                action_id = '%s/%s/%s'%(provider, action.getCategory(),
                                        action.getId())
                cur_expr = action.getActionExpression()
                cur_condition = action.getCondition()
                for regex, replacement in action_replacements:
                    new_expr = regex.sub(replacement, cur_expr)
                    new_condition = regex.sub(replacement, cur_condition)
                    if new_expr != cur_expr:
                        action.setActionExpression(new_expr)
                        out.append(
                      'Changed url expression on action %s from: \n%s\nto:\n%s'%(
                                             action_id, cur_expr, new_expr))
                    if new_condition != cur_condition:
                        action.edit(condition=new_condition)
                        out.append(
                           'Changed condition on action %s from: \n"%s"\nto:\n"%s"'%(
                                         action_id, cur_condition, new_condition))
                    cur_expr = new_expr
                    cur_condition = new_condition


def migrateCSSRegExpression(portal, out):
    """Changes calls to the isRightToLeft script to use the view, also
       replaces the use of restrictedTraverse with a more compact path
       expression."""
    css_reg = getToolByName(portal, 'portal_css', None)
    if css_reg is not None:
        resource = css_reg.getResource('RTL.css')
        # The None that comes out of RR is apparently acquisition wrapped,
        # nasty.
        if aq_base(resource) is not None:
            css_expr = resource.getExpression()
            new_expr = 'object/@@plone/isRightToLeft'
            if "object.isRightToLeft" in css_expr or \
               "object.restrictedTraverse('@@plone')" in css_expr:
                resource.setExpression(new_expr)
                css_reg.cookResources()
                out.append("Fixed RTL.css expression to use the @@plone view")
