#    _____           ______  _____
#  / ____/    /\    |  ____ |  __ \
# | |        /  \   | |__   | |__) | Caer - Modern Computer Vision
# | |       / /\ \  |  __|  |  _  /  Languages: Python, C, C++, Cuda
# | |___   / ____ \ | |____ | | \ \  http://github.com/jasmcaus/caer
#  \_____\/_/    \_ \______ |_|  \_\

# Licensed under the MIT License <http://opensource.org/licenses/MIT>
# SPDX-License-Identifier: MIT
# Copyright (c) 2020-2021 The Caer Authors <http://github.com/jasmcaus>


import cv2 as cv
import numpy as np
from urllib.request import urlopen

# from urllib.error import URLError

from .resize import resize
from ..coreten import to_tensor, Tensor
from ..color import rgb2bgr, to_bgr
from ..path import exists
from .._internal import _check_target_size
from typing import Tuple, Optional, Union


__all__ = [
    "imread", 
    "imsave"
]

IMREAD_COLOR = 1


def imread(
    image_path: str,
    rgb: bool = True,
    target_size: Optional[Tuple[int, int]] = None,
    resize_factor: Optional[Union[float, Tuple]] = None,
    preserve_aspect_ratio: bool = False,
    interpolation: str = "bilinear",
) -> Tensor:
    r"""
        Loads in an image from `image_path` (can be either a system filepath or a URL)

    Args:
        image_path (str): Filepath/URL to read the image from.
        rgb (bool): Boolean to keep RGB ordering. Default: True
        target_size (tuple): Size of the target image
        resize_factor (float, tuple): Resizing Factor to employ.
                Shrinks the image if ``resize_factor < 1``
                Enlarges the image if ``resize_factor > 1``
        preserve_aspect_ration (bool): Prevent aspect ratio distortion (employs center crop). Default:False

    Returns:
        Tensor with shape ``(height, width, channels)``.

    Examples::

        >> tens = caer.imread(tens_path) # From FilePath
        >> tens.shape
        (427, 640, 3)

        >> tens = caer.imread('https://raw.githubusercontent.com/jasmcaus/caer/master/caer/data/beverages.jpg') # From URL
        >> tens.shape
        (427, 640, 3)

    """

    return _imread(image_path,rgb=rgb,target_size=target_size,resize_factor=resize_factor,preserve_aspect_ratio=preserve_aspect_ratio,interpolation=interpolation,)


def _imread(
    image_path, 
    rgb: bool = True, 
    target_size=None, 
    resize_factor=None, 
    preserve_aspect_ratio=False, 
    interpolation: str = "bilinear"
) -> Tensor:
    if target_size is not None:
        _check_target_size(target_size)

    # if not isinstance(channels, int) or channels not in [1, 3]:
    #     raise ValueError("channels must be an integer - 1 (Grayscale) or 3 (RGB)")

    interpolation_methods = {
        'nearest':  0, '0': 0, 0: 0,  # 0
        'bilinear': 1, '1': 1, 1: 1,  # 1
        'bicubic':  2, '2': 2, 2: 2,  # 2
        'area':     3, '3': 3, 3: 3,  # 3
    }

    if interpolation not in interpolation_methods:
        raise ValueError("Specify a valid interpolation type - area/nearest/bicubic/bilinear")

    if exists(image_path):
        tens = _read_image(image_path)  # returns RGB

    # TODO: Create URL validator
    elif image_path.startswith(('http://", "https://')):
        # Returns RGB image
        tens = _url_to_image(image_path)

        # If the URL is valid, but no image at that URL, NoneType is returned
        if tens is None:
            raise ValueError("The URL specified does not point to an image")

    else:
        raise ValueError("Specify either a valid URL or filepath")

    # try:
    #     # Returns RGB image
    #     tens = _url_to_image(image_path)

    #     # If the URL is valid, but no image at that URL, NoneType is returned
    #     if tens is None:
    #         raise ValueError("The URL specified does not point to an image")

    #     # return tens

    # # If the URL is invalid
    # except (Exception, URLError):
    #     if exists(image_path):
    #         tens = _read_image(image_path) # returns RGB

    #     else:
    #         raise ValueError("Specify either a valid URL or filepath")

    if target_size is not None or resize_factor is not None:
        # Enforce a Tensor is passed to resize()
        to_tensor(tens, cspace="rgb")
        tens = resize(tens,target_size,resize_factor=resize_factor,preserve_aspect_ratio=preserve_aspect_ratio,interpolation=interpolation,)

    # If `rgb=False`, then we assume that BGR is expected
    if not rgb:
        tens = rgb2bgr(tens)
        # We need to convert back to tensor
        return to_tensor(tens, cspace="bgr")

    return to_tensor(tens, cspace="rgb")


def _read_image(image_path: str) -> np.ndarray:
    r"""
        Returns an RGB ndarray

    Args:
        image_path (str): Filepath/URL to read the image from.
    """
    if not exists(image_path):
        raise FileNotFoundError("The image file was not found")

    # BGR image
    tens = cv.imread(image_path)

    # Convert to RGB
    # WARNING: DO NOT USE to_rgb() as it creates a brand new Tensor (which defaults to RGB)
    # This issue will, hopefully, be fixed in a future update.
    # tens = to_rgb(tens)
    return cv.cvtColor(tens, cv.COLOR_BGR2RGB)


def _url_to_image(url: str) -> np.ndarray:
    r"""
        Returns an RGB ndarray.
    """
    response = urlopen(url)
    tens = np.asarray(bytearray(response.read()), dtype="uint8")
    # BGR image
    tens = cv.imdecode(tens, IMREAD_COLOR)

    if tens is not None:
        return cv.cvtColor(tens, cv.COLOR_BGR2RGB)

    else:
        raise ValueError(f"No image found at '{url}'")


def imsave(path: str, tens: Tensor) -> bool:
    r"""
        Saves a Tensor to `path`

    Args:
        path (str): Filepath to save the image to

    Returns
        ``True``; if `tens` was written to `path`
        ``False``; otherwise

    Examples::

        >> tens = caer.data.audio_mixer()
        >> caer.imsave("audio_mixer.png", tens)
        True

    """
    if not isinstance(tens, Tensor):
        raise TypeError("`tens` must be a caer.Tensor")

    # Convert to tensor
    tens._nullprt()  # raises a ValueError if we're dealing with a Foreign Tensor with illegal `.cspace` value

    try:
        # OpenCV uses BGR Tensors and saves them as RGB images
        if tens.cspace != "bgr":
            tens = to_bgr(tens) # type: ignore
        return cv.imwrite(path, tens)
    except:
        raise ValueError(
            "`tens` needs to be a caer Tensor. Try reading the image using `caer.imread()`."
            "More support for additional platforms will follow. Check the Changelog for further details."
        )
