import io
from pathlib import Path

import django.forms as forms
from django.http import JsonResponse
from django.shortcuts import render
from django.template import loader
from django.urls import reverse
from django.views.decorators.csrf import ensure_csrf_cookie
from PIL import Image

import piexif

import troggle.settings as settings
from troggle.core.utils import WriteAndCommitError, write_and_commit

from .auth import login_required_if_public

MAX_IMAGE_WIDTH = 1000
MAX_IMAGE_HEIGHT = 800

THUMBNAIL_WIDTH = 200
THUMBNAIL_HEIGHT = 200

"""This is all written by Martin Green, and completely undocumented.

It uses a lot of opinionated Django default magic, none of which I am familiar with.
Philip
"""

def get_dir(path):
    "From a path sent from urls.py, determine the directory."
    # todo re-write this to use modern pathlib functions
    if "/" in path:
        return path.rsplit("/", 1)[0]
    else:
        return ""


def image_selector(request, path):
    """Returns available images"""
    directory = get_dir(path)
    print(f" - image selector {directory=} {path=}")
    thumbnailspath = Path(settings.EXPOWEB) / directory / "t"
    thumbnails = []
    if thumbnailspath.is_dir():
        for f in thumbnailspath.iterdir():
            if f.is_file():
                if directory:
                    base = f"{directory}/"
                else:
                    base = ""
                thumbnail_url = reverse("expopage", args=[f"{base}t/{f.name}"])
                name_base = f.name.rsplit(".", 1)[0]
                page_path_base = Path(settings.EXPOWEB) / directory / "l"
                if (page_path_base / (f"{name_base}.htm")).is_file():
                    page_url = reverse("expopage", args=[f"{base}l/{name_base}.htm"])
                else:
                    page_url = reverse("expopage", args=[f"{base}l/{name_base}.html"])

                thumbnails.append({"thumbnail_url": thumbnail_url, "page_url": page_url})

    return render(request, "image_selector.html", {"thumbnails": thumbnails})
    
def reorient_image(img, exif_dict):
     if piexif.ImageIFD.Orientation in exif_dict["0th"]:
            print(exif_dict)
            orientation = exif_dict["0th"].pop(piexif.ImageIFD.Orientation)
            
            if orientation == 2:
                img = img.transpose(Image.FLIP_LEFT_RIGHT)
            elif orientation == 3:
                img = img.rotate(180)
            elif orientation == 4:
                img = img.rotate(180).transpose(Image.FLIP_LEFT_RIGHT)
            elif orientation == 5:
                img = img.rotate(-90, expand=True).transpose(Image.FLIP_LEFT_RIGHT)
            elif orientation == 6:
                img = img.rotate(-90, expand=True)
            elif orientation == 7:
                img = img.rotate(90, expand=True).transpose(Image.FLIP_LEFT_RIGHT)
            elif orientation == 8:
                img = img.rotate(90, expand=True)
     return img


@login_required_if_public
@ensure_csrf_cookie
def new_image_form(request, path):
    """Manages a form to upload new images"""
    directory = get_dir(path)
    if request.method == "POST":
        form = NewWebImageForm(request.POST, request.FILES, directory=directory)
        if form.is_valid():
            f = request.FILES["file_"]
            binary_data = io.BytesIO()
            for chunk in f.chunks():
                binary_data.write(chunk)
            i = Image.open(binary_data)
            if "exif" in i.info:
                exif_dict = piexif.load(i.info["exif"])
                i = reorient_image(i, exif_dict)
                exif_dict['Exif'][41729] = b'1'
                # can crash here with bad exif data
                try:
                    exif = piexif.dump(exif_dict)
                except:
                    exif = None
            else:
                exif = None
            
            width, height = i.size
            if width > MAX_IMAGE_WIDTH or height > MAX_IMAGE_HEIGHT:
                scale = max(width / MAX_IMAGE_WIDTH, height / MAX_IMAGE_HEIGHT)
                i = i.resize((int(width / scale), int(height / scale)), Image.ANTIALIAS)
            tscale = max(width / THUMBNAIL_WIDTH, height / THUMBNAIL_HEIGHT)
            thumbnail = i.resize((int(width / tscale), int(height / tscale)), Image.ANTIALIAS)
            
            ib = io.BytesIO()
            i = i.convert('RGB')
            i.save(ib, format="jpeg", quality = 75)
            tb = io.BytesIO()
            thumbnail = thumbnail.convert('RGB')
            thumbnail.save(tb, format="jpeg", quality = 70)
            image_rel_path, thumb_rel_path, desc_rel_path = form.get_rel_paths()
            image_page_template = loader.get_template("image_page_template.html") # this is the .html file in the /l/ folder
            image_page = image_page_template.render(
                {
                    "header": form.cleaned_data["header"],
                    "description": form.cleaned_data["description"],
                    "photographer": form.cleaned_data["photographer"],
                    "year": form.cleaned_data["year"],
                    "filepath": f"/{image_rel_path}",
                }
            )
            image_path, thumb_path, desc_path = form.get_full_paths()
            # Create directories if required
            for full_path in image_path, thumb_path, desc_path:
                print(full_path, full_path.parent)
                full_path.parent.mkdir(parents=True, exist_ok=True)
            try:
                change_message = form.cleaned_data["change_message"]
                write_and_commit(
                    [
                        (desc_path, image_page, "utf-8"),
                        (image_path, ib.getbuffer(), False),
                        (thumb_path, tb.getbuffer(), False),
                    ],
                    f"{change_message} - online adding of an image",
                )
            except WriteAndCommitError as e:
                return JsonResponse({"error": e.message})
            linked_image_template = loader.get_template("linked_image_template.html")
            html_snippet = linked_image_template.render(
                {"thumbnail_url": f"/{thumb_rel_path}", "page_url": f"/{desc_rel_path}"}, request
            )
            return JsonResponse({"html": html_snippet})
    else:
        form = NewWebImageForm(directory=directory)
    template = loader.get_template("new_image_form.html")
    htmlform = template.render({"form": form, "path": path}, request)
    return JsonResponse({"form": htmlform})


class NewWebImageForm(forms.Form):
    """The form used by the editexpopage function"""

    header = forms.CharField(
        widget=forms.TextInput(
            attrs={"size": "60", "placeholder": "Enter title (displayed as a header and in the tab)"}
        )
    )
    file_ = forms.FileField()
    description = forms.CharField(
        widget=forms.Textarea(attrs={"cols": 80, "rows": 20, "placeholder": "Describe the photo (using HTML)"})
    )
    photographer = forms.CharField(
        widget=forms.TextInput(attrs={"size": "60", "placeholder": "Photographers name"}), required=False
    )
    year = forms.CharField(
        widget=forms.TextInput(attrs={"size": "60", "placeholder": "Year photo was taken"}), required=False
    )
    change_message = forms.CharField(
        widget=forms.Textarea(attrs={"cols": 80, "rows": 3, "placeholder": "Descibe the change made (for git)"})
    )

    def __init__(self, *args, **kwargs):
        self.directory = Path(kwargs.pop("directory"))
        super(forms.Form, self).__init__(*args, **kwargs)

    def get_rel_paths(self):
        f = self.cleaned_data["file_"]
        return [
            self.directory / "i" / (f.name.rsplit(".", 1)[0] + ".jpg"),
            self.directory / "t" / (f.name.rsplit(".", 1)[0] + ".jpg"),
            self.directory / "l" / (f.name.rsplit(".", 1)[0] + ".html"),
        ]

    def get_full_paths(self):
        return [Path(settings.EXPOWEB) / x for x in self.get_rel_paths()] # this is where we would want to insert the /caveid/ ?!

    def clean_file_(self):
        for rel_path, full_path in zip(self.get_rel_paths(), self.get_full_paths()):
            if full_path.exists():
                raise forms.ValidationError(f"File already exists in {rel_path}")
        return self.cleaned_data["file_"]


class HTMLarea(forms.Textarea):
    """This is called from CaveForm in core/forms.py which is called from core/views/caves.py when editing a cave description
    (and similarly for Entrance Descriptions). It is alls called from core/views/expo.py when editing expo (i.e. handbook) pages.
    """
    template_name = "widgets/HTMLarea.html"

    def __init__(self, *args, **kwargs):
        self.preview = kwargs.pop("preview", False)
        super(forms.Textarea, self).__init__(*args, **kwargs)

    def get_context(self, name, value, attrs):
        c = super(forms.Textarea, self).get_context(name, value, attrs)
        c["preview"] = self.preview
        return c
