from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import Group
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import redirect, get_object_or_404
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
from django.views.generic.base import View, TemplateResponseMixin
from guardian.shortcuts import assign
from ldt.ldt_utils.models import Project, Content
from ldt.ldt_utils.views.content import write_content_base, delete_content
from ldt.ldt_utils.utils import generate_uuid, copy_ldt
from ldt.security.permissionchecker import check_object_perm_for_user
from metadatacomposer.forms import ImageUploadModelForm
from metadatacomposer.models import Image
from sorl.thumbnail import get_thumbnail
import os
import datetime


import logging #@UnresolvedImport
logger = logging.getLogger(__name__)


class MetadataComposerContextView(View):
    
    branding = "iri"
    
    def get_context_dict(self, request):
        context = {}
        context["branding"] = self.branding
        context["top_header_css"] = "metadatacomposer/%s/composer_header_css.html" % self.branding
        context["top_header_partial"] = "metadatacomposer/%s/composer_header.html" % self.branding
        context["player_css"] = "metadatacomposer/%s/composer_player_css.html" % self.branding
        context["player_about"] = "metadatacomposer/%s/composer_player_about.html" % self.branding
        return context



class MetadataComposerHome(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_home.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerHome, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        
        projects = Project.safe_objects.filter(owner=request.user).exclude(title__startswith='front').order_by('-modification_date')[:6]
        images = Image.objects.order_by('-modification_date')[:6]
        contents = Content.safe_objects.order_by('-update_date')[:6]
        
        context = self.get_context_dict(request)
        context.update({"projects":projects, "images": images, "contents": contents})
        return self.render_to_response(context)



class MetadataComposerProjectList(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_project_list.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerProjectList, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        page = request.GET.get("page") or 1
        
        projects = Project.safe_objects.prefetch_related("contents").exclude(title__startswith='front').order_by('-modification_date').filter(owner=request.user)
        nb = getattr(settings, 'METADATACOMPOSER_ELEMENTS_PER_PAGE', 9)
        if page=="x":
            nb = projects.count()
        
        paginator = Paginator(projects, nb)
        try:
            results = paginator.page(page)
        except (EmptyPage, InvalidPage):
            results = paginator.page(paginator.num_pages)
        
        context = self.get_context_dict(request)
        context.update({"results":results})
        return self.render_to_response(context)



class MetadataComposerResourceList(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_resource_list.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerResourceList, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        
        # We get the first page of images
        image_results = get_images(1)
        # We get the first contents page and theirs projects
        content_results = get_contents_and_projects(1, request.user)
        
        context = self.get_context_dict(request)
        context.update({"image_results":image_results, "content_results":content_results})
        return self.render_to_response(context)



class MetadataComposerImagePagination(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        if self.template_name and self.template_name!="":
            return self.template_name
        else:
            return "partial/resource_image_list.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerImagePagination, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        page = request.GET.get("page") or 1
        mode = request.GET.get("mode") or "resource"
        # Get current contents page and theirs projects
        image_results = get_images(page)
        
        if mode=="library":
            self.template_name = "partial/library_image_list.html"
        
        context = self.get_context_dict(request)
        context.update({"image_results":image_results})
        return self.render_to_response(context)



class MetadataComposerContentPagination(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        if self.template_name and self.template_name!="":
            return self.template_name
        else:
            return "partial/resource_content_list.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerContentPagination, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        page = request.GET.get("page") or 1
        mode = request.GET.get("mode") or "resource"
        select_media = False
        create_empty_project = False
        if mode=="library":
            self.template_name = "partial/library_content_list.html"
            select_media = True
        elif mode=="create":
            self.template_name = "partial/library_content_list.html"
            create_empty_project = True
        
        # Get current contents page and theirs projects
        content_results = get_contents_and_projects(page, request.user, select_media, create_empty_project)
        
        context = self.get_context_dict(request)
        context.update({"content_results":content_results, "mode":mode})
        return self.render_to_response(context)



def get_images(page):
    
    # We get the current's page images
    images = Image.objects.order_by('-modification_date')
    nb = getattr(settings, 'METADATACOMPOSER_ELEMENTS_PER_PAGE', 8)
    if page=="x":
        nb = images.count()
    paginator = Paginator(images, nb)
    try:
        results = paginator.page(page)
    except (EmptyPage, InvalidPage):
        results = paginator.page(paginator.num_pages)
    
    return results



def get_contents_and_projects(page, user, select_media=False, create_empty_project=False):
    
    # We get the current's page contents
    # prefetch_related("project_set") is unfortunately useless because we have to filter the project queryset later
    if select_media:
        contents = Content.safe_objects.select_related("media_obj").order_by('-update_date')
    else:
        contents = Content.safe_objects.order_by('-update_date')
    nb = getattr(settings, 'METADATACOMPOSER_ELEMENTS_PER_PAGE', 8)
    if page=="x":
        nb = contents.count()
    paginator = Paginator(contents, nb)
    try:
        results = paginator.page(page)
    except (EmptyPage, InvalidPage):
        results = paginator.page(paginator.num_pages)
    
    # We add the user's projects for each content
    results_object_list = []
    for content in results.object_list:
        # We filter the content's projects with the user's ones
        if select_media:
            if not content.videopath and content.videopath!="":
                content.url = content.videopath.rstrip('/') + "/" + content.src
            else:
                content.url = content.src
        elif create_empty_project:
            content.url = "create_project_for_me"
        projects = content.project_set.all().filter(owner=user)
        results_object_list.append({"content":content, "projects":projects})
    
    results.object_list = results_object_list
    return results



class MetadataComposerModalContent(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_modal_content.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerModalContent, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", iri_id=None, **kwargs):
        self.branding = branding
        
        # Generate fake id to validate ContentForm
        if not iri_id:
            iri_id = generate_uuid()
            content = None
        else:
            content = Content.safe_objects.select_related("media_obj").get(iri_id=iri_id)
        
        context = self.get_context_dict(request)
        context.update({"iri_id":iri_id, "content":content})
        return self.render_to_response(context)
    
    def post(self, request, branding="iri", iri_id=None, **kwargs):
        self.branding = branding
        
        # We create the media
        content_form, media_form, picture_form, form_status, _, current_front_project, e, traceback = write_content_base(request, iri_id)
        # And test creation
        if (content_form == False and media_form == False and picture_form == False and form_status == False and current_front_project == False):
            #message=_("An error occurred - Please try again or contact webmaster")
            #title = _("Error")
            raise e, None, traceback
        return redirect(request.META['HTTP_REFERER'])



class MetadataComposerModalContentLibrary(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_modal_content_library.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerModalContentLibrary, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        mode = request.GET.get("mode") or None
        
        select_media = True
        create_empty_project = False
        if mode=="create":
            select_media = False
            create_empty_project = True
        
        context = self.get_context_dict(request)
        context.update({"content_results":get_contents_and_projects(1, request.user, select_media, create_empty_project), "mode":mode})
        return self.render_to_response(context)



class MetadataComposerModalImage(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_modal_image.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerModalImage, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        image_pk = request.GET.get("image") or None
        
        # Add form
        if image_pk:
            form = ImageUploadModelForm(instance=Image.objects.get(pk=image_pk))
        else:
            form = ImageUploadModelForm()
        
        context = self.get_context_dict(request)
        context.update({"form":form, "image_pk":image_pk})
        return self.render_to_response(context)
    
    def post(self, request, branding="iri", **kwargs):
        self.branding = branding
        
        # Check form
        if 'image_pk' in request.POST:
            form = ImageUploadModelForm(request.POST, request.FILES, instance=Image.objects.get(pk=request.POST['image_pk']))
        else:
            form = ImageUploadModelForm(request.POST, request.FILES)
        if form.is_valid():
            # If an image id was in the form, we update the existing image
            form.save()
            return redirect(request.META['HTTP_REFERER'])
        else:
            context = self.get_context_dict(request)
            context.update({"form":form})
            return redirect("composer_modal_image", branding=branding)



class MetadataComposerModalImageLibrary(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_modal_image_library.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerModalImageLibrary, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        
        context = self.get_context_dict(request)
        context.update({"image_results":get_images(1)})
        return self.render_to_response(context)



class MetadataComposerRemoveImage(View):
    
    @method_decorator(login_required)
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        image_pk = request.GET.get("image") or None
        if image_pk:
            image = get_object_or_404(Image, pk=image_pk)
            image.delete()
        
        return redirect(request.META['HTTP_REFERER'])



class MetadataComposerRemoveContent(View):
    
    @method_decorator(login_required)
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        delete_content(request)
        
        return redirect(request.META['HTTP_REFERER'])



class MetadataComposerRemoveProject(View):
    
    @method_decorator(login_required)
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        ldt_id = request.GET.get("ldt_id") or None
        if ldt_id:
            proj = get_object_or_404(Project, ldt_id=ldt_id)
            if check_object_perm_for_user(proj, "change_project", request.user):
                proj.delete()
        
        return redirect(request.META['HTTP_REFERER'])



class MetadataComposerImage(View):
    
    def get(self, request, branding="iri", image_pk=None, **kwargs):
        self.branding = branding
        image_data = None
        if image_pk:
            image = get_object_or_404(Image, pk=image_pk)
            width = request.GET.get("width") or None
            height = request.GET.get("height") or None
            maxwidth = request.GET.get("maxwidth") or None
            maxheight = request.GET.get("maxheight") or None
            image_file = None
            image_data = None
            # Check w and h
            if width:
                try:
                    width = str(int(width))
                except:
                    return HttpResponseBadRequest("width parameter must be integer.")
            if height:
                try:
                    height = str(int(height))
                except:
                    return HttpResponseBadRequest("height parameter must be integer.")
            if maxwidth:
                try:
                    maxwidth = int(maxwidth)
                except:
                    return HttpResponseBadRequest("maxwidth parameter must be integer.")
            if maxheight:
                try:
                    maxheight = int(maxheight)
                except:
                    return HttpResponseBadRequest("maxheight parameter must be integer.")
            if (maxwidth and not maxheight) or (not maxwidth and maxheight):
                return HttpResponseBadRequest("maxwidth AND maxheight parameters must be set.")
            if not width and not height and not maxwidth and not maxheight:
                # No w and no h parameter : raw file 200px width
                image_file = get_thumbnail(os.path.join(settings.MEDIA_ROOT, image.image_file.path), '200', crop='center', format='PNG', quality=99)
                #image_data = open(os.path.join(settings.MEDIA_ROOT, image.image_file.path), "rb").read()
            elif width and not height:
                # W and no h parameter : thumbnail with fixed width
                image_file = get_thumbnail(os.path.join(settings.MEDIA_ROOT, image.image_file.path), width, crop='center', format='PNG', quality=99)
            elif not width and height:
                # H and no w parameter : thumbnail with fixed height
                image_file = get_thumbnail(os.path.join(settings.MEDIA_ROOT, image.image_file.path), 'x'+height, crop='center', format='PNG', quality=99)
            elif width and height:
                # H and no w parameter : thumbnail with fixed height
                image_file = get_thumbnail(os.path.join(settings.MEDIA_ROOT, image.image_file.path), width+'x'+height, crop='center', format='PNG', quality=99)
            elif maxwidth and maxheight:
                # We compare wanted format with image's format and we resize W or H
                if (float(image.image_file.width)/float(image.image_file.height)) > (float(maxwidth)/float(maxheight)):
                    # Image ratio > wanted ratio, we resize with fixed width
                    image_file = get_thumbnail(os.path.join(settings.MEDIA_ROOT, image.image_file.path), str(maxwidth), crop='center', format='PNG', quality=99)
                else:
                    # Image ratio < wanted ratio, we resize with fixed height
                    image_file = get_thumbnail(os.path.join(settings.MEDIA_ROOT, image.image_file.path), 'x'+str(maxheight), crop='center', format='PNG', quality=99)
            
            # We display directly the image file or, if asked, the <img> html tag
            imgtag = request.GET.get("imgtag") or None
            if imgtag:
                imgtag = {'true': True, 'false': False, "0": False, "1": True}.get(imgtag.lower())
            if imgtag:
                return HttpResponse('<img src="' + image_file.url + '" />')
            else:
                if image_file:
                    image_data = image_file.read()
        return HttpResponse(image_data, mimetype="image/png")



class MetadataComposerPlayer(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_player.html"
    
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerPlayer, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", ldt_id=None, **kwargs):
        self.branding = branding
        
        if not ldt_id:
            return HttpResponseBadRequest("ldt_id parameter must be set.")
        
        context = self.get_context_dict(request)
        context.update({"ldt_id": ldt_id})
        return self.render_to_response(context)



class MetadataComposerPreviewPlayer(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_preview_player.html"
    
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerPreviewPlayer, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", ldt_id=None, **kwargs):
        self.branding = branding
        
        if not ldt_id:
            return HttpResponseBadRequest("ldt_id parameter must be set.")
        
        context = self.get_context_dict(request)
        context.update({"ldt_id": ldt_id})
        return self.render_to_response(context)



class MetadataComposerEdit(TemplateResponseMixin, MetadataComposerContextView):
    
    def get_template_names(self):
        return "metadatacomposer_edit.html"
    
    @method_decorator(login_required)
    @method_decorator(never_cache)
    def dispatch(self, *args, **kwargs):
        return super(MetadataComposerEdit, self).dispatch(*args, **kwargs)
    
    def get(self, request, branding="iri", ldt_id=None, **kwargs):
        self.branding = branding
        
        project = None
        if not ldt_id:
            return HttpResponseBadRequest("ldt_id parameter must be set.")
        else:
            project = get_object_or_404(Project.safe_objects, ldt_id=ldt_id)
        if project:
            if not check_object_perm_for_user(project, "change_project", request.user):
                return HttpResponseBadRequest("Current user has no right to edit this project.")
        
        context = self.get_context_dict(request)
        context.update({"ldt_id": ldt_id, "project":project})
        return self.render_to_response(context)



class MetadataComposerNewProject(View):
    
    @method_decorator(login_required)
    def get(self, request, branding="iri", iri_id=None, **kwargs):
        self.branding = branding
        # Get content and creator user
        content = get_object_or_404(Content.safe_objects, iri_id=iri_id)
        contents = [ content, ]
        u = request.user
        # Create project
        project = Project.create_project(title="Project on " + content.title + " " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
                                         user=u, contents=contents, 
                                         description='', 
                                         set_icon=False)
        # Set project state to "published" and save
        assign('view_project', u, project)
        assign('change_project', u, project)
        project.publish()
        # Since the project is published by default, we assign permission for the everyone group
        #assign('ldt_utils.view_project', Group.objects.get(name=settings.PUBLIC_GROUP_NAME), project)
        #assign('view_project', Group.objects.get(name=settings.PUBLIC_GROUP_NAME), project)

        # Redirect to project edition in composer interface
        return redirect('composer_edit', branding=branding, ldt_id=project.ldt_id)



class MetadataComposerDuplicateProject(View):
    
    @method_decorator(login_required)
    def get(self, request, branding="iri", **kwargs):
        self.branding = branding
        ldt_id = request.GET.get("ldt_id") or None
        if ldt_id:
            proj = get_object_or_404(Project, ldt_id=ldt_id)
            u = request.user
            # Create project
            newproj = Project.create_project(title="Copy of " + proj.title,
                         user=u, contents=proj.contents.all(), 
                         description=proj.description, 
                         set_icon=False)
            # Copy project (copies xml/ldt data and updates the xml with the good new ldt_id)
            newproj = copy_ldt(proj, newproj, u)
            # Save ldt content and set project state to "published" and save
            newproj.state = 2
            newproj.save()
            assign('view_project', u, newproj)
            assign('change_project', u, newproj)
            # Since the project is published by default, we assign permission for the everyone group
            assign('ldt_utils.view_project', Group.objects.get(name=settings.PUBLIC_GROUP_NAME), newproj)
        
        return redirect("composer_project_list", branding=branding)



