from django.db import models
from django import forms
from django.utils import timezone
from datetime import datetime
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class InfoItem(models.Model):
    class __meta__:
        abstract = True
    name = models.CharField(max_length=255)
    expire_date = models.DateTimeField(blank=True,null=True) # None means never expiring item

    def get_template_url(self):
        raise NotImplementedError("inheriting classes must implement get_template_url")
    @staticmethod
    def get_create_template_url():
        raise NotImplementedError("inheriting classes must implement get_create_template_url")

    @classmethod
    def create_from_dict(cls,d):
        item = cls()
        item.update_from_dict(d)
        return item

    def update_from_dict(self,d):
        try:
            expire_date = d.pop('expire_date', None)
            self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S")
        except:
            pass
        dmap = {
            'name': 'name',
        }
        for k,v in d.items():
            try:
                self.__setattr__(dmap[k],v)
            except KeyError:
                pass
        self.save()


    def get_dict(self):
        return {
            'id': self.id,
            'name': self.name,
            'item_type': ContentType.objects.get_for_model(self).id,
            'template_url': self.get_template_url(),
            'create_template_url': self.get_create_template_url(),
            'options': {}
        }

    def delete(self):
        # since generic foreignkeys suck. delete infoitems pointing here manually
        InfoInstance.objects.filter(item_id=self.id, item_type=ContentType.objects.get_for_model(self)).delete()
        super().delete()

    @classmethod
    def get_subclasses(cls):
        for subclass in cls.__subclasses__():
            yield from subclass.get_subclasses()
            yield subclass

    def __str__(self):
        return self.name


class ABBInfoItem(InfoItem):
    def get_template_url(self):
        return "/static/html/abb.html"
    @staticmethod
    def get_create_template_url():
        return "/static/html/abb_create.html"

class SossoInfoItem(InfoItem):
    def get_template_url(self):
        return "/static/html/sosso.html"
    @staticmethod
    def get_create_template_url():
        return "/static/html/sosso_create.html"

class ImageInfoItem(InfoItem):
    img = models.ImageField(upload_to="infoimages/")

    def get_template_url(self):
        #get param to avoid angular from optimizing same template with different options
        return "/static/html/generic_image.html?img={}".format(self.name)

    @staticmethod
    def get_create_template_url():
        return "/static/html/generic_image_create.html"

    def get_dict(self):
        d = super().get_dict()
        d["options"] = {'img': self.img.url}
        return d

class ExternalImageInfoItem(InfoItem):
    url = models.TextField()

    def get_template_url(self):
        return "/static/html/generic_image.html?img={}".format(self.name)

    @staticmethod
    def get_create_template_url():
        return "/static/html/generic_external_image_create.html"

    def get_dict(self):
        d = super().get_dict()
        d["options"] = {'img': self.url}
        return d

    @classmethod
    def create_from_dict(cls,d):
        item = cls()
        item.update_from_dict(d)
        return item

    def update_from_dict(self,d):
        try:
            expire_date = d.pop('expire_date', None)
            self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S")
        except:
            pass

        dmap = {
            'name': 'name',
            'url': 'url',
        }
        for k,v in d.items():
            try:
                self.__setattr__(dmap[k],v)
            except KeyError:
                pass
        self.save()

class InfoInstance(models.Model):
    rotation = models.ForeignKey('Rotation', related_name='instances')
    duration = models.FloatField(default=15.0) # seconds
    # generic relation to somekind of InfoItem
    item_id = models.PositiveIntegerField()
    item_type = models.ForeignKey(ContentType,on_delete=models.CASCADE)
    item = GenericForeignKey('item_type','item_id')

    @classmethod
    def create_from_dict(cls,d):
        try:
            rotation = Rotation.objects.get(pk=int(d["rotation_id"]))
            ct = ContentType.objects.get_for_id(int(d["item_type"]))
            item = ct.get_object_for_this_type(pk=int(d["item_id"]))
            duration = float(d["duration"])
        except:
            raise RuntimeError("invalid parameters supplied supplied")
        try:
            return cls.objects.create(
                rotation=rotation,
                item=item,
                duration=duration
            )
        except:
            raise RuntimeError("error while adding instance to db")

    def get_dict(self):
        return {
            'id':self.id,
            'item': self.item.get_dict(),
            'duration': self.duration,
        }
    def __str__(self):
        return "{}: {} ({}s)".format(self.rotation.name, self.item.name, self.duration)

class Rotation(models.Model):
    name = models.CharField(max_length=255)

    def get_dict(self):
        # exclude expired items from rotation (note: using tricky syntax to avoid excluding items with no expire_date)
        now = timezone.now()
        instances = self.instances.all()
        filtered = filter(lambda i: (i.item.expire_date or now) >= now, list(instances))
        instance_list = list(map(lambda i:i.get_dict(), filtered))

        return {
            'id':self.id,
            'name': self.name,
            'instances': instance_list,
        }
    def get_list(self):
        return {
            'id':self.id,
            'name':self.name,
        }

    def __str__(self):
        return self.name

class ImageUploadForm(forms.Form):
    '''
    Form used to handle imageuploads to
    infoscreen app
    '''
    name = forms.CharField()
    image = forms.ImageField()
