# This file is part of the MapProxy project.
# Copyright (C) 2012 Omniscale <http://omniscale.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from PIL import Image, ImageDraw
from mapproxy.srs import SRS, make_lin_transf, _SRS
from mapproxy.image import ImageResult, BaseImageResult
from mapproxy.image.opts import create_image
from mapproxy.util.geom import flatten_to_polygons
from mapproxy.util.bbox import BBOX
from mapproxy.util.coverage import GeomCoverage


def mask_image_result_from_coverage(image_result: BaseImageResult, bbox, bbox_srs, coverage,
                                    image_opts=None) -> ImageResult:
    if image_opts is None:
        image_opts = image_result.image_opts
    img = image_result.as_image()
    img = mask_image(img, bbox, bbox_srs, coverage)
    result = create_image(img.size, image_opts)
    result.paste(img, (0, 0), img)
    return ImageResult(result, image_opts=image_opts)


def mask_image(img: Image.Image, bbox, bbox_srs, coverage) -> Image.Image:
    geom = mask_polygons(bbox, SRS(bbox_srs), coverage)
    mask = image_mask_from_geom(img.size, bbox, geom)
    img = img.convert('RGBA')
    img.paste((255, 255, 255, 0), (0, 0), mask)
    return img


def mask_polygons(bbox: BBOX, bbox_srs: _SRS, coverage: GeomCoverage):
    coverage = coverage.transform_to(bbox_srs)
    coverage = coverage.intersection(bbox, bbox_srs)
    return flatten_to_polygons(coverage.geom)


def image_mask_from_geom(size, bbox, polygons) -> Image.Image:
    mask = Image.new('L', size, 255)
    if len(polygons) == 0:
        return mask

    transf = make_lin_transf(bbox, (0, 0) + size)

    # use negative ~.1 pixel buffer
    buffer = -0.1 * min((bbox[2] - bbox[0]) / size[0], (bbox[3] - bbox[1]) / size[1])

    draw = ImageDraw.Draw(mask)

    def draw_polygon(p):
        draw.polygon([transf(coord) for coord in p.exterior.coords], fill=0)
        for ring in p.interiors:
            draw.polygon([transf(coord) for coord in ring.coords], fill=255)

    for p in polygons:
        # little bit smaller polygon does not include touched pixels outside coverage
        buffered = p.buffer(buffer, resolution=1, join_style=2)

        if buffered.is_empty:  # can be empty after negative buffer
            continue

        if buffered.geom_type == 'MultiPolygon':
            # negative buffer can turn polygon into multipolygon
            for p in buffered.geoms:
                draw_polygon(p)
        else:
            draw_polygon(buffered)

    return mask
