# -*- coding: utf-8 -*-

import datetime

from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models

from hdabo import utils
from hdabo.utils import Property, normalize


# User Class, due to migration to django 1.6.5
class User(AbstractUser):
    class Meta:
        db_table = 'auth_user'


class SortedModelManager(models.Manager):
    use_for_related_fields = True
    def get_query_set(self):
        qs = super(SortedModelManager, self).get_query_set()
        if getattr(self, 'through', None) is not None and getattr(self.through, 'Meta', None) is not None and getattr(self.through.Meta, 'ordering', None) is not None:
            qs = qs.order_by(*[self.through._meta.db_table + "." + f for f in self.through.Meta.ordering])
        return qs


class Organisation(models.Model):
    hda_id = models.CharField(max_length=512, unique=True, blank=False, null=False)
    name = models.CharField(max_length=512, unique=False, blank=False, null=False)
    location = models.CharField(max_length=512, unique=False, blank=True, null=True)
    website = models.CharField(max_length=2048, unique=False, blank=True, null=True)
    
class Author(models.Model):
    hda_id = models.CharField(max_length=512, unique=True, blank=False, null=False)
    lastname = models.CharField(max_length=512, unique=False, blank=True, null=True)
    firstname = models.CharField(max_length=512, unique=False, blank=True, null=True)

class TimePeriod(models.Model):
    TIME_PERIOD_CHOICES = (
        (1, u'Primaire'),
        (2, u'Collège'),
        (3, u'Lycée'),
    )
    TIME_PERIOD_DICT = {
        u'Primaire': 1,
        u'Collège': 2,
        u'Lycée': 3,
    }
    label = models.CharField(max_length=512, unique=False, blank=False, null=False)
    school_period = models.IntegerField(choices=TIME_PERIOD_CHOICES)
    natural_key = models.CharField(max_length=512, unique=True, blank=False, null=False)

    objects = SortedModelManager()
    
    class Meta:
        unique_together = ("label", "school_period")

    def __unicode__(self):
        return unicode(self.label)

class Domain(models.Model):
    DOMAIN_PERIOD_CHOICES = (
        (0, u'Global'),
        (1, u'Primaire'),
        (2, u'Collège'),
        (3, u'Lycée'),
    )
    DOMAIN_PERIOD_DICT = {
        u'Global': 0,
        u'Primaire': 1,
        u'Collège': 2,
        u'Lycée': 3,
    }
    label = models.CharField(max_length=512, unique=False, blank=False, null=False)
    school_period = models.IntegerField(choices=DOMAIN_PERIOD_CHOICES)
    natural_key = models.CharField(max_length=512, unique=True, blank=False, null=False)

    objects = SortedModelManager()

    class Meta:
        unique_together = ("label", "school_period")

    def __unicode__(self):
        return unicode(self.label)


class DocumentFormat(models.Model):
    label = models.CharField(max_length=512, unique=True, blank=False, null=False)

    def __unicode__(self):
        return unicode(self.label)
    
class TagCategory(models.Model):
    label = models.CharField(max_length=512, unique=True, blank=False, null=False)
    natural_key = models.CharField(max_length=512, unique=False, blank=False, null=False, db_index=True)
    
    def __unicode__(self):
        return unicode(self.label)
    
    class Meta:
        verbose_name_plural = "TagCategories"

class Tag(models.Model):
    TAG_URL_STATUS_CHOICES = (
        (0, "null_result"),
        (1, "redirection"),
        (2, "homonyme"),
        (3, "match"),
        (4, "unsematized"),
    )
    
    TAG_URL_STATUS_DICT = {
        "null_result":0,
        "redirection":1,
        "homonyme":2,
        "match":3,
        "unsemantized":4,
    }
    
    label = models.CharField(max_length=1024, unique=False, blank=False, null=False, db_index=True)
    alternative_label = models.CharField(max_length=1024, unique=False, blank=True, null=True)
    normalized_label = models.CharField(max_length=1024, unique=False, blank=False, null=False, db_index=True, editable=False)
    created_at = models.DateTimeField(auto_now_add=True)
    original_label = models.CharField(max_length=1024, unique=False, blank=False, null=False, editable=False)
    alias = models.CharField(max_length=1024, unique=False, blank=True, null=True)
    category = models.ForeignKey(TagCategory, null=True, blank=True)
    wikipedia_url = models.URLField(max_length=2048, blank=True, null=True, db_index=True)
    wikipedia_pageid = models.BigIntegerField(unique=False, blank=True, null=True, db_index=True)
    alternative_wikipedia_url = models.URLField(max_length=2048, blank=True, null=True, db_index=True)
    alternative_wikipedia_pageid = models.BigIntegerField(unique=False, blank=True, null=True, db_index=True)
    url_status = models.IntegerField(choices=TAG_URL_STATUS_CHOICES, blank=True, null=True, default=None, db_index=True)
    dbpedia_uri = models.URLField(max_length=2048, blank=True, null=True, db_index=True)
    popularity = models.IntegerField(blank=False, null=False, default=0, db_index=True)
    #natural_key = models.CharField(max_length=7168, blank=True, null=True, db_index=True)
    #TODO: find a proper key. natural key is not really a key.
    natural_key = models.CharField(max_length=7168, blank=False, null=False, db_index=True)
    

    def __init__(self, *args, **kwargs):
        models.Model.__init__(self, *args, **kwargs)
        self.force_natural_key = False

    @Property
    def url_status_text(): #@NoSelf
        def fget(self):
            return self.TAG_URL_STATUS_CHOICES[self.url_status][1]
        
        return locals()

    def calculate_natural_key(self):
        parts = [
            utils.sanitize(self.label if hasattr(self,'label') else ''),
            utils.sanitize(self.normalized_label if hasattr(self,'normalized_label') else ''),
            utils.sanitize(self.original_label if hasattr(self,'original_label') else ''),
            self.wikipedia_url.split("/")[-1].rstrip('/') if hasattr(self,'wikipedia_url') and self.wikipedia_url else ""
        ]
        return ('_'.join(parts))[:7168]

    
    def save(self, *args, **kwargs):
        if self.label and not self.normalized_label:
            self._normalized_label = normalize(self.label)
        if not self.force_natural_key:
            self.natural_key = self.calculate_natural_key()
        super(Tag, self).save(*args, **kwargs) 
            
    class Meta:
        unique_together = (('label', 'original_label', 'url_status'),)
        
class Location(models.Model):
    name = models.CharField(max_length=512, unique=False, blank=False, null=False)
    insee = models.CharField(max_length=5, unique=True, blank=False, null=False)

    def __unicode__(self):
        return unicode("%s : %s" % (self.name, self.insee))


def generate_m2m_setter(m2m_field_name):
    
    def set_m2m_field(self, lst):
        
        m2m_manager = getattr(self, m2m_field_name)
        m2m_manager.clear()
        
        through_klass = set_m2m_field.cache.get('through_klass', None)
        if through_klass is None:
            field = getattr(self.__class__, m2m_field_name)
            through_klass = field.through
            set_m2m_field.cache['through_klass'] = through_klass
            for f in through_klass._meta.fields:
                if isinstance(f, models.ForeignKey) and f.name != "datasheet":
                    set_m2m_field.cache['target_obj_field_name'] = f.name
                    break
        target_obj_field_name = set_m2m_field.cache['target_obj_field_name']

        for i, obj in enumerate(lst):
            kwargs = {
                'datasheet': self,
                'sort_value' : i,
                target_obj_field_name: obj
            }
            new_rel = through_klass(**kwargs)
            new_rel.save()
    set_m2m_field.cache = {}
            
    return set_m2m_field


class Datasheet(models.Model):
    hda_id = models.CharField(max_length=512, unique=True, blank=False, null=False)
    author = models.ForeignKey(Author, null=True, blank=True, serialize=False)
    organisation = models.ForeignKey(Organisation, serialize=False, null=True)
    title = models.CharField(max_length=2048, unique=False, blank=False, null=False, serialize=False)
    description = models.TextField(blank=True, null=True, serialize=False)
    url = models.URLField(max_length=2048, blank=True, null=True, serialize=False)
    domains = models.ManyToManyField(Domain, limit_choices_to={'school_period':Domain.DOMAIN_PERIOD_DICT[u'Global']}, related_name="datasheets", through="Datasheet_domains", serialize=False)
    primary_periods = models.ManyToManyField(TimePeriod, limit_choices_to={'school_period':TimePeriod.TIME_PERIOD_DICT[u'Primaire']}, related_name="primary_periods_datasheets", through="Datasheet_primary_periods", serialize=False)
    college_periods = models.ManyToManyField(TimePeriod, limit_choices_to={'school_period':TimePeriod.TIME_PERIOD_DICT[u'Collège']}, related_name="college_periods_datasheets", through="Datasheet_college_periods", serialize=False)
    highschool_periods = models.ManyToManyField(TimePeriod, limit_choices_to={'school_period':TimePeriod.TIME_PERIOD_DICT[u'Lycée']}, related_name="highschool_periods_datasheets", through="Datasheet_highschool_periods", serialize=False)
    primary_themes = models.ManyToManyField(Domain, limit_choices_to={'school_period':Domain.DOMAIN_PERIOD_DICT[u'Primaire']}, related_name="primary_themes_datasheets", through="Datasheet_primary_themes", serialize=False)
    college_themes = models.ManyToManyField(Domain, limit_choices_to={'school_period':Domain.DOMAIN_PERIOD_DICT[u'Collège']}, related_name="college_themes_datasheets", through="Datasheet_college_themes", serialize=False)
    highschool_themes = models.ManyToManyField(Domain, limit_choices_to={'school_period':Domain.DOMAIN_PERIOD_DICT[u'Lycée']}, related_name="highschool_themes_datasheets", through="Datasheet_highschool_themes", serialize=False)
    town = models.ForeignKey(Location, null=True, blank=True, serialize=False)
    format = models.ForeignKey(DocumentFormat, null=True, blank=True, serialize=False)
    original_creation_date = models.DateField(serialize=False)
    original_modification_date = models.DateField(serialize=False)
    modification_datetime = models.DateTimeField(auto_now=True, serialize=False)
    validation_date = models.DateTimeField(null=True, blank=True, serialize=False)
    validated = models.BooleanField(default=False, db_index=True)
    validator = models.ForeignKey(User, null=True, blank=True, serialize=False)
    manual_order = models.BooleanField(default=False, db_index=True, serialize=False)
    tags = models.ManyToManyField(Tag, through='TaggedSheet', serialize=False)
    
    def natural_key(self):
        return self.hda_id
        
    def validate(self, user):
        self.validation_date = datetime.datetime.now()
        self.validated = True
        self.validator = user
        self.save()
    
    def unvalidate(self):
        self.validation_date = datetime.datetime.min
        self.validated = False
        self.validator = None
        self.save()
        
        
    set_domains = generate_m2m_setter("domains")
    
    @Property
    def domains_list(): #@NoSelf
        def fget(self):
            return [d.label for d in self.domains.all()]
        
        return locals() 

    @Property
    def domains_text(): #@NoSelf
        def fget(self):
            return "; ".join(self.domains_list)
        
        return locals() 


    set_primary_periods = generate_m2m_setter("primary_periods")

    @Property
    def primary_periods_list(): #@NoSelf
        def fget(self):
            return [d.label for d in self.primary_periods.all()] 

        return locals() 

    
    @Property
    def primary_periods_text(): #@NoSelf
        def fget(self):
            return "; ".join(self.primary_periods_list) 

        return locals() 

    set_college_periods = generate_m2m_setter("college_periods")

    @Property
    def college_periods_list(): #@NoSelf
        def fget(self):
            return [d.label for d in self.college_periods.all()] 

        return locals() 

    @Property
    def college_periods_text(): #@NoSelf
        def fget(self):
            return "; ".join(self.college_periods_list) 

        return locals() 

    set_highschool_periods = generate_m2m_setter("highschool_periods")

    @Property
    def highschool_periods_list(): #@NoSelf
        def fget(self):
            return [d.label for d in self.highschool_periods.all()] 

        return locals() 

    @Property
    def highschool_periods_text(): #@NoSelf
        def fget(self):
            return "; ".join(self.highschool_periods_list) 

        return locals() 

    set_primary_themes = generate_m2m_setter("primary_themes")

    @Property
    def primary_themes_list(): #@NoSelf
        def fget(self):
            return [d.label for d in self.primary_themes.all()] 

        return locals() 


    @Property
    def primary_themes_text(): #@NoSelf
        def fget(self):
            return "; ".join(self.primary_themes_list) 

        return locals() 

    set_college_themes = generate_m2m_setter("college_themes")

    @Property
    def college_themes_list(): #@NoSelf
        def fget(self):
            return [d.label for d in self.college_themes.all()] 

        return locals() 
    
    @Property
    def college_themes_text(): #@NoSelf
        def fget(self):
            return "; ".join(self.college_themes_list) 

        return locals() 

    set_highschool_themes = generate_m2m_setter("highschool_themes")

    @Property
    def highschool_themes_list(): #@NoSelf
        def fget(self):
            return [d.label for d in self.highschool_themes.all()] 

        return locals()

    @Property
    def highschool_themes_text(): #@NoSelf
        def fget(self):
            return "; ".join(self.highschool_themes_list) 

        return locals()

    @Property
    def town_text(): #@NoSelf
        def fget(self):
            return self.town.name if self.town else ""
        
        return locals()

    @Property
    def tags_text(): #@NoSelf
        def fget(self):
            return "; ".join([t.label for t in self.tags.all()])
        
        return locals()

    @models.permalink
    def get_absolute_url(self):
        return ('display_datasheet', (), {
                    'ds_id': self.hda_id
                })


class TaggedSheet(models.Model):
    datasheet = models.ForeignKey(Datasheet)
    tag = models.ForeignKey(Tag)
    created_at = models.DateTimeField(auto_now_add=True)
    original_order = models.IntegerField(null=False, blank=False, default=0)
    order = models.IntegerField(null=False, blank=False, default=0, db_index=True)
    index_note = models.FloatField(null=False, blank=False, default=0.0, db_index=True)
    wikipedia_revision_id = models.BigIntegerField(unique=False, blank=True, null=True)
    
    @Property
    def wikipedia_verion_permalink(): #@NoSelf
        def fget(self):
            return settings.WIKIPEDIA_VERSION_PERMALINK_TEMPLATE % (unicode(self.wikipedia_revision_id))
        
        return locals()


class SortedDatasheetLink(models.Model):    
    datasheet = models.ForeignKey(Datasheet, db_index=True, null=False, blank=False)
    sort_value = models.IntegerField(null=False, blank=False)

    class Meta:
        abstract = True
        ordering = ['sort_value']
        

class Datasheet_domains(SortedDatasheetLink):
    domain = models.ForeignKey(Domain, db_index=True, null=False, blank=False)

class Datasheet_highschool_periods(SortedDatasheetLink):
    timeperiod = models.ForeignKey(TimePeriod, db_index=True, null=False, blank=False)

class Datasheet_highschool_themes(SortedDatasheetLink):
    domain = models.ForeignKey(Domain, db_index=True, null=False, blank=False)

class Datasheet_college_periods(SortedDatasheetLink):
    timeperiod = models.ForeignKey(TimePeriod, db_index=True, null=False, blank=False)

class Datasheet_college_themes(SortedDatasheetLink):
    domain = models.ForeignKey(Domain, db_index=True, null=False, blank=False)

class Datasheet_primary_periods(SortedDatasheetLink):
    timeperiod = models.ForeignKey(TimePeriod, db_index=True, null=False, blank=False)

class Datasheet_primary_themes(SortedDatasheetLink):
    domain = models.ForeignKey(Domain, db_index=True, null=False, blank=False)


# Evolution pour Hda 2 : folders of datasheets
class Folder(models.Model):
    url = models.URLField(max_length=2048, unique=True, blank=False, null=False)
    title = models.CharField(max_length=2048, blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    datasheets = models.ManyToManyField(Datasheet)



