# -*- coding: utf-8 -*-
import datetime
import django
from django.db import utils, IntegrityError
from django.db import models
from django.db.models import *
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser, PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager

from django.core.exceptions import ValidationError

from django.utils.translation import ugettext_lazy as _

from django.conf import settings as appSettings

from keycloak_oidc import exceptions as sso_exc

from records_audit.utils import DataAudited


class AppUser(AbstractUser, DataAudited):
    """
    Prepare an empty User class just in case it will be needed later.
    """
    ### Jiz definovano v predkovi
    #first_name = CharField(_('firstName'),max_length=100, default='')
    #last_name = CharField(_('lastName'),max_length=100, default='')
    #email = CharField(_('email'),max_length=100, default='', blank=True, null=True)

    # Keycloak user ID
    ssoUid = CharField(_(u'Keycloak SSO user ID'), max_length=40, default=None,
                            blank=True, null=True)
    createdStamp = DateTimeField(_('Uživatel vytvořen'),
        default=datetime.datetime.now, blank=False, null=False, editable=False)
        # Datum a čas poslední změny údajů, které se mají propisovat do LDAP.
        #   Podle tohoto údaje se v synchronizačním skriptu pozná, jaké
        #   záznamy je potřeba aktualizovat v LDAP.

    # Email login token
    emailToken = CharField(_(u'E-mail login token'), max_length=120, default=None,
                            blank=True, null=True)
    etStamp = DateTimeField(_('E-mail token timestamp'), editable=False,
                            default=None, blank=True, null=True)

    ##
    #   Constants
    ##
    DISTRICT_PHA = 0
    DISTRICT_JHC = 1
    DISTRICT_JHM = 2 
    DISTRICT_KVK = 3
    DISTRICT_VYS = 4
    DISTRICT_KHK = 5
    DISTRICT_LBK = 6
    DISTRICT_MSK = 7
    DISTRICT_OLK = 8
    DISTRICT_PAK = 9
    DISTRICT_PLK = 10
    DISTRICT_STC = 11
    DISTRICT_ULK = 12
    DISTRICT_ZLK = 13

    DISTRICT_CHOICES = (
        (DISTRICT_PHA, 'Hlavní město Praha'),
        (DISTRICT_JHC, 'Jihočeský kraj'),
        (DISTRICT_JHM, 'Jihomoravský kraj'),
        (DISTRICT_KVK, 'Karlovarský kraj'),
        (DISTRICT_VYS, 'Kraj Vysočina'),
        (DISTRICT_KHK, 'Královéhradecký kraj'),
        (DISTRICT_LBK, 'Liberecký kraj'),
        (DISTRICT_MSK, 'Moravskoslezský kraj'),
        (DISTRICT_OLK, 'Olomoucký kraj'),
        (DISTRICT_PAK, 'Pardubický kraj'),
        (DISTRICT_PLK, 'Plzeňský kraj'),
        (DISTRICT_STC, 'Středočeský kraj'),
        (DISTRICT_ULK, 'Ústecký kraj'),
        (DISTRICT_ZLK, 'Zlínský kraj'),
    )
    DISTRICT_CHOICES_STR = {
        DISTRICT_PHA: 'Hlavní město Praha',
        DISTRICT_JHC: 'Jihočeský kraj',
        DISTRICT_JHM: 'Jihomoravský kraj',
        DISTRICT_KVK: 'Karlovarský kraj',
        DISTRICT_VYS: 'Kraj Vysočina',
        DISTRICT_KHK: 'Královéhradecký kraj',
        DISTRICT_LBK: 'Liberecký kraj',
        DISTRICT_MSK: 'Moravskoslezský kraj',
        DISTRICT_OLK: 'Olomoucký kraj',
        DISTRICT_PAK: 'Pardubický kraj',
        DISTRICT_PLK: 'Plzeňský kraj',
        DISTRICT_STC: 'Středočeský kraj',
        DISTRICT_ULK: 'Ústecký kraj',
        DISTRICT_ZLK: 'Zlínský kraj',
    }
    DISTRICT_ROLES = {
        'sso_kraj_pha' : DISTRICT_PHA,
        'sso_kraj_jhc' : DISTRICT_JHC,
        'sso_kraj_jhm' : DISTRICT_JHM,
        'sso_kraj_kvk' : DISTRICT_KVK,
        'sso_kraj_vys' : DISTRICT_VYS,
        'sso_kraj_khk' : DISTRICT_KHK,
        'sso_kraj_lbk' : DISTRICT_LBK,
        'sso_kraj_msk' : DISTRICT_MSK,
        'sso_kraj_olk' : DISTRICT_OLK,
        'sso_kraj_pak' : DISTRICT_PAK,
        'sso_kraj_plk' : DISTRICT_PLK,
        'sso_kraj_stc' : DISTRICT_STC,
        'sso_kraj_ulk' : DISTRICT_ULK,
        'sso_kraj_zlk' : DISTRICT_ZLK,
    }

    STATUS_NEW = 0
    STATUS_REG = 1
    STATUS_CHOICES = (
        (STATUS_NEW, 'nový'),
        (STATUS_REG, 'registrovaný'),
    )
    STATUS_CHOICES_STR = {
        STATUS_NEW: 'nový',
        STATUS_REG: 'registrovaný',
    }

    KIND_NEWSLETTER = 0
    KIND_WANT_SUPPORTER = 1
    KIND_WANT_MEMBER = 2
    KIND_HELP_VOLUNTEER = 3
    KIND_HELP_EXPERT = 4
    KIND_ALREADY_MEMBER = 5
    KIND_ALREADY_OTHER = 6
    KIND_CHOICES = (
        (KIND_NEWSLETTER, _('dostávat novinky')),
        (KIND_HELP_VOLUNTEER, _('pomáhat jako dobrovolník')),
        (KIND_HELP_EXPERT, _('pomáhat jako expert')),
        (KIND_WANT_SUPPORTER, _('se stát příznivcem')),
        (KIND_WANT_MEMBER, _('se stát členem')),
        (KIND_ALREADY_MEMBER, _('již jsem člen')),
        (KIND_ALREADY_OTHER, _('již pirátím jinak')),
    )
    KIND_CHOICES_STR = {
        KIND_NEWSLETTER: _('dostávat novinky'),
        KIND_HELP_VOLUNTEER: _('pomáhat jako dobrovolník'),
        KIND_HELP_EXPERT: _('pomáhat jako expert'),
        KIND_WANT_SUPPORTER: _('se stát příznivcem'),
        KIND_WANT_MEMBER: _('se stát členem'),
        KIND_ALREADY_MEMBER: _('již jsem člen'),
        KIND_ALREADY_OTHER: _('již pirátím jinak'),
    }

    ##
    #   User self-editable fields
    ##
    postcode = IntegerField(_('PSČ'), blank=False, null=True)
    city = CharField(_(u'Město'), max_length=120, default=None,
                            blank=True, null=True)
    district = IntegerField(_('Kraj'), blank=True, null=True,
        choices=DISTRICT_CHOICES, default=None)

    kind = IntegerField(_('Chci'), blank=False, null=False,
        default = KIND_NEWSLETTER, choices=KIND_CHOICES)
        # Stav KIND_MEMBER se nastavuje dle skutečného členství, a bude
        # pravidelně aktualizován synchronizací s DB členů.

    interestedIn = CharField(_('Moje dovednosti (chci pomoci s)'), blank=True, null=True,
            max_length=150)
        # Stav KIND_MEMBER se nastavuje dle skutečného členství, a bude
        # pravidelně aktualizován synchronizací s DB členů.

    email_contact = EmailField(_('Kontaktní e-mail'),max_length=100,
        default='', blank=True, null=True)
        # Kontaktni email, ktery slouzi ke skryti emailu registrovaneho na SSO
        # a pouziteho napr. v prihlasce o clenstvi.
        # SSO bude dalsim aplikacim, krome nalodeni, predavat pouze kontaktni
        #   email, pokud bude nastaven.

    email_contact_token = CharField(_('Ověřovací token pro kontaktní e-mail.'),
        max_length=150, default=None, blank=True, null=True)
        # Token pro ověření kontaktního emailu. Má tvat timestamp - token,
        #   kde timestamp se neposílá emailem, ale slouží pro vypršení platnosti.

    email_contact_verified = BooleanField(_('Kontaktní e-mail byl ověřen.'),
        default=False, blank=True)
        # Kontaktni email je potřeba ověřit zaslání ověřovacího emailu.

    email_contact_active = EmailField(_('Kontaktní e-mail aktivní'),max_length=100,
        default='', blank=True, null=True)
        # Kontaktni email, který je ověřen a zapsán v LDAP.
        #   pole je vyplňováno při přihlášení z SSO.

    ts_for_ldap_sync = DateTimeField(_('Timestamp pro LDAP synchronizaci'),
        default=datetime.datetime.now, blank=True, null=True)
        # Datum a čas poslední změny údajů, které se mají propisovat do LDAP.
        #   Podle tohoto údaje se v synchronizačním skriptu pozná, jaké
        #   záznamy je potřeba aktualizovat v LDAP.

    dc_stamp = DateTimeField(_('Data consent timestamp'),
                            default=None, blank=True, null=True)


    # dotaznik pro uzivatele
    userform = ForeignKey('UserForm', on_delete=CASCADE, verbose_name=_('dotazník'),
            blank=True, null=True, default=None)

    ##
    #   Other fields
    ##
    status = IntegerField(_('Stav'), blank=False, null=False,
        default = STATUS_NEW, choices=STATUS_CHOICES)


    ##
    # pro kompatibilitu kodu a modelu
    @property
    def firstName(self):
        return self.first_name

    @firstName.setter
    def firstName(self,value):
        self.first_name = value

    @property
    def lastName(self):
        return self.last_name

    @lastName.setter
    def lastName(self, value):
        self.last_name = value
    #
    ##

    ##
    #   Ukladani a nacitani nastaveni

    # consent_gdpr
    @property
    def consent_gdpr(self):
        return readUserSetting(self, 'consent_gdpr', None, datetime.date)
    @consent_gdpr.setter
    def consent_gdpr(self, value):
        writeUserSetting(self, 'consent_gdpr', value, datetime.date)
    

    # consent_cookies
    @property
    def consent_cookies(self):
        return readUserSetting(self, 'consent_cookies', None, datetime.date)
    @consent_cookies.setter
    def consent_cookies(self, value):
        writeUserSetting(self, 'consent_cookies', value, datetime.date)
    

    # consent_terms
    @property
    def consent_terms(self):
        return readUserSetting(self, 'consent_terms', None, datetime.date)
    @consent_terms.setter
    def consent_terms(self, value):
        writeUserSetting(self, 'consent_terms', value, datetime.date)

    #   Konec
    ##


    ##
    #   Meta information
    ##
    def __str__(self):
        return self.username

        if self.ssoUid:
            return "%s %s" % (self.first_name, self.last_name)
        else:
            return self.email

    _audit_fields = ('postcode', 'district', 'kind', 'password')
    _audit_fields_exclude = ('emailToken',)
    _audit_fields_private = ('password',)
    class Meta:
        verbose_name = _('AppUser')
        verbose_name_plural = _('AppUsers')
        unique_together = (("email", ),("username",), ("emailToken",),)
        ordering = ('username',)

    
    ##
    #   Permission functions
    ##
    def do_site_perms_calc(self, site_perms):
        '''Precalculate needed permissions:
                - district IDs for the given roles
        '''
        spc = {}

        rslt = []
        for p in self.DISTRICT_ROLES:
            if p in site_perms:
                # append the ID of the choice
                rslt.append(self.DISTRICT_ROLES[p])

        spc['dist'] = rslt

        return spc

            


USER_MODEL = AppUser
#USER_MODEL = get_user_model()

class InterestRegion(Model):
    """
    Dostupné emailové zdroje informací.
    """
    name = CharField(_('Název'), blank=True, null=True,
            max_length=100)
    tag = CharField(_('Značka (tag) newsletteru'), blank=True, null=True,
            max_length=20)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name',]
        unique_together = ( ('tag',),)

class UserSkill(Model):
    """
    Dovednosti, které může mít daný uživatel.
    """
    name = CharField(_('Název'), blank=True, null=True,
            max_length=100)
    tag = CharField(_('Značka (tag) dovednosti'), blank=True, null=True,
            max_length=20)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['tag',]
        unique_together = ( ('tag',),)

class UserTopic(Model):
    """
    Zájmová témata daného uživatele.
    """
    name = CharField(_('Název'), blank=True, null=True,
            max_length=100)
    tag = CharField(_('Značka (tag) tématu'), blank=True, null=True,
            max_length=20)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name',]
        unique_together = ( ('tag',),)




class UserForm(Model, DataAudited):
    """
    Dotaznik ohledne dovednosti a schopnosti uzivatele.
    """
    skills = ManyToManyField(UserSkill, blank=True, verbose_name=_('Dovednosti'))
    skills_note = CharField(_('Poznámka k dovednostem'),max_length=250,
        default=None, blank=True, null=True)

    topics = ManyToManyField(UserTopic, blank=True, verbose_name=_('Zájmová témata'))
    regions = ManyToManyField(InterestRegion, blank=True, verbose_name=_('Zájmové regiony'))


class AppRegEmail(Model):
    """
    Pozadavky na registraci emailu.
    """
    createdStamp = DateTimeField(_('Uživatel vytvořen'),
        default=datetime.datetime.now, blank=False, null=False, editable=False)

    email = CharField(_('e-mail'),max_length=100, default='', blank=True, null=True)
    postcode = IntegerField(_('PSČ (kvůli dělení do krajů)'), blank=False, null=True)

    kind = IntegerField(_('Chci'), blank=True, null=True,
        default = AppUser.KIND_NEWSLETTER, choices=AppUser.KIND_CHOICES)
        # Stav KIND_MEMBER se nastavuje dle skutečného členství, a bude
        # pravidelně aktualizován synchronizací s DB členů.

    interestedIn = CharField(_('Poznámka (info pro koordinátora)'), blank=True, null=True,
            max_length=100)

    data_consent = BooleanField(_('Souhlasím se zpracováním osobních údajů.'),
        default=False, blank=False, null=False)
        # Souhlas se zpracováním os. údajů
    dc_stamp = DateTimeField(_('Data consent timestamp'), editable=False,
                            default=None, blank=True, null=True)


    # registration token
    emailToken = CharField(_(u'Registration token'), max_length=120, default=None,
                            blank=True, null=True)
    etStamp = DateTimeField(_('Registration token timestamp'), editable=False,
                            default=None, blank=True, null=True)

    userform = ForeignKey(UserForm, on_delete=CASCADE, verbose_name=_('dotazník'),
            blank=True, null=True, default=None)

    class Meta:
        verbose_name = _('AppRegEmail')
        verbose_name_plural = _('AppRegEmails')
        unique_together = (("emailToken",),("email",))


class Euro2019Interest(Model):
    """
    Zajemci o pomoc ve volbach 2019.
    """
    createdStamp = DateTimeField(_('Uživatel vytvořen'),
        default=datetime.datetime.now, blank=False, null=False, editable=False)

    email = CharField(_('e-mail'),max_length=100, blank=False, null=True)
    district = IntegerField(_('Kraj'), blank=True, null=True,
        choices=AppUser.DISTRICT_CHOICES, default=None)

    postcode = IntegerField(_('PSČ (kvůli dělení do krajů)'), blank=False, null=True)

    want_info = BooleanField(_('Chci dostávat informace e-mailem'),
        default=True, blank=False, null=False)

    want_campaign = BooleanField(_('Chci rozdávat Pirátské listy'),
        default=False, blank=False, null=False)

    want_be_active = BooleanField(_('Chci se podílet chodu kampaně'),
        default=False, blank=False, null=False)

    note = CharField(_('Poznámka (info pro koordinátora)'), blank=True, null=True,
            max_length=100)

    data_consent = BooleanField(_('Souhlasím se zpracováním osobních údajů'),
        default=False, blank=False, null=False)
        # Souhlas se zpracováním os. údajů
    dc_stamp = DateTimeField(_('Data consent timestamp'), editable=False,
                            default=None, blank=True, null=True)


    class Meta:
        unique_together = (("email",))



def get_user_by_keycloak_email(id):
    """
      Funkce pro dohledani uzivatele podle dat ze SSO serveru.
      "aud" a "azp" jsou client-id dle SSO serveru.

      Struktura id :
          {
              'jti': '0d5be61a-fccc-49ce-b82d-78fe53eec7ac'
              'exp': 1505657690
              'nbf': 0
              'iat': 1505657390
              'iss': 'http://localhost:8080/auth/realms/nga'
              'aud': 'nalodeni'
              'sub': 'db831083-7e1e-42ee-9682-2ff85021fcbc'
              'typ': 'ID'
              'azp': 'nalodeni'
              'auth_time': 1505657362
              'session_state': '73b44047-b86b-429b-b21f-800b2bc0d33f'
              'acr': '0'
              'name': 'Martin Rejman'
              'preferred_username': 'mr'
              'given_name': 'Martin'
              'family_name': 'Rejman'
              'email': 'martin.rejman@centrum.cz'
          }
    """
    # check presence of required attributes
    if not 'sub' in id:
        raise sso_exc.MissingSsoInfo(_('SSO server nepředal identifikaci uživatele.'))
    if not 'preferred_username' in id:
        raise sso_exc.MissingSsoInfo(_('SSO server nepředal uživatelské jméno.'))
    if not 'email' in id:
        raise sso_exc.MissingSsoInfo(_('SSO server nepředal email uživatele.'))
    if not 'given_name' in id:
        raise sso_exc.MissingSsoInfo(_('SSO server nepředal křestní jméno uživatele.'))
    if not 'family_name' in id:
        raise sso_exc.MissingSsoInfo(_('SSO server nepředal příjmení uživatele.'))

    id_email = id['email'].lower().strip()
    if 'email_contact' in id:
        id_email_contact_active = id['email_contact'].lower().strip()
    else:
        id_email_contact_active = None

    # get user by SSO id
    sso_users = USER_MODEL.objects.filter(ssoUid=id['sub'])

    if len(sso_users) == 0:
        # user unknown in Django
        email_users = USER_MODEL.objects.filter(email=id_email)

        if len(email_users) == 0:
            if len(id['preferred_username']) > 30: # Django User username is 30 character limited
                raise sso_exc.MissingSsoInfo(_('Uživatelské jméno je příliš dlouhé.'))

            usernames_found = USER_MODEL.objects.filter(username=id['preferred_username'])
            if len(usernames_found) > 0:
                raise sso_exc.UsernameAlreadyTaken( _('Uživatelské jméno %s je '
                        + 'již obsazeno.') % id['preferred_username'])
                
            user = USER_MODEL()
            user.ssoUid = id['sub']
            user.email = id_email
            user.email_contact_active = id_email_contact_active
            user.username = id['preferred_username']
            user.first_name = id['given_name']
            user.last_name = id['family_name']
            user.save()

        elif len(email_users) == 1:
            user = email_users[0]

            if user.ssoUid == None:
                user.ssoUid = id['sub']
            else:
                # email souhlasí, ale ssoUid nikoliv
                raise sso_exc.EmailVersusIdentityMismatch(
                        _('Účet nalodění s tímto emailem (%s, %s) ' +
                      'je již napárován na jinou identitu (%s) na Pirátské identitě.')
                      % ( user.email, user.ssoUid, id['sub'] )
                    )

            #check duplicity of the new username
            if len(id['preferred_username']) > 30: # Django User username is 30 character limited
                raise sso_exc.MissingSsoInfo(_('Uživatelské jméno je příliš dlouhé.'))
            rslt = USER_MODEL.objects.filter(username=id['preferred_username'])
            if len(rslt) > 0:
                raise sso_exc.UsernameAlreadyTaken( _('Uživatelské jméno %s je '
                        + 'již obsazeno.') % id['preferred_username'])

            user.username = id['preferred_username']
            user.first_name = id['given_name']
            user.last_name = id['family_name']
            user.email_contact_active = id_email_contact_active

            # reset email login attempts
            user.emailToken = None
            user.etStamp = None

            user.save()

        elif len(email_users) > 1:
            raise Exception("More users with one email value.")

    elif len(sso_users) == 1:
        # user known
        user = sso_users[0]

            

        changed = False
        if user.username != id['preferred_username']:
            #check duplicity of the new values (username, email)
            if len(id['preferred_username']) > 30: # Django User username is 30 character limited
                raise sso_exc.MissingSsoInfo(_('Uživatelské jméno je příliš dlouhé.'))
            rslt = USER_MODEL.objects.filter(username=id['preferred_username'])
            if len(rslt) > 0:
                raise sso_exc.UsernameAlreadyTaken( _('Uživatelské jméno %s je '
                        + 'již obsazeno.') % id['preferred_username'])
            user.username = id['preferred_username']
            changed = True
        if user.first_name != id['given_name']:
            user.first_name = id['given_name']
            changed = True
        if user.last_name != id['family_name']:
            user.last_name = id['family_name']
            changed = True
        if user.email != id_email:
            # check duplicity
            rslt = USER_MODEL.objects.filter(email=id_email)
            if len(rslt) > 0:
                raise sso_exc.EmailAlreadyTaken( _('Email %s je '
                        + 'již obsazen jiným účtem.') % id_email)
            user.email = id_email
            changed = True
        if user.email_contact_active != id_email_contact_active:
            user.email_contact_active = id_email_contact_active
            changed = True
        if changed:
            user.save()

    else:
        raise Exception("More users with one sso-uid value.")

    return user


class Newsletter(Model):
    """
    Záznam o newsletteru, který se chce zasílat.
    """
    is_del = BooleanField(_('Smazaný'),
        default=False, blank=False, null=False, editable=False)

    name = CharField(_('Název'), blank=True, null=True,
            max_length=150)

    desc = CharField(_('Popis'), blank=True, null=True,
            max_length=500)
    PER_WEEK = 0
    PER_MONTH = 1
    PER_IRR = 2
    PER_CHOICES = (
        (PER_WEEK, _('Týdně')),
        (PER_MONTH, _('Měsíčně')),
        (PER_IRR, _('Nepravidelně')),
    )
    period = IntegerField(_('Perioda zasílání'),
        blank=False, null=False, default=PER_MONTH, choices=PER_CHOICES)


    # Kdo může newsletter nastavovat
    managed_by = ForeignKey(USER_MODEL, models.CASCADE, verbose_name=_('Správce newsletteru'),
        related_name="newsletter_managed")

    # Kdo může pomocí tohoto newsletteru odesílat zprávy
    sent_by = ManyToManyField(USER_MODEL, blank=True, verbose_name=_('Odesílatelé'),
        related_name="newsletter_sent")

    enabled = BooleanField(_('Aktivní'),
        default=False, blank=False, null=False)

    recipients = CharField(_('Příjemci (pomocí tagů)'), blank=True, null=True,
            max_length=500)

    replyToEmail = CharField(_('E-mailová adresa pro odpovědi'),
        blank=True, null=True, max_length=150)


    class Meta:
        ordering = ("-enabled", "name",)

    def __str__(self):
        return self.name

    def get_recip_users(self):
        """
        Parse self.recipients and return a list of matching users.
        """
        all_users = USER_MODEL.objects.none()

        if self.recipients is None:
            return all_users

        for group in self.recipients.strip().replace('\n',' ').split('*'):
            group = group.strip()

            if group == "":
                continue

            topic_ids = []
            region_ids = []
            skill_ids = []

            #print('* ')
            for item in group.split(" "):
                item = item.strip()

                if item == "":
                    continue

                #print(item)
                if item[0] == "s":
                    rslt = UserSkill.objects.filter(tag=item[2:])
                    if len(rslt) == 1:
                        skill_ids.append(rslt[0].id)
                elif item[0] == "r":
                    rslt = InterestRegion.objects.filter(tag=item[2:])
                    if len(rslt) == 1:
                        region_ids.append(rslt[0].id)
                elif item[0] == "t":
                    rslt = UserTopic.objects.filter(tag=item[2:])
                    if len(rslt) == 1:
                        topic_ids.append(rslt[0].id)

            #print(topic_ids, region_ids, skill_ids)
            rslt_u = AppUser.objects.all()
            if len(topic_ids) > 0:
                rslt_u = rslt_u.filter(userform__topics__in = topic_ids)
            if len(skill_ids) > 0:
                rslt_u = rslt_u.filter(userform__skills__in = skill_ids)
            if len(region_ids) > 0:
                rslt_u = rslt_u.filter(userform__regions__in = region_ids)

            #print(rslt_u)
            all_users = all_users | rslt_u

        # use distinct because ORM _will_ multiply the results
        return all_users.distinct()

    def get_recip_users_count(self):
        return len(self.get_recip_users())



class NewsCond(Model):
    """
    Podmínky pro filtrování uživatelů, kterým se má newsletter odesílat.

    Podmínky v rámci skupiny se spojují operátorem AND, skupiny jako celek
    jsou pak spojeny operátorem OR.
    """

    news = ForeignKey(Newsletter, models.CASCADE, verbose_name=_('Newsletter'))

    group = IntegerField(_('Skupina podmínek'),
        default=False, blank=False, null=False)
    neg = BooleanField(_('Negovat podmínku'),
        default=False, blank=False, null=False)

    KIND_SKILL = 0
    KIND_TOPIC = 1
    KIND_REGION = 2
    KIND_CHOICES = (
        (KIND_SKILL, _('Dovednost')),
        (KIND_TOPIC, _('Téma')),
        (KIND_REGION, _('Území')),
    )
    kind = IntegerField(_('Druh'),
        blank=False, null=False, default=KIND_SKILL, choices=KIND_CHOICES)
    opt_id = IntegerField(_('ID vybrané možnosti'),
        blank=False, null=False)


class NewsMsg(Model):
    """
    Jedna konkrétní zpráva k rozeslání, skládá se z bloků, které tvoří její obsah.
    Uživatelé mohou reagovat na NewsMsg pomocí rozhraní nad NewsMsgReply.
    """
    is_del = BooleanField(_('Smazaný'),
        default=False, blank=False, null=False, editable=False)

    news = ForeignKey(Newsletter, models.CASCADE, verbose_name=_('Newsletter'))

    created_by = ForeignKey(USER_MODEL, models.CASCADE, verbose_name=_('Vytvořil'))

    created_ts = DateTimeField(_('Datum vytvoření'),
        default=datetime.datetime.now, blank=False, null=False, editable=False)
    delivery_ts = DateTimeField(_('Datum plánovaného rozeslání'),
        blank=True, null=True, editable=True)
    sent_ts = DateTimeField(_('Datum skutečného rozeslání'),
        blank=True, null=True, editable=True)

    title = CharField(_('Název, předmět emailu'), blank=False, null=True,
            max_length=150)

    headerText = TextField(_('Hlavička'), blank=True, null=True)
    footerText = TextField(_('Patička'), blank=True, null=True)

    testMailRecipients = CharField(_('Přidaní adresáti testovacího emailu'),
        blank=True, null=True, max_length=150)


    def __str__(self):
        return self.title


class NewsMsgBlock(Model):
    """
    Jeden blok newsletteru, ze kterých se skládá celá zpráva.
    """

    newsmsg = ForeignKey(NewsMsg, models.CASCADE, verbose_name=_('Zpráva'))
    order = IntegerField(_('Pořadí'),
        default=False, blank=False, null=False)

    heading = CharField(_('Nadpis'), max_length=150,
        blank=True, null=True)
    content = TextField(_('Text (i validované HTML)'),
        blank=True, null=True)

    link_text = CharField(_('Text odkazu'), max_length=100,
        blank=True, null=True)
    link = CharField(_('Odkaz'), max_length=500,
        blank=True, null=True)

    # časem je asi možné obrázky nahrávat přímo do nalodění
    img_thumb_url = CharField(_('Obrázek náhledu (URL)'), max_length=500,
        blank=True, null=True)
    img_url = CharField(_('Obrázek (URL)'), max_length=500,
        blank=True, null=True)
    img_label = CharField(_('Popis obrázku'), max_length=100,
        blank=True, null=True)

    class Meta:
        ordering = ['order', 'heading']




class NewsMsgReply(Model):
    """
    Reakce ctenaru na dany informacni blok.
    """

    block = ForeignKey(NewsMsgBlock, models.CASCADE, verbose_name=_('Část zprávy'))
    user = ForeignKey(USER_MODEL, models.CASCADE, verbose_name=_('Vytvořil'))

    comment = CharField(_('Poznámka'), max_length=250,
        blank=True, null=True)

    rating_usefullness = IntegerField(_('Využiji to'),
        default=0, blank=True, null=True)

    rating_interest = IntegerField(_('Zajímá mě to'),
        default=0, blank=True, null=True)

    rating_action = IntegerField(_('Zapojím se / Pomůžu'),
        default=0, blank=True, null=True)





## .......+++++++......... ##
#    User settings          #
#                           #
class UserSetting(Model):
    """
    Different settings and values stored for global purpose.
    """

    user = ForeignKey(USER_MODEL, models.CASCADE, verbose_name=_('user'))
    key = CharField(_("Key"), max_length=64)
    value = CharField(_("Value"), max_length=128, null=True)

    def __str__(self):
        return str(self.key)

    class Meta:
        unique_together = (( 'user','key', ),)


def readUserSetting(user, key, default, dataType):
    """
        Reads user settings for the current SITE_ID.

        Returns 'default' value if the setting is not present
        or the value is None.
    """
    obj = UserSetting.objects.filter(key=key,user=user)
    if len(obj) == 0:
        return default
    else:
        val = obj[0].value

        if val is None:
            return default

        if dataType == datetime.date:
            return datetime.datetime.strptime(val,'%Y-%m-%d').date()
        elif dataType == bool:
            return val == 'T'
        else:
            return dataType(val)

def writeUserSetting(user, key, value, dataType):
    """
        Saves a user setting for the current SITE_ID.
    """
    obj, created = UserSetting.objects.get_or_create(key=key,user=user)

    # modify 
    if value is not None:
        if dataType == bool:
            value = 'T' if value else 'F'
        elif dataType == datetime.date:
            value = value.strftime('%Y-%m-%d')

    # save modified value, or the original one,
    #   or None if value is None
    obj.value = value
    obj.save()


#                           #
#    End user settings      #
## .......+++++++......... ##


## .......+++++++......... ##
#    GLOBAL APP SETTINGS    #
#                           #
class AppSetting(Model):
    """
    Different settings and values stored for global purpose.
    """

    key = CharField(_("Key"), max_length=64)
    # not null ... because we agree "" is null
    value = CharField(_("Value"), max_length=128)

    def __str__(self):
        return self.key

    class Meta:
        unique_together = (( 'key', ),)


def readAppSetting(key, default=None):
    try:
        obj = AppSetting.objects.get(key=key)
        if obj.value == "":
            return default
        else:
            return obj.value
    except:
        return default

def writeAppSetting(key, value):
    obj, created = AppSetting.objects.get_or_create(key=key)

    if created:
        pass
        # means you have created a new person
    else:
        pass
        # person just refers to the existing one
    if value is not None:
        obj.value = str(value)
    else:
        obj.value = ''
    obj.save()


#                           #
#    END GLOBAL SETTINGS    #
## .......+++++++......... ##


