# ------------------------------------------------------------------------
# coding=utf-8
# ------------------------------------------------------------------------
#
#  ct_tracker.py
#  FeinCMS
#
#  Created by Martin J. Laubach on 02.10.09.
#  Copyright (c) 2009 Martin J. Laubach. All rights reserved.
#  Updated in 2011 by Matthias Kestenholz for the 1.3 release.
#
# ------------------------------------------------------------------------

"""
Track the content types for pages. Instead of gathering the content
types present in each page at run time, save the current state at
saving time, thus saving a db query on page delivery.
"""

from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _

from feincms.contrib.fields import JSONField
from feincms.models import ContentProxy


# ------------------------------------------------------------------------
class TrackerContentProxy(ContentProxy):
    def __init__(self, item):
        item._needs_content_types()
        self.item = item
        self.media_cache = None

        """
        If an object with an empty _ct_inventory is encountered, compute all the
        content types currently used on that page and save the list in the
        object itself. Further requests for that object can then access that
        information and find out which content types are used without resorting
        to multiple selects on different ct tables.

        It is therefore important that even an "empty" object does not have an
        empty _ct_inventory. (TODO, this isn't true yet.)
        """

        if self.item._ct_inventory:
            self.content_type_counts = self._from_inventory(self.item._ct_inventory)
        else:
            self.content_type_counts = self._fetch_content_type_counts(item.pk)

            self.item._ct_inventory_clobber = False
            self.item._ct_inventory = self._to_inventory(self.content_type_counts)
            self.item.save()

        self.content_type_instances = self._fetch_content_type_instances(
            self.content_type_counts)

    def _translation_map(self):
        if not hasattr(self.__class__, '_translation_map_cache'):
            # Prime translation map and cache it in the class. This needs to be
            # done late as opposed to at class definition time as not all information
            # is ready, especially when we are doing a "syncdb" the ContentType table
            # does not yet exist
            cache = {}
            for fct in self.item._feincms_content_types:
                dct = ContentType.objects.get_for_model(ct)

                # Rely on non-negative primary keys
                cache[fct.id] = dct
                cache[-dct.id] = fct

            self.__class__._translation_map_cache = cache
        return self._translation_map_cache

    def _from_inventory(self, inventory):
        """
        Transforms the inventory from Django's content types to FeinCMS's
        ContentProxy counts format.
        """

        map = self._translation_map()

        return dict((region, [
            (pk, map[-ct]) for pk, map in items
            ]) for region, items in inventory.items())

    def _to_inventory(self, counts):
        map = self._translation_map()

        return dict((region, [
            (pk, map[ct]) for pk, map in items
            ]) for region, items in inventory.items())

# ------------------------------------------------------------------------
def post_save_handler(sender, instance, **kwargs):
    """
    Intercept save and null out the content type list in the page itself.
    """

    # Clobber _ct_inventory unless we are saving an updated version right now
    clobber = getattr(instance, '_ct_inventory_clobber', True)
    instance.get_descendants(include_self=clobber).update(_ct_inventory=None)

# ------------------------------------------------------------------------
def register(cls, admin_cls):
    cls.add_to_class('_ct_inventory', JSONField(_('content types'), editable=False, blank=True, null=True))
    cls.content_proxy_class = TrackerContentProxy

    post_save.connect(post_save_handler, sender=cls)
# ------------------------------------------------------------------------

@@ -2571,21 +2571,19 @@
         
-cache
+map
  = %7B%7D%0A  
@@ -2599,16 +2599,21 @@
  for
+ idx,
  fct in 
 self
@@ -2608,16 +2608,26 @@
  fct in 
+enumerate(
 self.ite
@@ -2650,16 +2650,17 @@
 nt_types
+)
 :%0A      
@@ -2709,16 +2709,17 @@
 r_model(
+f
 ct)%0A%0A   
@@ -2787,15 +2787,14 @@
     
-cache%5Bf
+map%5B-d
 ct.i
@@ -2802,48 +2802,86 @@
 %5D = 
-dct%0A                cache%5B-dct.id%5D = fct
+idx # From-inventory map%0A                map%5Bidx%5D = dct.id  # To-inventory map
 %0A%0A  
@@ -2930,21 +2930,19 @@
 cache = 
-cache
+map
 %0A       
@@ -3252,35 +3252,34 @@
 p%5B-ct%5D) for pk, 
-map
+ct
  in items%0A      
@@ -3469,19 +3469,18 @@
 for pk, 
-map
+ct
  in item
@@ -3513,33 +3513,30 @@
 n, items in 
-inventory
+counts
 .items())%0A%0A#

Fix ct_tracker extension -- that code was NOT ready at all