from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.db.models import Q

class Link(models.Model):
    """
    Links objects of any models together.
    """
    content_type1 = models.ForeignKey(ContentType, related_name="linked_set1")
    object_id1 = models.CharField(max_length=200)

    content_type2 = models.ForeignKey(ContentType,  related_name="linked_set2")
    object_id2 = models.CharField(max_length=200)

    order = models.FloatField(blank=True, null=True)

    obj1 = generic.GenericForeignKey(ct_field="content_type1", fk_field="object_id1")
    obj2 = generic.GenericForeignKey(ct_field="content_type2", fk_field="object_id2")
    
    def __unicode__(self):
        return u"%s => %s" % (self.obj1, self.obj2)
    
    @classmethod
    def fromObjects(cls, obj1, obj2):
        try:
            return cls.objects.get(cls.Q4Obj(obj1, 1) & cls.Q4Obj(obj2, 2))
        except cls.DoesNotExist:
            return cls(obj1=obj1, obj2=obj2)
        
    @staticmethod
    def Q4Obj(obj, which):
        if which not in (1, 2):
            raise ValueError("Value of which is `%s', expected 1 or 2" % which)
        ct = ContentType.objects.get_for_model(obj)
        return Q(**{'object_id%d' % which: obj.id,
                'content_type%d' % which: ct})

    @classmethod
    def filterByObj(cls, obj, which=None):
        if not which:
            q = cls.Q4Obj(obj, 1) | cls.Q4Obj(obj, 2)
        else:
            q = cls.Q4Obj(obj, which)
        return cls.objects.filter(q)

    @classmethod
    def getLinked(cls, obj):
        s = set()
        ct = ContentType.objects.get_for_model(obj)
        for l in cls.filterByObj(obj, 1):
            s.add(l.obj2)
        for l in cls.filterByObj(obj, 2):
            s.add(l.obj1)
        return s
    
    @classmethod
    def Q4Model(cls, model, which):
        if which not in (1, 2):
            raise ValueError("Value of which is `%s', expected 1 or 2" % which)
        ct = ContentType.objects.get_for_model(model)
        return Q(**{'content_type%d' % which: ct})
        
    
