import re

import django
from django.conf.urls import url
from django.views.generic import RedirectView
from django.contrib import admin
from django.contrib.admin.utils import unquote
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect, Http404
from django.http import HttpResponsePermanentRedirect
from django.utils.html import escape
from django.utils.translation import ugettext as _
try:
    from django.utils.encoding import force_text
except ImportError:  # Django < 1.5
    from django.utils.encoding import force_unicode as force_text

from treemenus.models import Menu, MenuItem
from treemenus.utils import get_parent_choices, MenuItemChoiceField, move_item_or_clean_ranks


class MenuItemAdmin(admin.ModelAdmin):
    ''' This class is used as a proxy by MenuAdmin to manipulate menu items. It should never be registered. '''
    def __init__(self, model, admin_site, menu):
        super(MenuItemAdmin, self).__init__(model, admin_site)
        self._menu = menu

    def delete_view(self, request, object_id, extra_context=None):
        if request.method == 'POST':  # The user has already confirmed the deletion.
            # Delete and return to menu page
            super(MenuItemAdmin, self).delete_view(request, object_id, extra_context)
            return HttpResponseRedirect("../../../")
        else:
            # Show confirmation page
            return super(MenuItemAdmin, self).delete_view(request, object_id, extra_context)

    def save_model(self, request, obj, form, change):
        obj.menu = self._menu
        obj.save()

    def response_add(self, request, obj, post_url_continue=None):
        if django.VERSION < (1, 5):
            post_url_continue = '../%s/'
        else:
            pk_value = obj._get_pk_val()
            post_url_continue = '../%s/' % pk_value
        response = super(MenuItemAdmin, self).response_add(request, obj, post_url_continue)
        if "_continue" in request.POST:
            return response
        elif "_addanother" in request.POST:
            return HttpResponseRedirect(request.path)
        elif "_popup" in request.POST:
            return response
        else:
            return HttpResponseRedirect("../../")

    def response_change(self, request, obj):
        super(MenuItemAdmin, self).response_change(request, obj)
        if "_continue" in request.POST:
            return HttpResponseRedirect(request.path)
        elif "_addanother" in request.POST:
            return HttpResponseRedirect("../add/")
        elif "_saveasnew" in request.POST:
            return HttpResponseRedirect("../%s/" % obj._get_pk_val())
        else:
            return HttpResponseRedirect("../../")

    def get_form(self, request, obj=None, **kwargs):
        form = super(MenuItemAdmin, self).get_form(request, obj, **kwargs)
        choices = get_parent_choices(self._menu, obj)
        form.base_fields['parent'] = MenuItemChoiceField(choices=choices)
        return form


class MenuAdmin(admin.ModelAdmin):
    menu_item_admin_class = MenuItemAdmin

    def __call__(self, request, url):
        ''' DEPRECATED!! More recent versions of Django use the get_urls method instead.
            Overriden to route extra URLs.
        '''
        if url:
            if url.endswith('items/add'):
                return self.add_menu_item(request, unquote(url[:-10]))
            if url.endswith('items'):
                return HttpResponseRedirect('../')
            match = re.match('^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)$', url)
            if match:
                return self.edit_menu_item(request, match.group('menu_pk'), match.group('menu_item_pk'))
            match = re.match('^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)/delete$', url)
            if match:
                return self.delete_menu_item(request, match.group('menu_pk'), match.group('menu_item_pk'))
            match = re.match('^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)/history$', url)
            if match:
                return self.history_menu_item(request, match.group('menu_pk'), match.group('menu_item_pk'))
            match = re.match('^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)/move_up$', url)
            if match:
                return self.move_up_item(request, match.group('menu_pk'), match.group('menu_item_pk'))
            match = re.match('^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)/move_down$', url)
            if match:
                return self.move_down_item(request, match.group('menu_pk'), match.group('menu_item_pk'))
        return super(MenuAdmin, self).__call__(request, url)

    def get_urls(self):
        urls = super(MenuAdmin, self).get_urls()
        my_urls = [url(r'^(?P<menu_pk>[-\w]+)/items/add/$',
                            self.admin_site.admin_view(self.add_menu_item)),
                           url(r'^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)/$',
                            self.admin_site.admin_view(self.edit_menu_item)),
                           url(r'^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)/delete/$',
                            self.admin_site.admin_view(self.delete_menu_item)),
                           url(r'^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)/history/$',
                            self.admin_site.admin_view(self.history_menu_item)),
                           url(r'^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)/move_up/$',
                            self.admin_site.admin_view(self.move_up_item)),
                           url(r'^(?P<menu_pk>[-\w]+)/items/(?P<menu_item_pk>[-\w]+)/move_down/$',
                            self.admin_site.admin_view(self.move_down_item)),
                           ]

        if django.VERSION >= (1, 4):
            # Dummy named URLs to satisfy reversing the reversing requirements
            # of the menuitem add/change views. It shouldn't ever be used; it
            # just needs to exist so that it get resolved internally by the
            # django admin.
            
            my_urls += [
                                url(r'^item_changelist/$',
                                    RedirectView.as_view(url='/'),
                                    name='treemenus_menuitem_changelist'),
                                url(r'^item_add/$',
                                    RedirectView.as_view(url='/'),
                                    name='treemenus_menuitem_add'),
                                url(r'^item_history/(?P<pk>[-\w]+)/$',
                                    self.menu_item_redirect,
                                    {'action' : 'history'},
                                    name='treemenus_menuitem_history'),
                                url(r'^item_delete/(?P<pk>[-\w]+)/$',
                                    self.menu_item_redirect,
                                    {'action': 'delete'},
                                    name='treemenus_menuitem_delete'),
                                ]
        return my_urls + urls

    def get_object_with_change_permissions(self, request, model, obj_pk):
        ''' Helper function that returns a menu/menuitem if it exists and if the user has the change permissions '''
        try:
            obj = model._default_manager.get(pk=obj_pk)
        except model.DoesNotExist:
            # Don't raise Http404 just yet, because we haven't checked
            # permissions yet. We don't want an unauthenticated user to be able
            # to determine whether a given object exists.
            obj = None
        if not self.has_change_permission(request, obj):
            raise PermissionDenied
        if obj is None:
            raise Http404('%s object with primary key %r does not exist.' % (model.__name__, escape(obj_pk)))
        return obj

    def menu_item_redirect(self, request, pk, action):
        menu_pk = MenuItem.objects.select_related('menu').get(id=pk).menu.id
        return HttpResponsePermanentRedirect(
                r'../../%d/items/%s/%s/' % (menu_pk, pk, action))

    def add_menu_item(self, request, menu_pk):
        ''' Custom view '''
        menu = self.get_object_with_change_permissions(request, Menu, menu_pk)
        menuitem_admin = self.menu_item_admin_class(MenuItem, self.admin_site, menu)
        return menuitem_admin.add_view(request, extra_context={'menu': menu})

    def edit_menu_item(self, request, menu_pk, menu_item_pk):
        ''' Custom view '''
        menu = self.get_object_with_change_permissions(request, Menu, menu_pk)
        menu_item_admin = self.menu_item_admin_class(MenuItem, self.admin_site, menu)
        return menu_item_admin.change_view(request, menu_item_pk, extra_context={'menu': menu})

    def delete_menu_item(self, request, menu_pk, menu_item_pk):
        ''' Custom view '''
        menu = self.get_object_with_change_permissions(request, Menu, menu_pk)
        menu_item_admin = self.menu_item_admin_class(MenuItem, self.admin_site, menu)
        return menu_item_admin.delete_view(request, menu_item_pk, extra_context={'menu': menu})

    def history_menu_item(self, request, menu_pk, menu_item_pk):
        ''' Custom view '''
        menu = self.get_object_with_change_permissions(request, Menu, menu_pk)
        menu_item_admin = self.menu_item_admin_class(MenuItem, self.admin_site, menu)
        return menu_item_admin.history_view(request, menu_item_pk, extra_context={'menu': menu})

    def move_down_item(self, request, menu_pk, menu_item_pk):
        self.get_object_with_change_permissions(request, Menu, menu_pk)
        menu_item = self.get_object_with_change_permissions(request, MenuItem, menu_item_pk)

        if menu_item.rank < menu_item.siblings().count():
            move_item_or_clean_ranks(menu_item, 1)
            msg = _('The menu item "%s" was moved successfully.') % force_text(menu_item)
        else:
            msg = _('The menu item "%s" is not allowed to move down.') % force_text(menu_item)

        if django.VERSION >= (1, 4):
            self.message_user(request, message=msg)
        else:
            request.user.message_set.create(message=msg)

        return HttpResponseRedirect('../../../')

    def move_up_item(self, request, menu_pk, menu_item_pk):
        self.get_object_with_change_permissions(request, Menu, menu_pk)
        menu_item = self.get_object_with_change_permissions(request, MenuItem, menu_item_pk)

        if menu_item.rank > 0:
            move_item_or_clean_ranks(menu_item, -1)
            msg = _('The menu item "%s" was moved successfully.') % force_text(menu_item)
        else:
            msg = _('The menu item "%s" is not allowed to move up.') % force_text(menu_item)

        if django.VERSION >= (1, 4):
            self.message_user(request, message=msg)
        else:
            request.user.message_set.create(message=msg)

        return HttpResponseRedirect('../../../')


admin.site.register(Menu, MenuAdmin)
