# Source code for fusionsid.image

    from io import BytesIO
    from datetime import datetime

    import PIL
    import aiohttp
    import aiofiles
    from PIL import Image as img

    from .http import HTTPClient
    from .errors import ImageNotGenerated

    class BaseImage:
        """
        Base class for all images
        -------------------------

        Methods
        -------
            save(path : str) : Saves the file to a path
        """

        def __init__(self, image_bytes=None) -> None:
            self.image_bytes = image_bytes

        async def save(self, path: str) -> None:

            """
            Saves an image
            """

            if self.image_bytes is None:

                raise ImageNotGenerated

            async with aiofiles.open(path, "wb") as f:
                await f.write(self.image_bytes)

        async def show(self) -> None:

            """
            Shows an image
            """

            if self.image_bytes is None:
                raise ImageNotGenerated

            image = img.open(BytesIO(self.image_bytes))
            image.show()

            return None

        async def save_bytesio(self) -> BytesIO:

            """
            Saves the image to BytesIO

            Returns
            -------
                :class:`BytesIO`
            """

            if self.image_bytes is None:
                raise ImageNotGenerated

            image = BytesIO(self.image_bytes)

            return image

        async def pil(self) -> PIL.Image.Image:

            """
            Converts the image to a PIL.Image.Image object and returns it

            Returns
            -------
                :class:`PIL.Image.Image`
            """

            image = img.open(BytesIO(self.image_bytes))
            return image

    class RandomMeme(BaseImage):
        """
        Random Meme
        -----------

        Example
        -------
            >>> meme = await Image.random_meme()
            >>> await meme.save('meme.png')

        Attrbutes
        ---------
            image_bytes (bytes) : The image in bytes
            title (str) : Title of the post
            upvotes (str) : How many upvotes the meme has
            author (str) : Author of the meme
            url (str) : Url to the reddit post
            ext (str) : The file extension (eg: png)
            json (Dict) : The json response from the api
            created_at (datetime.datetime) : The time when the class was made
        """

        def __init__(self, json, image_bytes) -> None:
            self.json = json
            super().__init__(image_bytes=image_bytes)
            self.created_at = datetime.now()

        @property
        def title(self) -> str:
            """The title of the meme"""
            if self.image_bytes is None:
                raise ImageNotGenerated
            return self.json["title"]

        @property
        def upvotes(self) -> str:
            """How many upvotes the meme has"""
            if self.image_bytes is None:
                raise ImageNotGenerated
            return self.json["upvotes"]

        @property
        def author(self) -> str:
            """Author of the post"""
            if self.image_bytes is None:
                raise ImageNotGenerated
            return self.json["author"]

        @property
        def url(self) -> str:
            """Url to the reddit post"""
            if self.image_bytes is None:
                raise ImageNotGenerated
            return self.json["url"]

        @property
        def ext(self) -> str:
            """The file extension"""
            if self.image_bytes is None:
                raise ImageNotGenerated
            return self.json["url"].split(".")[-1]

    class QRCode(BaseImage):
        """
        QR Code
        -------

        Example
        -------
            >>> qrcode = Image.qrcode("https://google.com")
            >>> await meme.save('qrcode.png')

        Attrbutes
        ---------
            url (str) : The url that your making the meme for
            image_bytes (bytes) : The image in bytes
            created_at (datetime) : The time when the class was made

        """

        def __init__(self, url, image_bytes):
            self.url = url
            self.created_at = datetime.now()
            super().__init__(image_bytes=image_bytes)

    class FontImage(BaseImage):
        """
        FontConvertedImage
        ------------------

        Attributes
        ----------
            created_at (datetime) : The time when the class was made
        """

        def __init__(self, image_bytes):
            self.created_at = datetime.now()
            super().__init__(image_bytes=image_bytes)

    class Image:
        """
        Group of non meme generating image function
        -------------------------------------------

        Methods
        -------
            random_meme()
            qrcode(url : str)
            get_colors(filepath : str, show_hex : bool = True)

        """

        @classmethod
        async def random_meme(cls) -> RandomMeme:
            """
            Gets a random meme from the subreddit: r/memes

            Returns
            -------
                :class:`RandomMeme` : The meme
            """
            while True:
                image = await HTTPClient().get_json(url="meme?reddit_json_info=True")
                if "." in image["url"]:
                    break
            image_url = image["url"]
            image_bytes = await HTTPClient().get_url_image(image_url)

            return RandomMeme(json=image, image_bytes=image_bytes)

        @classmethod
        async def qrcode(cls, url: str) -> QRCode:
            """
            Generates a qr code

            Returns
            -------
                :class:`QRCode` : The qrcode
            """
            qrcode = await HTTPClient().get_image(f"qrcode?link={url}")

            return QRCode(url, qrcode)

        @classmethod
        async def get_colors(cls, filepath: str, show_hex=True) -> dict:
            """
            Gets the most dominant color and color palette of an image

            Parameters
            ----------
                filepath (str) : The path to the file
                show_hex (bool) : By default the api will return hex values but if you want rgb set this to False

            Example
            -------
                >>> await Image.get_colors("snail.png", False)
            """
            try:
                with open(filepath, "rb") as f:
                    image_in_bytes = f.read()

            except FileNotFoundError:
                return "File not found"

            async with aiohttp.ClientSession() as session:
                async with session.post(
                    f"https://api.fusionsid.xyz/api/get_colors/?show_hex={show_hex}",
                    data={"image": image_in_bytes},
                ) as resp:
                    data = await resp.json()

            return data

        @classmethod
        async def font_convert(
            cls, text: str, font_name: str, color: str = "black"
        ) -> FontImage:
            """
            Converts text to a font you choose

            Parameters
            ----------
                text (str): The text you are converting
                font_name (str): The font you are converting to,
                    Use the Image.font_list() function to get a list of them
                color (str, Optional): The color of the text, Defaults to black

            Returns
            -------
                :class:`FontImage`: The image of the font
            """
            list_of_fonts = await HTTPClient().get_json("fontconvert/list")
            list_of_fonts = list_of_fonts["Font_List"]

            if font_name not in list_of_fonts:
                url = "https://api.fusionsid.xyz/api/fontconvert/list"
                return print(
                    f"Invalid font, go to {url} to get a list of them or use the font list function"
                )

            url = f"""fontconvert?text={text}&font={font_name}&color={color}"""
            image_bytes = await HTTPClient().get_image(url)

            return FontImage(image_bytes)

        @classmethod
        async def font_list(cls, print_all: bool = False) -> list:
            """
            Prints a list of all the fonts that the api supports converting to

            Parameters
            ----------
                print_all (bool, Optional): setting this to true will print all the fonts in console

            Returns
            -------
                :class:`List` : The list of fonts
            """
            list_of_fonts = await HTTPClient().get_json("fontconvert/list")
            list_of_fonts = list_of_fonts["Font_List"]

            if print_all:
                print("\n".join(list_of_fonts))
            return list_of_fonts

    class Meme(BaseImage):
        """
        Meme
        ----

        Attributes
        ----------
            created_at (datetime) : The time when the class was made
        """

        def __init__(self, image_bytes):
            self.created_at = datetime.now()
            super().__init__(image_bytes=image_bytes)

    class GenerateMeme:
        """
        Generate Memes
        --------------

        """

        @classmethod
        async def abandon(cls, text: str) -> Meme:
            """
            Generates the abandon meme

            Parameters
            ----------
                text (str) : The text you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(
                f"abandon?text={text}"
            )
            return Meme(image_bytes)

        @classmethod
        async def armor(cls, text: str) -> Meme:
            """
            Generates the armor meme

            Parameters
            ----------
                text (str) : The text you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(
                f"armor?text={text}"
            )
            return Meme(image_bytes)

        @classmethod
        async def surprised(cls, text: str) -> Meme:
            """
            Generates the surprised meme

            Parameters
            ----------
                text (str) : The text you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(
                f"surprised?text={text}"
            )
            return Meme(image_bytes)

        @classmethod
        async def violence(cls, text: str) -> Meme:
            """
            Generates the violence meme

            Parameters
            ----------
                text (str) : The text you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(
                f"violence?text={text}"
            )
            return Meme(image_bytes)

        @classmethod
        async def change_my_mind(cls, text: str) -> Meme:
            """
            Generates the change my mind meme

            Parameters
            ----------
                text (str) : The text you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(
                f"changemymind?text={text}"
            )
            return Meme(image_bytes)

        @classmethod
        async def balloon(cls, balloon_text: str, arrow_text: str) -> Meme:
            """
            Generates the balloon meme

            Parameters
            ----------
                balloon_text (str) : The text you want to use for the ballon part of the meme
                arrow_text (str) : The text you want to use for the arrow part of the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(
                f"balloon?text1={balloon_text}&text2={arrow_text}"
            )
            return Meme(image_bytes)

        @classmethod
        async def boo(cls, text_1: str, text_2: str) -> Meme:
            """
            Generates the boo meme

            Parameters
            ----------
                text_1 (str) : The text you want to use for the first part of the meme
                text_2 (str) : The text you want to use for the second part of the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(
                f"boo?text_1={text_1}&text_2={text_2}"
            )
            return Meme(image_bytes)

        @classmethod
        async def brain(cls, text_1: str, text_2: str, text_3 : str, text_4 : str) -> Meme:
            """
            Generates the brain meme

            Parameters
            ----------
                text_1 (str): The text you want to use for the first box
                text_2 (str): The text you want to use for the second box
                text_3 (str): The text you want to use for the third box
                text_4 (str): The text you want to use for the forth box

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(
                f"brain?text_1={text_1}&text_2={text_2}&text_3={text_3}&text_4={text_4}"
            )
            return Meme(image_bytes)

        @classmethod
        async def expanding_wwe(cls, text_1: str, text_2: str, text_3 : str, text_4 : str, text_5 : str) -> Meme:
            """
            Generates the expanding wwe meme

            Parameters
            ----------
                text_1 (str): The text you want to use for the first box
                text_2 (str): The text you want to use for the second box
                text_3 (str): The text you want to use for the third box
                text_4 (str): The text you want to use for the forth box
                text_5 (str): The text you want to use for the fifth box

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(
                f"expandingwwe?text_1={text_1}&text_2={text_2}&text_3={text_3}&text_4={text_4}&text_5={text_5}"
            )
            return Meme(image_bytes)

        @classmethod
        async def bongocat(cls, image_url: str) -> Meme:
            """
            Generates the bongocat meme

            Parameters
            ----------
                image_url (str) : The image you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(f"bongocat?image_url={image_url}")
            return Meme(image_bytes)

        @classmethod
        async def brazzers(cls, image_url: str) -> Meme:
            """
            Generates the brazzers meme

            Parameters
            ----------
                image_url (str) : The image you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(f"brazzers?image_url={image_url}")
            return Meme(image_bytes)

        @classmethod
        async def gun(cls, image_url: str) -> Meme:
            """
            Generates the gun meme

            Parameters
            ----------
                image_url (str) : The image you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(f"gun?image_url={image_url}")
            return Meme(image_bytes)

        @classmethod
        async def trash(cls, image_url: str) -> Meme:
            """
            Generates the trash meme

            Parameters
            ----------
                image_url (str) : The image you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(f"trash?image_url={image_url}")
            return Meme(image_bytes)

        @classmethod
        async def aborted(cls, image_url: str) -> Meme:
            """
            Generates the aborted meme

            Parameters
            ----------
                image_url (str) : The image you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(f"aborted?image_url={image_url}")
            return Meme(image_bytes)

        @classmethod
        async def affect(cls, image_url: str) -> Meme:
            """
            Generates the aborted meme

            Parameters
            ----------
                image_url (str) : The image you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(f"affect?image_url={image_url}")
            return Meme(image_bytes)

        @classmethod
        async def wanted(cls, image_url: str) -> Meme:
            """
            Generates the wanted meme

            Parameters
            ----------
                image_url (str) : The image you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(f"wanted?image_url={image_url}")
            return Meme(image_bytes)

        @classmethod
        async def delete(cls, image_url: str) -> Meme:
            """
            Generates the delete meme

            Parameters
            ----------
                image_url (str) : The image you want to use for the meme

            Returns
            -------
                Meme

            """
            image_bytes = await HTTPClient().get_image(f"delete?image_url={image_url}")
            return Meme(image_bytes)