from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import Group
from django.core.urlresolvers import reverse
from django.forms.models import model_to_dict
from django.forms.util import ErrorList
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.utils.translation import ugettext as _, ungettext
from ldt.ldt_utils.forms import ContentForm, MediaForm
from guardian.shortcuts import remove_perm
from ldt.ldt_utils.models import Content, Media, Project
from ldt.security.utils import assign_perm_to_obj, add_change_attr, get_userlist, get_userlist_model
from ldt.security.cache import cached_assign
from ldt.user.forms import PictureForm
from urllib2 import urlparse
import ldt.utils.path as ldt_utils_path
import logging
import mimetypes
import os
import urllib2
import subprocess
import re
import datetime
import math

def write_content_base(request, iri_id=None): 

    if iri_id:        
        instance_content = Content.safe_objects.get(iri_id=iri_id) #@UndefinedVariable
        instance_media = instance_content.media_obj
        logging.debug("write_content_base : valid form: for instance : media -> " + repr(instance_media) + " content : for instance : " + repr(instance_content)) #@UndefinedVariable
    else:
        logging.debug("No iri_id") #@UndefinedVariable
        instance_content = None
        instance_media = None
    
    form_status = 'none'        
    if request.method == "POST":
        
        if instance_content is not None:
            content_instance_val = model_to_dict(instance_content, exclude=ContentForm.Meta.exclude)
        else:
            content_instance_val = {}
        
        if instance_media is not None:
            media_instance_val = model_to_dict(instance_media, exclude=MediaForm.Meta.exclude)
        else:
            media_instance_val = {}
        #add prefix
        
        def add_prefix(dict, prefix):
            for key, value in dict.items():
                dict['%s-%s' % (prefix, key)] = value
                del(dict[key])
        
        add_prefix(content_instance_val, "content")
        add_prefix(media_instance_val, "media")     
        
        for k in request.POST.keys():
            value = request.POST.get(k)
            content_instance_val[k] = value
            media_instance_val[k] = value
            
        content_instance_val['read_list'] = request.POST.getlist('read_list')
        content_instance_val['write_list'] = request.POST.getlist('write_list')
        content_instance_val['share'] = request.POST.get('share', False)

        content_form = ContentForm(content_instance_val, prefix="content", instance=instance_content)
        media_form = MediaForm(media_instance_val, request.FILES, prefix="media", instance=instance_media)
        picture_form = PictureForm(None, request.POST, request.FILES)
        
        if request.user.is_staff:
            content_form.fields['front_project'].queryset = Project.objects.filter(contents__in=[instance_content])

        
        media_valid = media_form.is_valid()
        content_valid = content_form.is_valid()
        picture_valid = picture_form.is_valid()
        
        logging.debug("write_content_base : valid form: for instance : " + repr(instance_media) + " -> media " + str(media_valid) + " content : for instance : " + repr(instance_content) + " : " + str(content_valid)) #@UndefinedVariable
        
        if media_valid and content_valid and picture_valid:

            # see if media must be created
            cleaned_data = {}
            cleaned_data.update(media_form.cleaned_data)
            
            media_input_type = content_form.cleaned_data["media_input_type"]
            
            if media_input_type == "none":
                media = None
            elif media_input_type == "link":
                media = content_form.cleaned_data["media_obj"]
                created = False
            elif media_input_type == "create":
                del cleaned_data["media_file"]
                if not cleaned_data['videopath']:
                    cleaned_data['videopath'] = settings.STREAM_URL
                # if the source is already http:// or rtmp:// we don't have to add STREAM_URL
                if cleaned_data['src'].startswith("rtmp://") or cleaned_data['src'].startswith("http://"):
                    cleaned_data['videopath'] = ''
                media, created = Media.objects.get_or_create(src=cleaned_data['src'], defaults=cleaned_data) #@UndefinedVariable
            elif media_input_type == "url" or media_input_type == "upload" :
                # copy file
                #complet src
                destination_file = None
                source_file = None
                try:
                    if media_input_type == "url":
                        url = cleaned_data["external_src_url"]
                        source_file = urllib2.urlopen(url)
                        source_filename = source_file.info().get('Content-Disposition', None)
                        if not source_filename:
                            source_filename = urlparse.urlparse(url).path.rstrip("/").split('/')[-1]
                    elif media_input_type == "upload":
                        #source_file = request.FILES['media-media_file']
                        # At this point the file has already be uploaded thanks to the upload view, and original file name is sent through a post var
                        source_filename = request.POST["media-local_file_name"]
                    
                    source_filename = ldt_utils_path.sanitize_filename(source_filename)
                    destination_filepath = os.path.join(settings.STREAM_PATH, source_filename)
                    base_source_filename = source_filename
                    extension = base_source_filename.split(".")[-1]
                    if extension == base_source_filename:
                        extension = ""
                        base_basename_filename = base_source_filename
                    else:
                        base_basename_filename = base_source_filename[:-1 * (len(extension) + 1)]
                    i = 0
                    
                    while os.path.exists(destination_filepath):
                        base_source_filename = "%s.%d.%s" % (base_basename_filename, i, extension)
                        destination_filepath = os.path.join(settings.STREAM_PATH, base_source_filename)
                        i += 1
                    
                    if media_input_type == "url":
                        # we upload the file if we are in url case
                        destination_file = open(destination_filepath, "wb")
                        chunck = source_file.read(2048)
                        while chunck:
                            destination_file.write(chunck)
                            chunck = source_file.read(2048)
                        
                    elif media_input_type == "upload":
                        # The media file has been uploaded in the session temp folder 
                        # so we just have to move to the regular folder and rename it.
                        if os.path.exists(os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME] + "/", source_filename)):
                            os.rename(os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME] + "/", source_filename), os.path.join(settings.STREAM_PATH, base_source_filename))
                    
                    
                    src_prefix = settings.STREAM_SRC_PREFIX.rstrip("/")
                    if len(src_prefix) > 0:
                        cleaned_data["src"] = src_prefix + "/" + base_source_filename
                    else:
                        cleaned_data["src"] = base_source_filename
                    
                    
                except Exception as inst:
                    logging.debug("write_content_base : POST error when processing file:" + str(inst)) #@UndefinedVariable
                    form_status = "error"
                    #set error for form
                    if media_input_type == "url":
                        errors = media_form._errors.setdefault("external_src_url", ErrorList())
                        errors.append(_("Problem when downloading file from url : ") + url)
                    elif media_input_type == "upload":
                        errors = media_form._errors.setdefault("media_file", ErrorList())
                        errors.append(_("Problem when uploading file : ") + str(inst))
                finally:
                    if media_input_type == "url":
                        if destination_file:
                            destination_file.close()
                        if source_file:
                            source_file.close()
                
                
                if form_status != "error":
                    #try:
                    del cleaned_data["media_file"]
                    if not cleaned_data['videopath']:
                        cleaned_data['videopath'] = settings.STREAM_URL
                    mimetype = cleaned_data.get('mimetype_field', None)
                    if not mimetype:
                        mimetype = mimetypes.guess_type(cleaned_data['src'])
                    cleaned_data['mimetype_field'] = mimetype
                    media, created = Media.safe_objects.get_or_create(src=cleaned_data['src'], defaults=cleaned_data) #@UndefinedVariable
                    cached_assign('view_media', request.user, media)
                else:
                    media = None
                    

            if media and not created:
                for attribute in ('external_id', 'external_permalink', 'external_publication_url', 'external_src_url', 'media_creation_date', 'videopath', 'duration', 'description', 'title', 'front_project'):
                    setattr(media, attribute, cleaned_data.get(attribute))
                mimetype = cleaned_data.get('mimetype_field', None)
                if not mimetype:
                    mimetype = mimetypes.guess_type(media.src)
                media.mimetype_field = mimetype
                cached_assign('view_media', request.user, media)
                cached_assign('change_media', request.user, media)
                media.save()
            
            if form_status != "error": 
                content_defaults = {}
                content_defaults.update(content_form.cleaned_data)
                content_defaults['media_obj'] = media
                               
                for key in ["media_input_type", "groups", "is_public", "read_list", "write_list", "share" ]:
                    del content_defaults[key]
                     
                content, created = Content.safe_objects.get_or_create(iri_id=content_form.cleaned_data['iri_id'], defaults=content_defaults) #@UndefinedVariable
                
                if not created and not request.user.has_perm('ldt_utils.change_content', content):
                    raise AttributeError("%s is not allowed to change content %s" % (request.user, content))
                
                cached_assign('change_content', request.user, content)
                cached_assign('view_content', request.user, content)
                everyone = Group.objects.get(name=settings.PUBLIC_GROUP_NAME)
                
                if content_form.cleaned_data['is_public']:
                    cached_assign('view_content', everyone, content)
                    if media:
                        cached_assign('view_media', everyone, media)
                else:
                    remove_perm('ldt_utils.view_content', everyone, content)
                    remove_perm('ldt_utils.view_media', everyone, media)
                    assign_perm_to_obj(content, content_form.cleaned_data['read_list'], content_form.cleaned_data['write_list'], request.user)
                    if media:
                        assign_perm_to_obj(media, content_form.cleaned_data['read_list'], content_form.cleaned_data['write_list'], request.user)
                if not created:
                    for attribute in ('iriurl', 'title', 'description', 'duration', 'content_creation_date', 'tags', 'media_obj'):
                        setattr(content, attribute, content_defaults[attribute])
                        
                    if request.user.is_staff and content_defaults.has_key('front_project'):
                        content.front_project = content_defaults['front_project']
                    
                content.save()
                picture_form.model = content
                picture_form.save()  

                form_status = 'saved'
                media_form = MediaForm(instance=media, prefix="media")
                content_form = ContentForm(instance=content, prefix="content")
                picture_form = PictureForm()
        else:
            form_status = 'error'
    else:
        form_status = 'empty'
        initial = { 'media_input_type':"link"}
        if instance_content:
            initial['is_public'] = instance_content.is_public
        else:
            initial['is_public'] = False
        
        content_form = ContentForm(prefix="content", instance=instance_content, initial=initial)
        media_form = MediaForm(prefix="media", instance=instance_media) 
        picture_form = PictureForm() 
                
        if instance_content is not None:
            content_form.media_input_type = "link"
                   
            if request.user.is_staff:
                content_form.fields['front_project'].queryset = Project.objects.filter(contents__in=[instance_content])
    
    return content_form, media_form, picture_form, form_status

@login_required
def write_content(request, iri_id=None):    
    submit_action = request.REQUEST.get("submit_button", False) 
    member_list = admin_list = []
    
    if submit_action == "prepare_delete": 
        errors, titles = prepare_delete_content(request, iri_id)
        if errors and len(errors) > 0:
            message = ungettext("There is %(count)d error when deleting content", "There are %(count)d errors when deleting content", len(errors)) % { 'count': len(errors)}
            title_msg = _('title error deleting content')
        else:
            message = _("Confirm delete content %(titles)s") % { 'titles' : ",".join(titles) }
            title_msg = _("confirm delete content")
        return render_to_response('ldt/ldt_utils/error_confirm.html', {'errors':errors, 'message':message, 'title': title_msg}, context_instance=RequestContext(request))  
    elif submit_action == "delete":
        delete_content(request, iri_id)
        form_status = "deleted"
        content_form = ContentForm()
        media_form = MediaForm()
        picture_form = PictureForm()
    else:
        content_form, media_form, picture_form, form_status = write_content_base(request, iri_id)
        if iri_id:
            member_list, admin_list = get_userlist_model(Content.objects.get(iri_id=iri_id), request.user)
        
    if iri_id:
        create_content_action = reverse('ldt.ldt_utils.views.content.write_content', kwargs={'iri_id':iri_id})
        img_container = content_form.instance
    else:        
        create_content_action = reverse('ldt.ldt_utils.views.content.write_content')
        img_container = ''
    
    session_key = request.COOKIES[settings.SESSION_COOKIE_NAME]
    cookie_name = settings.SESSION_COOKIE_NAME
    content_form.fields["media_obj"].queryset = Media.safe_objects.all()
    
    return render_to_response('ldt/ldt_utils/create_content.html', {'content_form': content_form, 'media_form': media_form, 'form_status': form_status, 'create_content_action': create_content_action,
                                                                    'elem_list': get_userlist(request.user), 'member_list': member_list, 'admin_list': admin_list,  'iri_id': iri_id, 'session_key':session_key,
                                                                    'cookie_name':cookie_name, 'img_container': img_container, 'profile_picture_form': picture_form}, context_instance=RequestContext(request))

@login_required
def prepare_delete_content(request, iri_id=None): 
    errors = []
    titles = []
    if not iri_id:
        iri_id = request.REQUEST.get("iri_id", None)
        
    if iri_id:
        for content in Content.safe_objects.filter(iri_id=iri_id): 
            titles.append(unicode(content.title))
            projects = content.project_set.all()
            projects_nb = len(projects)
            if projects_nb > 0:
                project_titles = map(lambda p: unicode(p.title), projects)
                errors.append(ungettext("Content '%(title)s' is referenced by this project : %(project_titles)s. Please delete it beforehand.", "Content '%(title)s' is referenced by %(count)d projects: %(project_titles)s. Please delete them beforehand.", projects_nb) % {'title':unicode(content.title), 'count':projects_nb, 'project_titles': ",".join(project_titles)})
 
    return errors, titles


@login_required
def delete_content(request, iri_id=None):
    if not iri_id:
        iri_id = request.REQUEST.get("iri_id", None)
        
    if iri_id:
        Content.safe_objects.get(iri_id=iri_id).delete()


def upload(request):
    if request.method == 'POST':
        for field_name in request.FILES:
            # We get the file name
            source_file = request.FILES[field_name]
            source_filename = source_file.name
            # We sanitize the file name : no space, only lower case.
            source_filename = ldt_utils_path.sanitize_filename(source_filename)
            # We create the session temp folder if necessary
            if not os.path.exists(os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME])):
                os.makedirs(os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME]))
            destination_filepath = os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME] + "/", source_filename)
            # We delete the existing file if necessary
            if os.path.exists(destination_filepath):
                os.remove(destination_filepath)
            
            destination_file = open(destination_filepath, "wb")
            
            for chunk in source_file.chunks():
                destination_file.write(chunk)
            destination_file.close()
            
        # indicate that everything is OK for SWFUpload
        return HttpResponse("ok", mimetype="text/plain")
    else:
        return HttpResponse("notok", mimetype="text/plain")

def remove_temp_file(request):
    # The filename arrives with a GET var.
    file_path = os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME] + "/", ldt_utils_path.sanitize_filename(request.GET["filename"]))
    if os.path.exists(file_path):
        os.remove(file_path)
    return HttpResponse("remove ok", mimetype="text/plain")

def get_duration(request):
    try:
        # The filename arrives with a GET var.
        file_path = os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME] + "/", ldt_utils_path.sanitize_filename(request.GET["filename"]))
        if hasattr(settings, 'FFMPEG_PATH') and os.path.exists(file_path):
            output = str(subprocess.Popen([settings.FFMPEG_PATH, "-i", file_path], stderr=subprocess.PIPE).communicate()[1])
            m = re.search("Duration:\s*?([\d.:]+)", output, re.M)
            dur_arr = m.group(1).split(":")
            td = datetime.timedelta(hours=int(dur_arr[0]), minutes=int(dur_arr[1]), seconds=float(dur_arr[2]))
            str_duration = str((td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6) / 10 ** 3)
            return HttpResponse(str_duration, mimetype="text/plain")
        else:
            return HttpResponse("", mimetype="text/plain")
    except Exception as inst:
        return HttpResponse(str(inst), mimetype="text/plain")

    
@login_required
def contents_filter(request, filter): 
    if filter and len(filter) > 0 and filter[0] == '_':
        filter = filter[1:]
    
    num_page = 0
    if request.GET.has_key('num_page'):
        num_page = int(request.GET["num_page"])
    
    # We paginate the content list, in case of filter or not
    if filter:
        content_nb = Content.safe_objects.filter(title__icontains=filter).count()
        nb_ct_pages = int(math.ceil(content_nb / settings.LDT_MAX_CONTENTS_PER_PAGE))
        content_list = Content.safe_objects.filter(title__icontains=filter)[(num_page*settings.LDT_MAX_CONTENTS_PER_PAGE):((num_page+1)*settings.LDT_MAX_CONTENTS_PER_PAGE)] #@UndefinedVariable
    else:
        content_nb, nb_ct_pages, content_list = get_contents_page(num_page, request.user)
    #Change attributes with object permissions
    content_list = add_change_attr(request.user, content_list)
    
    is_gecko = ((request.META['HTTP_USER_AGENT'].lower().find("firefox")) > -1);
    return render_to_response("ldt/ldt_utils/partial/contentslist.html",
                              {'contents': content_list, 'nb_ct_pages': nb_ct_pages, 'content_nb': content_nb, 'current_content_page':float(num_page),
                               'is_gecko': is_gecko
                               },
                              context_instance=RequestContext(request))
    

def get_contents_page(num_page, user):
    content_nb = float(Content.safe_objects.count()) #@UndefinedVariable
    nb_ct_pages = int(math.ceil(content_nb / settings.LDT_MAX_CONTENTS_PER_PAGE))
    content_list = add_change_attr(user, Content.safe_objects.all()[(num_page*settings.LDT_MAX_CONTENTS_PER_PAGE):((num_page+1)*settings.LDT_MAX_CONTENTS_PER_PAGE)]) #@UndefinedVariable
    return content_nb, nb_ct_pages, content_list
