from ldt.ldt_utils.contentindexer import add_segment, edit_segment, delete_segment
from ldt.ldt_utils.models import Project, Content, Segment
from ldt.ldt_utils.utils import LdtAnnotation
from ldt.security import protect_models, unprotect_models

from django.conf.urls import url
from tastypie import fields
from tastypie.authorization import Authorization
from tastypie.exceptions import NotFound, BadRequest
from tastypie.resources import Resource
from tastypie.utils import trailing_slash


class AnnotationObject(object):
    def __init__(self, id="", project="", type="", type_title="", ensemble="", media="", begin=0, end=0, content=None, tags=None, meta=None):  # @ReservedAssignment
        self.id = id
        self.project = project
        self.type = type
        self.type_title = type_title
        self.ensemble = ensemble
        self.media = media
        self.begin = begin
        self.end = end
        self.content = content if content is None else {"title":"", "description":""}
        self.tags = tags if tags is None else  []
        self.meta = meta if meta is None else {"creator":"", "created":""}

class AnnotationResource(Resource):
    # For the moment, these attributes are useless. We just prepare relations to AnnotationObject
    id = fields.CharField(attribute='id')
    project = fields.CharField(attribute='project')
    type = fields.CharField(attribute='type')
    type_title = fields.CharField(attribute='type_title')
    media = fields.CharField(attribute='media')
    begin = fields.IntegerField(attribute='begin')
    end = fields.IntegerField(attribute='end')
    content = fields.DictField(attribute='content')
    tags = fields.ListField(attribute='tags')
    meta = fields.DictField(attribute='meta')

    class Meta:
        allowed_methods = ['post', 'put', 'delete']
        resource_name = 'annotations'
        object_class = AnnotationObject
        authorization = Authorization()
        # always_return_data = True because we want the api returns the data with the updated ids
        always_return_data = True
        include_resource_uri = False

    def prepend_urls(self):
        return [
            url(r"^(?P<resource_name>%s)/(?P<element_id>[^/]*?)(?:/(?P<project_id>[^/]*?)(?:/(?P<iri_id>[^/]*?)(?:/(?P<grouping_id>[^/]*?)(?:/(?P<cutting_id>[^/]*?))?)?)?)?%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
        ]

    def extract_annotation_data(self, data):
        # This method extracts and validates the data we receive from the user for adding or editing an annotation

        project_id = ""
        if data.has_key('project') :
            project_id = data["project"]

        if data.has_key('media') :
            iri_id = data["media"]
        else :
            raise BadRequest

        if project_id and project_id != "" :
            try:
                project = Project.objects.get(ldt_id=project_id)
            except Project.DoesNotExist:
                raise NotFound("Project not found. project_id = " + project_id)
        else :
            # If the project's is not defined, we get or create the content's front project.
            try:
                content = Content.objects.get(iri_id=iri_id)
            except Content.DoesNotExist:
                raise NotFound("Content not found. iri_id = " + iri_id)
            project = content.get_or_create_front_project()
            project_id = project.ldt_id

        ann_duration = 0
        ann_begin = 0
        ann_end = 0
        if (data.has_key('begin') and data.has_key('end')):
            ann_duration = str(data['end'] - data['begin'])
            ann_begin = str(data['begin'])
            ann_end = str(data['end'])

        ann_audio_src = data.get('content', {}).get('audio', {}).get('src', "")
        ann_audio_href = data.get('content', {}).get('audio', {}).get('href', "")

        ann_author = data.get('meta', {}).get('creator', "")
        ann_date = data.get('meta', {}).get('created', "")

        ann_type_id = data.get('type', '')
        ann_type_title = data.get('type_title', '')
        ann_id = data.get("id", "")
        ann_title = data.get('content', {}).get('title', "")
        ann_description = data.get('content', {}).get('description')
        ann_tags = data.get('tags', [])

        content = project.contents.get(iri_id=iri_id)

        return {
            "project_obj" : project,
            "project_id" : project_id,
            "content_obj" : content,
            "iri_id" : iri_id,
            "ann_type_id" : ann_type_id,
            "ann_type_title" : ann_type_title,
            "ann_id" : ann_id,
            "ann_content" : {
                "title" : ann_title,
                "description" : ann_description,
                "audio" : {
                    "src" : ann_audio_src,
                    "href" : ann_audio_href,
                },
            },
            "ann_meta" : {
                "creator" : ann_author,
                "created" : ann_date,
            },
            "ann_tags" : ann_tags,
            "ann_begin" : ann_begin,
            "ann_end" : ann_end,
            "ann_duration" : ann_duration,
        }

    def obj_delete_list(self, bundle, **kwargs):
        return True

    def obj_create(self, bundle, **kwargs):
        # Here the a has the datas for only one annotation. Tastypie's post allows only one resource addition
        data_dict = self.extract_annotation_data(bundle.data)
        adder = LdtAnnotation(data_dict["project_obj"])

        unprotect_models()  # Allows anonymous user to modify models in this request only

        try:
            type_id, new_id, ensemble_id = adder.add(
                data_dict['iri_id'],  # media
                data_dict['ann_type_id'],  # cutting_id
                data_dict['ann_type_title'],  # cutting_title
                data_dict['ann_content']['title'],  # title
                data_dict['ann_content']['description'],  # text
                data_dict['ann_tags'],  # tags_list
                data_dict['ann_begin'],  # begin
                data_dict['ann_duration'],  # dur
                data_dict['ann_meta']['creator'],  # author
                data_dict['ann_meta']['created'],  # date
                None,
                "2194379",
                data_dict['ann_content']['audio']['src'],
                data_dict['ann_content']['audio']['href']
            )

            if not new_id:
                raise BadRequest

            # We update the ids
            data_dict['ann_type_id'] = type_id
            data_dict['ensemble_id'] = ensemble_id
            data_dict['ann_id'] = new_id

            # add segment
            add_segment({
                "project" : data_dict["project_obj"],
                "content" : data_dict["content_obj"],
                "ensemble_id" : data_dict["ensemble_id"],
                "cutting_id" :  data_dict['ann_type_id'],
                "element_id" : data_dict['ann_id'],
                "title" : data_dict['ann_content']['title'],
                "abstract" : data_dict['ann_content']['description'],
                "tags" : ",".join(data_dict['ann_tags']),
                "start_ts" : data_dict['ann_begin'],
                "duration" :  data_dict['ann_duration'],
                "author" : data_dict['ann_meta']['creator'],
                "date" : data_dict['ann_meta']['created'],
                "audio_src" : data_dict['ann_content']['audio']['src'],
                "audio_href" : data_dict['ann_content']['audio']['href'],
                "polemics": adder.get_polemic_syntax(data_dict['ann_content']['title'])
            })

            # We save the added annotation and reprotect the contents and projects
            adder.save(must_reindex=False)
        finally:
            protect_models()
        # We update the AnnotationObject for the returned datas to be correct.
        bundle.obj = AnnotationObject(
            id=data_dict["ann_id"],
            project=data_dict["project_id"],
            type=data_dict["ann_type_id"],
            type_title=data_dict["ann_type_title"],
            ensemble=data_dict['ensemble_id'],
            media=data_dict["iri_id"],
            begin=data_dict["ann_begin"],
            end=data_dict["ann_end"],
            content=data_dict['ann_content'],
            tags=data_dict['ann_tags'],
            meta=data_dict['ann_meta']
        )
        return bundle

    def obj_update(self, bundle, **kwargs):
        data_dict = self.extract_annotation_data(bundle.data)
        if not data_dict['ann_id']:
            data_dict['ann_id'] = kwargs["element_id"]

        adder = LdtAnnotation(data_dict["project_obj"])
        try:
            unprotect_models()  # Allows anonymous user to modify models in this request only

            ensemble_id = adder.edit(
                data_dict['ann_id'],  # annotation_id
                data_dict['iri_id'],  # media
                data_dict['ann_type_id'],  # cutting_id
                data_dict['ann_type_title'],  # cutting_title
                data_dict['ann_content']['title'],  # title
                data_dict['ann_content']['description'],  # text
                data_dict['ann_tags'],  # tags_list
                data_dict['ann_begin'],  # begin
                data_dict['ann_duration'],  # dur
                data_dict['ann_meta']['creator'],  # author
                data_dict['ann_meta']['created'],  # date
                None,
                "2194379",
                data_dict['ann_content']['audio']['src'],
                data_dict['ann_content']['audio']['href']
            )
            if not ensemble_id:
                raise BadRequest
            # We update the id
            data_dict['ensemble_id'] = ensemble_id
            # add segment
            edit_segment(
                data_dict["project_id"],
                data_dict["iri_id"],
                data_dict["ensemble_id"],
                data_dict['ann_type_id'],
                data_dict["ann_id"],
                params={
                    "project" : data_dict["project_obj"],
                    "content" : data_dict["content_obj"],
                    "ensemble_id" : data_dict["ensemble_id"],
                    "cutting_id" :  data_dict['ann_type_id'],
                    "element_id" : data_dict['ann_id'],
                    "title" : data_dict['ann_content']['title'],
                    "abstract" : data_dict['ann_content']['description'],
                    "tags" : ",".join(data_dict['ann_tags']),
                    "start_ts" : data_dict['ann_begin'],
                    "duration" :  data_dict['ann_duration'],
                    "author" : data_dict['ann_meta']['creator'],
                    "date" : data_dict['ann_meta']['created'],
                    "audio_src" : data_dict['ann_content']['audio']['src'],
                    "audio_href" : data_dict['ann_content']['audio']['href'],
                    "polemics": adder.get_polemic_syntax(data_dict['ann_content']['title'])
                })

            # We save the added annotation and reprotect the contents and projects
            adder.save(must_reindex=False)
        finally:
            protect_models()
        # We update the AnnotationObject for the returned datas to be correct.

        bundle.obj = AnnotationObject(
            id=data_dict["ann_id"],
            project=data_dict["project_id"],
            type=data_dict["ann_type_id"],
            type_title=data_dict["ann_type_title"],
            ensemble=data_dict['ensemble_id'],
            media=data_dict["iri_id"],
            begin=data_dict["ann_begin"],
            end=data_dict["ann_end"],
            content=data_dict['ann_content'],
            tags=data_dict['ann_tags'],
            meta=data_dict['ann_meta']
        )
        return bundle

    def obj_delete(self, bundle, **kwargs):
        query_args = {
            'project_id' : kwargs.get("project_id", None),
            'iri_id' : kwargs.get("iri_id", None),
            'ensemble_id' : kwargs.get("grouping_id", None),
            'cutting_id' : kwargs.get("cutting_id", None),
            'element_id' : kwargs.get("element_id", None)
        }
        query_args = { k: v for k,v in query_args.items() if v }
        try:
            seg_to_delete = Segment.objects.get(**query_args)
        except Segment.DoesNotExist:
            raise NotFound("Segment not found. element_id = " + query_args['element_id'])
        except Segment.MultipleObjectsReturned:
            raise NotFound("Multiple segments found. element_id = " + query_args['element_id'])
        project_id = seg_to_delete.project_id
        try:
            project = Project.objects.get(ldt_id=project_id)
        except Project.DoesNotExist:
            raise NotFound("Project not found. ldt_id = " + project_id)
        ann_type_id = seg_to_delete.cutting_id
        ensemble_id = seg_to_delete.ensemble_id
        iri_id = seg_to_delete.iri_id
        adder = LdtAnnotation(project)
        try:
            unprotect_models()
            deleted = adder.delete(query_args['element_id'], iri_id, ann_type_id)
            if not deleted:
                raise BadRequest
            delete_segment(project, project_id, iri_id, ensemble_id, ann_type_id, query_args['element_id'])
            adder.save()
        finally:
            protect_models()
        return bundle

    def get_resource_uri(self, bundle_or_obj=None, url_name='api_dispatch_list'):
        return ''
