from StringIO import StringIO
from django.contrib.contenttypes.models import ContentType
from django.dispatch import receiver
from ldt import settings
from ldt.indexation import object_delete, object_insert, object_run_index
from ldt.ldt_utils.events import post_project_save
from ldt.ldt_utils.models import Segment, Content, Project
from ldt.ldt_utils.stat import update_stat_project, add_annotation_to_stat
from ldt.ldt_utils.utils import reduce_text_node
from ldt.utils.url import request_with_auth
from taggit.models import Tag, TaggedItem
from taggit.utils import parse_tags
import lxml.etree #@UnresolvedImport

import logging
logger = logging.getLogger(__name__)

def Property(func):
    return property(**func()) 


class LdtIndexer(object):
    
    def __init__(self, object_list, decoupage_blackList=settings.DECOUPAGE_BLACKLIST, callback=None):
        self.__object_list = object_list
        self.__decoupage_blacklist = decoupage_blackList
        self.__callback = callback
        self.__segment_cache = []
        self.__all_tags_cache = {}
        self.__segment_tags_cache = {}
        self.__tags_cache = []
        
    @Property
    def decoupage_blacklist(): #@NoSelf
        doc = """get blacklist""" #@UnusedVariable
       
        def fget(self):
            if self.__decoupage_blacklist is None:
                self.__decoupage_blacklist = ()
            return self.__decoupage_blacklist
           
        def fset(self, value):
            self.__decoupage_blacklist = value
           
        def fdel(self):
            del self.__decoupage_blacklist
           
        return locals()
    
    def index_all(self):
        for i,obj in enumerate(self.__object_list):
            if self.__callback:
                self.__callback(i,obj)
            self.index_object(obj)
    
    
    def index_object(self, obj):
        self._do_index_object(obj)
        
        if self.__segment_cache:
            object_insert(Segment, self.__segment_cache, 'id_hash')
            object_run_index(Segment, self.__segment_cache)
            self.__segment_cache = []


    def _do_index_object(self, obj):
        raise NotImplementedError()
    
    def index_ensemble(self, ensemble, content, project=None):
        ensembleId = ensemble.get(u"id", None)
        ctp = ContentType.objects.get_for_model(Segment)
        
        for decoupageNode in ensemble.getchildren():
            if decoupageNode.tag != "decoupage"  or decoupageNode.get(u"id", None) in self.decoupage_blacklist:
                continue
            
            decoupId = decoupageNode.get(u"id", None)
            res = decoupageNode.xpath("elements/element")
            for elementNode in res:
                
                elementId = elementNode.get(u"id", None)
                tags = elementNode.get(u"tags", None)
                
                if tags is None or len(tags) == 0:
                    tags = u""
                    restagnode = elementNode.xpath("tag/text()", smart_strings=False)
                    for tagnode in restagnode:
                        tags = tags + u"," + tagnode
                        
                if tags is None or len(tags) == 0:
                    tags = u""
                    restagnode = elementNode.xpath("tags/tag/text()", smart_strings=False)

                    for tagnode in restagnode:
                        tags = tags + u"," + tagnode
                
                if tags is None:
                    tags = u""
                

                title = reduce_text_node(elementNode, "title/text()")                
                abstract = reduce_text_node(elementNode, "abstract/text()")
                polemics = elementNode.xpath('meta/polemics/polemic/text()')
                
                author = elementNode.get("author", "")
                start_ts = int(float(elementNode.get("begin", "-1")))
                duration = int(float(elementNode.get("dur", "0")))
                date_str = elementNode.get("date", "")
                ldt_id = u""
                if project:
                    ldt_id = project.ldt_id
                # audio annotation management
                audio_src = u""
                audio_href = u""
                audio_node = elementNode.xpath('audio')
                if audio_node:
                    audio_src = audio_node[0].get(u"source", u"")
                    audio_href = audio_node[0].text
                
                seg = Segment.create(content=content,
                              iri_id=content.iri_id,
                              ensemble_id=ensembleId,
                              cutting_id=decoupId,
                              element_id=elementId,
                              title=title,
                              abstract=abstract,
                              duration=duration,
                              author=author,
                              start_ts=start_ts,
                              date=date_str,
                              project_obj=project,
                              project_id=ldt_id,
                              audio_src=audio_src,
                              audio_href=audio_href)
                
                tags = parse_tags(tags)
                self.__segment_tags_cache[seg.id_hash] = tags
                seg.polemics = seg.get_polemic(polemics)
                if settings.LDT_INDEXATION_INSERT_BATCH_SIZE < 2:
                    seg.save()
                    seg.tags.add(*tags)
                else:
                    self.__segment_cache.append(seg)
                    self.__tags_cache = set( list(self.__tags_cache) + tags)
                    
                    if not (len(self.__segment_cache)%settings.LDT_INDEXATION_INSERT_BATCH_SIZE):
                        # First we insert/bulk_create the segments
                        object_insert(Segment, self.__segment_cache, 'id_hash')
                        # Filter already existing tags in current dict
                        for t in list(self.__tags_cache):
                            if t in self.__all_tags_cache:
                                self.__tags_cache.remove(t)
                        # Filter already existing tags in database
                        current_tags = Tag.objects.filter(name__in=self.__tags_cache)
                        for t in current_tags:
                            self.__all_tags_cache[t.name] = t
                            self.__tags_cache.remove(t.name)
                        # If the rest of tags were never in the db, we save them
                        if len(self.__tags_cache)>0:
                            for t in self.__tags_cache:
                                tag = Tag.objects.create(name=t)
                                self.__all_tags_cache[t] = tag
                        
                        # Prepare taggeditems
                        ti = []
                        for s in self.__segment_cache:
                            s.tag_list = self.__segment_tags_cache[s.id_hash]
                            for t in self.__segment_tags_cache[s.id_hash]:
                                ti.append( TaggedItem(tag=self.__all_tags_cache[t], content_type=ctp, object_id=s.pk) )
                        TaggedItem.objects.bulk_create(ti)
                        object_run_index(Segment, self.__segment_cache)
                        self.__segment_cache = []
        
        # last loop if necessary
        if len(self.__segment_cache) > 0:
            # First we insert/bulk_create the segments
            object_insert(Segment, self.__segment_cache, 'id_hash')
            # Filter already existing tags in current dict
            for t in list(self.__tags_cache):
                if t in self.__all_tags_cache:
                    self.__tags_cache.remove(t)
            # Filter already existing tags in database
            current_tags = Tag.objects.filter(name__in=self.__tags_cache)
            for t in current_tags:
                self.__all_tags_cache[t.name] = t
                self.__tags_cache.remove(t.name)
            # If the rest of tags were never in the db, we save them
            if len(self.__tags_cache)>0:
                for t in self.__tags_cache:
                    tag = Tag.objects.create(name=t)
                    self.__all_tags_cache[t] = tag
            
            # Prepare taggeditems
            ti = []
            for s in self.__segment_cache:
                s.tag_list = self.__segment_tags_cache[s.id_hash]
                for t in self.__segment_tags_cache[s.id_hash]:
                    ti.append( TaggedItem(tag=self.__all_tags_cache[t], content_type=ctp, object_id=s.pk) )
            TaggedItem.objects.bulk_create(ti)
            object_run_index(Segment, self.__segment_cache)
            # End of batch
            self.__segment_cache = []



class ContentIndexer(LdtIndexer):
        
    def _do_index_object(self, obj):
        
        content = obj 
        url = content.iri_url()
        _, file_content = request_with_auth(url)
        doc = lxml.etree.parse(StringIO(file_content)) #@UndefinedVariable
       
        object_delete(Segment, iri_id=content.iri_id, project_id='')
        
        res = doc.xpath("/iri/body/ensembles/ensemble")

        for ensemble in res:                
            self.index_ensemble(ensemble, content)
                            
            
class ProjectIndexer(LdtIndexer):
                              
    def _do_index_object(self, obj):

        project = obj
        # pocketfilms.utils.log.debug("Indexing project : "+str(project.iri_id))
        doc = lxml.etree.fromstring(project.ldt_encoded) #@UndefinedVariable

        object_delete(Segment, project_obj__ldt_id=project.ldt_id)
       
        res = doc.xpath("/iri/annotations/content")

        for content in res:
            contentId = content.get(u"id", None)
            content_obj = None

            clist = Content.objects.filter(iri_id = contentId) #@UndefinedVariable
            if len(clist) > 0:
                content_obj = clist[0]

            for ensemble in content.getchildren():
                self.index_ensemble(ensemble, content_obj, project)

@receiver(post_project_save)
def index_project(**kwargs):
    must_reindex = kwargs.get("must_reindex", True)
    if must_reindex and settings.AUTO_INDEX_AFTER_SAVE:
        instance = kwargs['instance']
        if instance.state != Project.PUBLISHED:
            object_delete(Segment, project_obj__ldt_id=instance.ldt_id)
            update_stat_project(instance)
        else:
            projectIndexer = ProjectIndexer([instance])
            projectIndexer.index_all()
            update_stat_project(instance)



def add_segment(params):
                                
    project = params.get("project",None)
    content = params.get("content",None)
    ensemble_id = params.get("ensemble_id", "")
    cutting_id = params.get("cutting_id", "")
    element_id = params.get("element_id", "")
    title = params.get("title", "")
    abstract = params.get("abstract", "")
    tags_str = params.get("tags", "")
    start_ts = params.get("start_ts", 0)
    duration = params.get("duration", 0)
    author = params.get("author", "")
    date_str = params.get("date", "")
    audio_src = params.get("audio_src", "")
    audio_href = params.get("audio_href", "")
    polemics = params.get("polemics", "")
    
    seg = Segment.create(content=content,
              iri_id=content.iri_id if content is not None else "",
              ensemble_id=ensemble_id,
              cutting_id=cutting_id,
              element_id=element_id,
              title=title,
              abstract=abstract,
              duration=duration,
              author=author,
              start_ts=start_ts,
              date=date_str,
              project_obj=project,
              project_id=project.ldt_id if project is not None else "",
              audio_src=audio_src,
              audio_href=audio_href)
    seg.polemics = seg.get_polemic(polemics)
    seg.save()
    for t in parse_tags(tags_str):
        seg.tags.add(t)
    seg.save()
    add_annotation_to_stat(seg.content, seg.start_ts, seg.start_ts+seg.duration)


def delete_segment(project, project_id, iri_id, ensemble_id, cutting_id, element_id):

    # delete Segment
    for seg in Segment.objects.filter(project_id=project_id, iri_id=iri_id, ensemble_id=ensemble_id, cutting_id=cutting_id, element_id=element_id):        
        seg.delete()
        add_annotation_to_stat(seg.content, seg.start_ts, seg.start_ts+seg.duration)
    
    
    