# Copyright 2019 - Remi Ferrand

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

"""This plugin controls the generation of a ZIP archive for a gallery

If the ``zip_gallery`` setting is set, it contains the location of a zip
archive with all original images of the corresponding directory.

To ignore a ZIP gallery generation for a particular album, put
a ``.nozip_gallery`` file next to it in its parent folder. Only the existence
of this ``.nozip_gallery`` file is tested.  If no ``.nozip_gallery`` file is
present, then make a ZIP archive with all media files.

See :ref:`compatibility with the encrypt plugin <compatibility-with-encrypt>`.
"""

import logging
import os
import zipfile
from functools import cached_property
from os.path import isfile, join

from sigal import signals
from sigal.gallery import Album

logger = logging.getLogger(__name__)


def _should_generate_album_zip(album):
    """Checks whether a `.nozip_gallery` file exists in the album folder"""
    nozipgallerypath = os.path.join(album.src_path, ".nozip_gallery")
    return not os.path.isfile(nozipgallerypath)


def _generate_album_zip(album):
    """Make a ZIP archive with all media files and return its path.

    If the ``zip_gallery`` setting is set,it contains the location of a zip
    archive with all original images of the corresponding directory.
    """

    zip_gallery = album.settings["zip_gallery"]

    if zip_gallery and len(album) > 0:
        zip_gallery = zip_gallery.format(album=album)
        archive_path = join(album.dst_path, zip_gallery)
        if album.settings.get("zip_skip_if_exists", False) and isfile(archive_path):
            logger.debug("Archive %s already created, passing", archive_path)
            return zip_gallery

        archive = zipfile.ZipFile(archive_path, "w", allowZip64=True)
        attr = (
            "src_path" if album.settings["zip_media_format"] == "orig" else "dst_path"
        )

        for p in album:
            path = getattr(p, attr)
            try:
                archive.write(path, os.path.split(path)[1])
            except OSError as e:
                logger.warn("Failed to add %s to the ZIP: %s", p, e)

        archive.close()
        logger.debug("Created ZIP archive %s", archive_path)
        return zip_gallery

    return False


def generate_album_zip(album):
    """Checks for .nozip_gallery file in album folder.
    If this file exists, no ZIP archive is generated.
    If the file is absent, make a ZIP archive with all media files and return its path.

    If the ``zip_gallery`` setting is set,it contains the location of a zip
    archive with all original images of the corresponding directory.
    """

    # check if ZIP file generation as been disabled by .nozip_gallery file
    if not _should_generate_album_zip(album):
        logger.info(
            "Ignoring ZIP gallery generation for album '%s' because of present "
            ".nozip_gallery file",
            album.name,
        )
        return False

    return _generate_album_zip(album)


def nozip_gallery_file(album, settings=None):
    """Filesystem based switch to disable ZIP generation for an Album"""
    Album.zip = cached_property(generate_album_zip)
    Album.zip.__set_name__(Album, "zip")


def check_settings(gallery):
    if gallery.settings["zip_gallery"] and not isinstance(
        gallery.settings["zip_gallery"], str
    ):
        logger.error("'zip_gallery' should be set to a filename")
        gallery.settings["zip_gallery"] = False


def register(settings):
    signals.album_initialized.connect(nozip_gallery_file)
    signals.gallery_initialized.connect(check_settings)
