# Python imports
import locale
import math
import uuid

# django imports
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.db import models
from django.template.defaultfilters import striptags
from django.utils.translation import ugettext_lazy as _
from django.conf import settings

# lfs imports
import lfs.catalog.utils
from lfs.core.fields.thumbs import ImageWithThumbsField
from lfs.core import utils as core_utils
from lfs.core.managers import ActiveManager
from lfs.catalog.settings import CHOICES, CONTENT_CATEGORIES
from lfs.catalog.settings import CHOICES_STANDARD
from lfs.catalog.settings import CHOICES_YES
from lfs.catalog.settings import PRODUCT_TYPE_CHOICES
from lfs.catalog.settings import CONFIGURABLE_PRODUCT
from lfs.catalog.settings import STANDARD_PRODUCT
from lfs.catalog.settings import VARIANT
from lfs.catalog.settings import PRODUCT_WITH_VARIANTS
from lfs.catalog.settings import CAT_CATEGORY_PATH
from lfs.catalog.settings import CATEGORY_TEMPLATES
from lfs.catalog.settings import CONTENT_PRODUCTS
from lfs.catalog.settings import LIST
from lfs.catalog.settings import DELIVERY_TIME_UNIT_CHOICES
from lfs.catalog.settings import DELIVERY_TIME_UNIT_SINGULAR
from lfs.catalog.settings import DELIVERY_TIME_UNIT_HOURS
from lfs.catalog.settings import DELIVERY_TIME_UNIT_DAYS
from lfs.catalog.settings import DELIVERY_TIME_UNIT_WEEKS
from lfs.catalog.settings import DELIVERY_TIME_UNIT_MONTHS
from lfs.catalog.settings import PROPERTY_FIELD_CHOICES
from lfs.catalog.settings import PROPERTY_NUMBER_FIELD
from lfs.catalog.settings import PROPERTY_SELECT_FIELD
from lfs.catalog.settings import PROPERTY_TEXT_FIELD
from lfs.catalog.settings import PROPERTY_STEP_TYPE_CHOICES
from lfs.catalog.settings import PROPERTY_STEP_TYPE_AUTOMATIC
from lfs.catalog.settings import PROPERTY_STEP_TYPE_MANUAL_STEPS
from lfs.catalog.settings import PROPERTY_STEP_TYPE_FIXED_STEP
from lfs.catalog.settings import PROPERTY_VALUE_TYPE_DEFAULT
from lfs.catalog.settings import PROPERTY_VALUE_TYPE_DISPLAY
from lfs.catalog.settings import PROPERTY_VALUE_TYPE_VARIANT
from lfs.catalog.settings import PRODUCT_TEMPLATES
from lfs.catalog.settings import QUANTITY_FIELD_TYPES
from lfs.catalog.settings import QUANTITY_FIELD_INTEGER
from lfs.catalog.settings import QUANTITY_FIELD_DECIMAL_1
from lfs.catalog.settings import THUMBNAIL_SIZES
from lfs.catalog.settings import VARIANTS_DISPLAY_TYPE_CHOICES
from lfs.catalog.settings import CATEGORY_VARIANT_CHEAPEST_PRICE
from lfs.catalog.settings import CATEGORY_VARIANT_CHEAPEST_BASE_PRICE
from lfs.catalog.settings import CATEGORY_VARIANT_CHEAPEST_PRICES
from lfs.catalog.settings import CATEGORY_VARIANT_DEFAULT
from lfs.catalog.settings import SELECT

from lfs.tax.models import Tax
from lfs.supplier.models import Supplier
from lfs.manufacturer.models import Manufacturer


def get_unique_id_str():
    return str(uuid.uuid4())

LFS_UNITS = []
for u in settings.LFS_UNITS:
    LFS_UNITS.append([u, u])

LFS_PRICE_UNITS = []
for u in settings.LFS_PRICE_UNITS:
    LFS_PRICE_UNITS.append([u, u])

LFS_BASE_PRICE_UNITS = []
for u in settings.LFS_BASE_PRICE_UNITS:
    LFS_BASE_PRICE_UNITS.append([u, u])

LFS_PACKING_UNITS = []
for u in settings.LFS_PACKING_UNITS:
    LFS_PACKING_UNITS.append([u, u])


class Category(models.Model):
    """A category is used to browse through the shop products. A category can
    have one parent category and several child categories.

    **Attributes:**

    name
        The name of the category.

    slug
        Part of the URL

    parent
        Parent of the category. This is used to create a category tree. If
        it's None the category is a top level category.

    show_all_products
       If True the category displays it's direct products as well as products
       of it's sub categories. If False only direct products will be
       displayed.

    products
        The assigned products of the category.

    short_description
        A short description of the category. This is used in overviews.

    description
        The description of the category. This can be used in details views
        of the category.

    image
        The image of the category.

    position
        The position of the category within the shop resp. the parent
        category.

    static_block
        A assigned static block to the category.

    content
        decides which content will be displayed. At the moment this is either
        sub categories or products.

    active_formats
        If True product_rows, product_cols and category_cols are taken from
        the category otherwise from the parent.

    product_rows, product_cols, category_cols
        Format information for the category views

    meta_title
        Meta title of the category (HTML title)

    meta_keywords
        Meta keywords of the category

    meta_description
       Meta description of the category

    uid
       The unique id of the category

    level
       The level of the category within the category hierachie, e.g. if it
       is a top level category the level is 1.

    template
       Sets the template which renders the category view. If left to None, default template is used.
    """
    name = models.CharField(_(u"Name"), max_length=50)
    slug = models.SlugField(_(u"Slug"), unique=True)
    parent = models.ForeignKey("self", verbose_name=_(u"Parent"), blank=True, null=True)

    # If selected it shows products of the sub categories within the product
    # view. If not it shows only direct products of the category.
    show_all_products = models.BooleanField(_(u"Show all products"), default=True)

    products = models.ManyToManyField("Product", verbose_name=_(u"Products"), blank=True, related_name="categories")
    short_description = models.TextField(_(u"Short description"), blank=True)
    description = models.TextField(_(u"Description"), blank=True)
    image = ImageWithThumbsField(_(u"Image"), upload_to="images", blank=True, null=True, sizes=THUMBNAIL_SIZES)
    position = models.IntegerField(_(u"Position"), default=1000)
    exclude_from_navigation = models.BooleanField(_(u"Exclude from navigation"), default=False)

    static_block = models.ForeignKey("StaticBlock", verbose_name=_(u"Static block"), blank=True, null=True, related_name="categories")
    template = models.PositiveSmallIntegerField(_(u"Category template"), blank=True, null=True, choices=CATEGORY_TEMPLATES)
    active_formats = models.BooleanField(_(u"Active formats"), default=False)

    product_rows = models.IntegerField(_(u"Product rows"), default=3)
    product_cols = models.IntegerField(_(u"Product cols"), default=3)
    category_cols = models.IntegerField(_(u"Category cols"), default=3)

    meta_title = models.CharField(_(u"Meta title"), max_length=100, default="<name>")
    meta_keywords = models.TextField(_(u"Meta keywords"), blank=True)
    meta_description = models.TextField(_(u"Meta description"), blank=True)

    level = models.PositiveSmallIntegerField(default=1)
    uid = models.CharField(max_length=50, editable=False, unique=True, default=get_unique_id_str)

    class Meta:
        ordering = ("position", )
        verbose_name = _('Category')
        verbose_name_plural = _('Categories')
        app_label = 'catalog'


    def __unicode__(self):
        return u"%s (%s)" % (self.name, self.slug
)

    def get_absolute_url(self):
        """
        Returns the absolute_url.
        """
        return ("lfs_category", (), {"slug": self.slug})
    get_absolute_url = models.permalink(get_absolute_url)

    @property
    def content_type(self):
        """
        Returns the content type of the category as lower string.
        """
        return u"category"

    def get_all_children(self):
        """
        Returns all child categories of the category.
        """
        def _get_all_children(category, children):
            for category in Category.ob