# -*- coding: utf-8 -*-
"""
PolyNum

Définition des entités administratives
"""

from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _

class EntityType(models.Model):

    class Meta:
        app_label = 'base'
        verbose_name = _(u"Type d'entité administrative")
        verbose_name_plural = _(u"Type d'entités administratives")

    def __unicode__(self):
        return self.name

    name = models.CharField(max_length=128, verbose_name=_(u'Nom'))
    description = models.TextField(verbose_name=_(u'Description'), blank=True)

class EntityManager(models.Manager):
    def rebuild_bounds(self, start=0):
        '''Reallocate left and right bounds, starting at start'''
        def rec(start, entity):
            entity.left_bound = start
            start += 1
            for child_entity in self.filter(parent=entity):
                start = rec(start, child_entity)
            entity.right_bound = start
            entity.save()
            return start + 1
        return rec(start, self.get(parent__isnull=True))

class Entity(models.Model):
    objects = EntityManager()

    class Meta:
        app_label = 'base'
        verbose_name = _(u'Entité administrative')
        verbose_name_plural = _(u'Entités administratives')
        ordering = ('left_bound',)

    def __unicode__(self):
        return self.get_name()

    code = models.CharField(max_length=64, verbose_name=_(u'Code'), db_index=True)
    is_active = models.BooleanField(verbose_name=_(u'Active'), default=True, blank=True, db_index=True)
    name = models.CharField(max_length=128, verbose_name=_(u'Nom'), blank=True, db_index=True)
    description = models.TextField(verbose_name=_(u'Description'), blank=True, db_index=True)
    entity_type = models.ForeignKey(EntityType, null=True, blank=True, related_name='+', on_delete=models.PROTECT)
    name_override = models.CharField(max_length=128, verbose_name=_(u'Nom spécifique PolyNum'), blank=True, db_index=True)
    description_override = models.TextField(verbose_name=_(u'Description spécifique PolyNum'), blank=True, db_index=True)
    depth = models.PositiveIntegerField(verbose_name=_('Niveau'), default=0, db_index=True)
    left_bound = models.PositiveIntegerField(unique=True, db_index=True)
    right_bound = models.PositiveIntegerField(unique=True, db_index=True)
    parent = models.ForeignKey('Entity', verbose_name=_(u'Entité parente'), blank=True, null=True, related_name='children_set')

    def children(self, included=False):
        if included:
            return Entity.objects.filter(left_bound__lt=self.right_bound, left_bound__gte=self.left_bound)
        else:
            return Entity.objects.filter(left_bound__lt=self.right_bound, left_bound__gt=self.left_bound)

    def parents(self, included=False):
        if included:
            return Entity.objects.filter(left_bound__lte=self.left_bound, right_bound__gte=self.right_bound)
        else:
            return Entity.objects.filter(left_bound__lt=self.left_bound, right_bound__gt=self.right_bound)

    def parents_and_their_children(self):
        parents = self.parents().order_by('depth')
        filters = []
        for parent in parents:
            filters.append(
                    Q(left_bound__lt=parent.right_bound,
                      left_bound__gte=parent.left_bound,
                      depth__lte=parent.depth+1))
        qs = Entity.objects.filter(
                reduce(Q.__or__, filters)).order_by('depth')
        return qs

    def accounting_codes(self):
        '''
        return a set of "accounting" codes: one of the entity and those of all its parents
        '''
        for parent in self.parents(included=True).order_by('-depth').prefetch_related('accountingcode_set'):
            if parent.accountingcode_set.all():
                return parent.accountingcode_set.all()
        return AccountingCode.objects.none()

    def parent_of_type(self, type_name):
        parent = self.parent
        while parent:
            if parent.entity_type.name == type_name:
                break
            parent = parent.parent
        return parent

    def get_name(self):
        return self.name_override or self.name
    get_name.short_description = _(u'Nom')

    def get_description(self):
        return self.description_override or self.description
    get_description.short_description = _(u'Description')

    def level1_parent(self):
        '''
        Show parent of level between 1 or 3 if found, self return the current entity.
        '''
        parents = self.parents()
        if len(parents) > 3:
            return parents[3]
        return self

class AccountingCode(models.Model):
    class Meta:
        app_label = 'base'
        verbose_name = _(u'Code de comptabilité/financier')
        verbose_name_plural = _(u'Codes de comptabilité/financier')
        ordering = ('entity', 'code')

    entity = models.ForeignKey(Entity)
    code = models.CharField(max_length=32, verbose_name=_('Code d\'imputation'), db_index=True)
