from __future__ import unicode_literals

import hashlib
import inspect

from django import forms
from django.conf import settings
from django.core.cache import cache
from django.core.files.uploadedfile import UploadedFile
from django.forms.boundfield import BoundField
from django.utils.html import format_html


def cache_key(name):
    return "imagefield-cache:%s" % hashlib.sha256(name.encode()).hexdigest()


def cache_timeout():
    value = settings.IMAGEFIELD_CACHE_TIMEOUT
    return value() if callable(value) else value


class PPOIWidget(forms.HiddenInput):
    class Media:
        css = {"screen": ("imagefield/ppoi.css",)}
        js = ("imagefield/ppoi.js",)


class PreviewAndPPOIMixin(object):
    def render(self, name, value, attrs=None, **kwargs):
        attrs = attrs or {}
        # Can be dropped once we drop support for Django<2.1
        attrs.setdefault("accept", "image/*")
        widget = super(PreviewAndPPOIMixin, self).render(
            name, value, attrs=attrs, **kwargs
        )

        # name does not require a file, .url does
        if not getattr(value, "name", "") or isinstance(value, UploadedFile):
            return widget

        # Find our BoundField so that we may access the form instance to
        # finally determine the ID attribute of our PPOI field.
        frame = inspect.currentframe()
        while frame:
            boundfield = frame.f_locals.get("self")
            if isinstance(boundfield, BoundField):
                break
            frame = frame.f_back

        if frame is None:  # pragma: no cover
            # Bail out. I have absolutely no idea why this would ever happen.
            return widget

        del frame

        try:
            ppoi = boundfield.form[boundfield.field.widget.ppoi_field].auto_id
        except (AttributeError, KeyError, TypeError):
            ppoi = ""

        context = value._process_context(self.processors)
        key = cache_key(context.name)
        url = value.storage.url(context.name)
        if not cache.get(key):
            try:
                value.process(self.processors)
                cache.set(key, 1, timeout=cache_timeout())
            except Exception:
                # Avoid crashing here since it will not be possible to even
                # replace corrupted images otherwise.
                pass

        return format_html(
            '<div class="imagefield" data-ppoi-id="{ppoi}">'
            '<div class="imagefield-preview">'
            '<img class="imagefield-preview-image" src="{url}" alt=""/>'
            "</div>"
            '<div class="imagefield-widget">{widget}</div>'
            "</div>",
            widget=widget,
            url=url,
            ppoi=ppoi,
        )


def with_preview_and_ppoi(widget, **attrs):
    return type(
        str("%sWithPreviewAndPPOI" % widget.__name__),
        (PreviewAndPPOIMixin, widget),
        dict(attrs, __module__="imagefield.widgets"),
    )
