from django.db import models

from syntax.models import Position

# === Atomic phrase attributes =================================================

# TODO a lot od repeated code, but can’t use simple inheritance:
# name values repeat across attributes, e.g. prep ‘jako’ / compar cat. ‘jako’

# --- Priority-sorted ----------------------------------------------------------

class Case(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

class PhraseAspect(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

class PhraseNegativity(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

class PhraseInherentSie(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

class AdverbialCategory(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

# lex

class Number(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

class Gender(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

class Degree(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

# --- Lexicographically sorted -------------------------------------------------

class ComplexPreposition(models.Model):
    name = models.CharField(max_length=16, unique=True)
    class Meta:
        ordering = ['name']
    def __str__(self):
        return self.name

class Preposition(models.Model):
    name = models.CharField(max_length=16, unique=True)
    class Meta:
        ordering = ['name']
    def __str__(self):
        return self.name

class CPType(models.Model):
    name = models.CharField(max_length=16, unique=True)
    class Meta:
        ordering = ['name']
    def __str__(self):
        return self.name

class CPRealisation(models.Model):
    name = models.CharField(max_length=16, unique=True)
    class Meta:
        ordering = ['name']
    def __str__(self):
        return self.name

class ComparCategory(models.Model):
    name = models.CharField(max_length=16, unique=True)
    class Meta:
        ordering = ['name']
    def __str__(self):
        return self.name

# === Phrase attribute objects =================================================

class PhraseTypeAttributes(models.Model):
    pass

class LexPhraseTypeAttributes(PhraseTypeAttributes):
    pass

# TODO a better solution for possp, E, qub etc.?
class EmptyAttributes(PhraseTypeAttributes):
    empty = models.TextField(unique=True, null=False)
    
    def __str__(self):
        return '()'

#-------------------------------------------------------------------------------

class NPAttributes(PhraseTypeAttributes):
    case = models.OneToOneField(Case, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.case)

class LexNPAttributes(LexPhraseTypeAttributes):
    num = models.OneToOneField(Number, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.num)

#-------------------------------------------------------------------------------

class NumPAttributes(PhraseTypeAttributes):
    case = models.OneToOneField(Case, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.case)

#-------------------------------------------------------------------------------

class AdjPAttributes(PhraseTypeAttributes):
    case = models.OneToOneField(Case, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.case)

class LexAdjPAttributes(LexPhraseTypeAttributes):
    num = models.ForeignKey(Number, on_delete=models.PROTECT)
    gend = models.ForeignKey(Gender, on_delete=models.PROTECT)
    deg = models.ForeignKey(Degree, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['num', 'gend', 'deg']
    
    def __str__(self):
        return '({},{},{})'.format(self.num, self.gend, self.deg)

#-------------------------------------------------------------------------------

class PPasPAttributes(PhraseTypeAttributes):
    case = models.OneToOneField(Case, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.case)

class LexPPasPAttributes(LexPhraseTypeAttributes):
    num = models.ForeignKey(Number, on_delete=models.PROTECT)
    gend = models.ForeignKey(Gender, on_delete=models.PROTECT)
    neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['num', 'gend', 'neg']
    
    def __str__(self):
        return '({},{},{})'.format(self.num, self.gend, self.neg)

#-------------------------------------------------------------------------------

class PActPAttributes(PhraseTypeAttributes):
    case = models.OneToOneField(Case, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.case)

class LexPActPAttributes(LexPhraseTypeAttributes):
    num = models.ForeignKey(Number, on_delete=models.PROTECT)
    gend = models.ForeignKey(Gender, on_delete=models.PROTECT)
    neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT)
    inhsie = models.ForeignKey(PhraseInherentSie, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['num', 'gend', 'neg', 'inhsie']
    
    def __str__(self):
        return '({},{},{})'.format(self.num, self.gend, self.neg, self.inhsie)

#-------------------------------------------------------------------------------

class PrepNPAttributes(PhraseTypeAttributes):
    case = models.ForeignKey(Case, on_delete=models.PROTECT)
    prep = models.ForeignKey(Preposition, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['case', 'prep']
    
    def __str__(self):
        return '({},{})'.format(self.prep, self.case)

class LexPrepNPAttributes(LexPhraseTypeAttributes):
    num = models.OneToOneField(Number, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.num)

#-------------------------------------------------------------------------------

class ComPrepNPAttributes(PhraseTypeAttributes):
    comprep = models.OneToOneField(ComplexPreposition, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.comprep)

#-------------------------------------------------------------------------------

class PrepNumPAttributes(PhraseTypeAttributes):
    case = models.ForeignKey(Case, on_delete=models.PROTECT)
    prep = models.ForeignKey(Preposition, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['case', 'prep']
    
    def __str__(self):
        return '({},{})'.format(self.prep, self.case)

#-------------------------------------------------------------------------------

class PrepAdjPAttributes(PhraseTypeAttributes):
    case = models.ForeignKey(Case, on_delete=models.PROTECT)
    prep = models.ForeignKey(Preposition, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['case', 'prep']
    
    def __str__(self):
        return '({},{})'.format(self.prep, self.case)

class LexPrepAdjPAttributes(LexPhraseTypeAttributes):
    num = models.ForeignKey(Number, on_delete=models.PROTECT)
    gend = models.ForeignKey(Gender, on_delete=models.PROTECT)
    deg = models.ForeignKey(Degree, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['num', 'gend', 'deg']
    
    def __str__(self):
        return '({},{},{})'.format(self.num, self.gend, self.deg)

#-------------------------------------------------------------------------------

class PrepPPasPAttributes(PhraseTypeAttributes):
    case = models.ForeignKey(Case, on_delete=models.PROTECT)
    prep = models.ForeignKey(Preposition, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['case', 'prep']
    
    def __str__(self):
        return '({},{})'.format(self.prep, self.case)

class LexPrepPPasPAttributes(LexPhraseTypeAttributes):
    num = models.ForeignKey(Number, on_delete=models.PROTECT)
    gend = models.ForeignKey(Gender, on_delete=models.PROTECT)
    neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['num', 'gend', 'neg']
    
    def __str__(self):
        return '({},{},{})'.format(self.num, self.gend, self.neg)

#-------------------------------------------------------------------------------

class PrepGerPAttributes(PhraseTypeAttributes):
    case = models.ForeignKey(Case, on_delete=models.PROTECT)
    prep = models.ForeignKey(Preposition, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['case', 'prep']
    
    def __str__(self):
        return '({},{})'.format(self.prep, self.case)

class LexPrepGerPAttributes(LexPhraseTypeAttributes):
    num = models.ForeignKey(Number, on_delete=models.PROTECT)
    neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT)
    inhsie = models.ForeignKey(PhraseInherentSie, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['num', 'neg', 'inhsie']
    
    def __str__(self):
        return '({},{})'.format(self.num, self.neg, self.inhsie)

#-------------------------------------------------------------------------------

class CPAttributes(PhraseTypeAttributes):
    cptype = models.ForeignKey(CPType, on_delete=models.PROTECT)
    cpreals = models.ManyToManyField(CPRealisation)
    
    def __str__(self):
        return '({},[{}])'.format(self.cptype, ','.join(map(str, self.cpreals.all())))

class LexCPAttributes(LexPhraseTypeAttributes):
    neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT)
    inhsie = models.ForeignKey(PhraseInherentSie, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['neg', 'inhsie']
    
    def __str__(self):
        return '({},{})'.format(self.neg, self.inhsie)

#-------------------------------------------------------------------------------

class NCPAttributes(PhraseTypeAttributes):
    case = models.ForeignKey(Case, on_delete=models.PROTECT)
    cptype = models.ForeignKey(CPType, on_delete=models.PROTECT)
    cpreals = models.ManyToManyField(CPRealisation)
    
    def __str__(self):
        return '({},{},[{}])'.format(self.case, self.cptype, ','.join(map(str, self.cpreals.all())))

class LexNCPAttributes(LexPhraseTypeAttributes):
    neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT)
    inhsie = models.ForeignKey(PhraseInherentSie, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['neg', 'inhsie']
    
    def __str__(self):
        return '({},{})'.format(self.neg, self.inhsie)

#-------------------------------------------------------------------------------

class PrepNCPAttributes(PhraseTypeAttributes):
    case = models.ForeignKey(Case, on_delete=models.PROTECT)
    prep = models.ForeignKey(Preposition, on_delete=models.PROTECT)
    cptype = models.ForeignKey(CPType, on_delete=models.PROTECT)
    cpreals = models.ManyToManyField(CPRealisation)
    
    def __str__(self):
        return '({},{},{},[{}])'.format(self.prep, self.case, self.cptype, ','.join(map(str, self.cpreals.all())))

#-------------------------------------------------------------------------------

class InfPAttributes(PhraseTypeAttributes):
    aspect = models.OneToOneField(PhraseAspect, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.aspect)

class LexInfPAttributes(LexPhraseTypeAttributes):
    neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT)
    inhsie = models.ForeignKey(PhraseInherentSie, on_delete=models.PROTECT)
    
    class Meta:
        unique_together = ['neg', 'inhsie']
    
    def __str__(self):
        return '({},{})'.format(self.neg, self.inhsie)

#-------------------------------------------------------------------------------

class XPAttributes(PhraseTypeAttributes):
    advcat = models.ForeignKey(AdverbialCategory, on_delete=models.PROTECT)
    reals = models.ManyToManyField('PhraseType')
    
    def __str__(self):
        return '({})'.format(self.advcat)

class LexXPAttributes(LexPhraseTypeAttributes):
    lex = models.OneToOneField('PhraseType', on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.lex)

#-------------------------------------------------------------------------------

class AdvPAttributes(PhraseTypeAttributes):
    advcat = models.ForeignKey(AdverbialCategory, on_delete=models.PROTECT)
    reals = models.ManyToManyField('PhraseType')
    
    def __str__(self):
        return '({})'.format(self.advcat)

class LexAdvPAttributes(LexPhraseTypeAttributes):
    deg = models.OneToOneField(Degree, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.deg)

#-------------------------------------------------------------------------------

class ComparAttributes(PhraseTypeAttributes):
    comparcat = models.OneToOneField(ComparCategory, on_delete=models.PROTECT)
    
    def __str__(self):
        return '({})'.format(self.comparcat)

class LexComparAttributes(LexPhraseTypeAttributes):
    lexes = models.ManyToManyField('PhraseType')
    
    def __str__(self):
        return '({})'.format('+'.join(map(str, self.lexes.all())))

#-------------------------------------------------------------------------------

class FixedAttributes(PhraseTypeAttributes):
    phrase = models.ForeignKey('PhraseType', on_delete=models.PROTECT)
    text = models.CharField(max_length=64)
    #text = models.CharField(max_length=64, unique=True)
    
    # TODO: the text should be unique, but it requires corrections in Walenty
    class Meta:
        unique_together = ['phrase', 'text']
    
    def __str__(self):
        return '({},{})'.format(self.phrase, self.text)

# === Lex lemmata ==============================================================

class LemmaOperator(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

class LemmaCooccur(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

class Lemma(models.Model):
    name = models.CharField(max_length=32, unique=True)
    class Meta:
        ordering = ['name']
    def __str__(self):
        return self.name

# === Lex modifications ========================================================

class ModificationType(models.Model):
    name = models.CharField(max_length=16, unique=True)
    priority = models.PositiveIntegerField()
    class Meta:
        ordering = ['priority']
    def __str__(self):
        return self.name

class Modification(models.Model):
    mod_type = models.ForeignKey(ModificationType, on_delete=models.PROTECT)
    positions = models.ManyToManyField(Position)
    def __str__(self):
        return '{}({})'.format(self.mod_type, '+'.join(map(str, self.positions.all())))

# === Phrase objects ===========================================================

class PhraseType(models.Model):
    # osobne wpisy dla występujących ograniczonych realizacji, np. xp(locat[prepnp(u,gen),prepnp(w,loc)]
    main_type = models.ForeignKey('PhraseTypeModel', related_name='main_phrase_types', on_delete=models.PROTECT)
    attributes = models.ForeignKey(PhraseTypeAttributes, on_delete=models.PROTECT)
    lexicalized_phrase = models.ForeignKey('PhraseType', related_name='lex_phrases', null=True,
                                                         on_delete=models.PROTECT)
    lemma_operator = models.ForeignKey(LemmaOperator, null=True, on_delete=models.PROTECT)
    lemma_cooccur = models.ForeignKey(LemmaCooccur, null=True, on_delete=models.PROTECT)
    lemmata = models.ManyToManyField(Lemma, related_name='phrase_types')
    modification = models.ForeignKey(Modification, null=True, on_delete=models.PROTECT)
    text_rep = models.TextField(unique=True)

    class Meta:
        ordering = ['main_type__priority', 'text_rep']

    def __str__(self):
        return self.text_rep


class PhraseTypeModel(models.Model):
    name = models.CharField(max_length=16, unique=True)
    phraseologic = models.BooleanField()
    priority = models.PositiveIntegerField()

    class Meta:
        # TODO define an ordering priority for this model
        # first lex and fixed, then alphabetical order?
        ordering = ['priority', 'name']

    def __str__(self):
        return self.name