# Copyright 2019 Akiomi Kamakura
#
# 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.


import math

import numpy as np
from PIL import Image, ImageChops

from pilgram import util
from pilgram.css.blending.alpha import alpha_blend
from pilgram.types import LUT256


def _d_cb(cb: int) -> float:
    """Returns D(Cb) - Cb"""

    cb_float = float(cb) / 255

    if cb_float <= 0.25:
        d = ((16 * cb_float - 12) * cb_float + 4) * cb_float
    else:
        d = math.sqrt(cb_float)

    return round((d - cb_float) * 255)


LUT_1_2_x_cs: LUT256 = [int(util.clip(255 - 2 * i)) for i in range(256)]
LUT_cb_x_1_cb: LUT256 = [int(round(util.clip(i * (1 - i / 255)))) for i in range(256)]
LUT_2_x_cs_1: LUT256 = [int(util.clip(2 * i - 255)) for i in range(256)]
LUT_d_cb: LUT256 = [int(_d_cb(i)) for i in range(256)]


def _soft_light(im1: Image.Image, im2: Image.Image) -> Image.Image:
    """The soft light blend mode.

    Arguments:
        im1: A backdrop image (RGB).
        im2: A source image (RGB).

    Returns:
        The output image.
    """

    _1_2_x_cs = util.apply_lut(im2, LUT_1_2_x_cs)
    cb_x_1_cb = util.apply_lut(im1, LUT_cb_x_1_cb)
    c1 = util.subtract(im1, ImageChops.multiply(_1_2_x_cs, cb_x_1_cb))

    _2_x_cs_1 = util.apply_lut(im2, LUT_2_x_cs_1)
    d_cb = util.apply_lut(im1, LUT_d_cb)
    c2 = util.add(im1, ImageChops.multiply(_2_x_cs_1, d_cb))

    cm = np.where(np.asarray(im2) <= 128, np.asarray(c1), np.asarray(c2))
    return Image.fromarray(cm)


def soft_light(im1: Image.Image, im2: Image.Image) -> Image.Image:
    """Darkens or lightens the colors, depending on the source color value.

    The soft light formula is defined as:

        if(Cs <= 0.5)
            B(Cb, Cs) = Cb - (1 - 2 x Cs) x Cb x (1 - Cb)
        else
            B(Cb, Cs) = Cb + (2 x Cs - 1) x (D(Cb) - Cb)

    where

        if(Cb <= 0.25)
            D(Cb) = ((16 * Cb - 12) x Cb + 4) x Cb
        else
            D(Cb) = sqrt(Cb)

    See the W3C document:
    https://www.w3.org/TR/compositing-1/#blendingsoftlight

    Arguments:
        im1: A backdrop image (RGB or RGBA).
        im2: A source image (RGB or RGBA).

    Returns:
        The output image.
    """

    return alpha_blend(im1, im2, _soft_light)
